ガトリング負荷テスト

蚘事はセルゲむ マスレ 、セルゲむ マスレンニコフ に代わっお公開されおい たす


UPD。 「ガトリングの拡匵機胜の実装」セクションを远加


ガリング


gatling.ioフレヌムワヌクの負荷テストに関する䞀連の蚘事を続けたす。 この蚘事では、Gatling DSLを䜿甚するための基本的なテクニックに぀いお説明したす。GatlingDSLは、ほずんどの堎合、負荷テストスクリプトの開発で䜿甚されたす。 だから、猫の䞋でお願いしたす。


ガトリング構造


前の蚘事で、 SBTのむンストヌルずGatlingフレヌムワヌク甚の環境のセットアップに぀いお曞きたした。 すべおのステップを完了するず、プロゞェクト1が䜜成されたす。その構造は以䞋の画像に瀺されおいたす。


プロゞェクト構造


plugins.sbt2ファむルで、ファむルが䜜成されおいない堎合は、sbtのガトリングプラグむンを接続する必芁がありたす。その埌、手動で䜜成したす。 プラグむンの最新バヌゞョンを指定する必芁があるこずに泚意しおください。執筆時点では2.2.2です。 ファむルコヌドは次のずおりです。


//plugins.sbt addSbtPlugin("io.gatling" % "gatling-sbt" % "2.2.2") 

次に、src / testディレクトリで、resources3ディレクトリを䜜成する必芁がありたす。 蚭定ファむルずテストデヌタが含たれおいたす。 gatling.confファむルには基本的な蚭定が含たれおおり、logback.xmlはロギングレベルずログ出力むンタヌフェヌスを担圓したす。 これらのファむルは、 https//gatling.io/download/バンドルから取埗できたす。


scalaディレクトリにはテストパッケヌゞが含たれおいたす。 パッケヌゞ名は奜きなように呌び出すこずができたすが、原則ずしお䌁業はその逆の名前ru.tcsbank.loadを䜿甚したす。


BasicSimulationファむルはメむンテストファむルであり、スクリプトを実行するための゚ントリポむントです。
タヌゲット\ gatling6ディレクトリでは、起動ログコン゜ヌルに衚瀺されるログにレポヌトが生成されたす。 もっず頻繁に芋る必芁がありたす-非垞に急速に成長したす


メむンプロゞェクトファむルはbuild.sbt7です。 接続するすべおのラむブラリぞの䟝存関係が含たれたす。 Gatlingフレヌムワヌクぞのリンクが瀺されおいるのはその䞭にあり、そのコヌドは以䞋にありたす。


 //build.sbt /*   Gatling.       plugins.sbt */ enablePlugins(GatlingPlugin) /*    */ name := "GatlingForArticle" /*   */ version := "0.1" /*   Scala */ scalaVersion := "2.12.4" /*   */ libraryDependencies += "io.gatling.highcharts" % "gatling-charts-highcharts" % "2.3.0" % "test,it" libraryDependencies += "io.gatling" % "gatling-test-framework" % "2.3.0" % "test,it" /* ,     JVM */ javaOptions in Gatling := overrideDefaultJavaOptions("-Xss10m", "-Xms2G", "-Xmx8G") 

最埌の重芁なファむルはgatling.logです。 送信された芁求ず応答を確認できるのはその䞭にありたす。 すべおのリク゚ストを衚瀺するには、logback.xmlファむルの「ALL HTTP」行のコメントを倖すこずを忘れないでください。


ガトリングの䞻なアむデア


䞀芋するず、スクリプトずDSLガトリングは耇雑に芋えるかもしれたせんが、スクリプトを䜜成するずいう考えを理解すれば、すべおが非垞に簡単になりたす。


Gatlingは、仮想ナヌザヌをscenario()スクリプトずしお提瀺したす。 このシナリオでは、ナヌザヌの数は、 injectメ゜ッド、負荷モデル、およびhttpプロトコル蚭定ずさたざたな゚ミュレヌション条件によっお瀺されたす。 これらはすべおsetUp()構造で指定されたす。 たずえば、オンラむンストアを読み蟌む堎合、賌入者ず管理者にはいく぀かのシナリオがありたす。


  1. 賌入者が賌入する
  2. バむダヌはバスケットに入れるだけです
  3. バむダヌは商品を芋るだけです

この分離により、特定のシナリオの仮想ナヌザヌの数を増やすこずで、負荷を簡単に調敎できたす。


スクリプトは実行のチェヌンであり、このチェヌン内でリク゚ストがexec()関数内に配眮されたす。


 - +-(1) +-(2) +-(3) +-(<>){ (4) }  { (5) } 

実行チェヌンはscenario(<Name>)匏で始たり、 exec()関数を呌び出すこずで組み立おられたす。


 #1 val scn2 = scenario("ChainScenario") .exec(http().get()) .exec(http().get()) .exec(http().get()) .doIfOrElse(true) { exec(http().get()) } { exec(http().get()) } 

仮想ナヌザヌJMeterずGatlingの゚ミュレヌションを比范するず、いく぀かの機胜を匷調できたす。 JMeterでは、ナヌザヌはThreadGroupコむルに配眮され、そこで番号が蚭定され、ルヌプ内で仮想ナヌザヌのスクリプトを繰り返し再生するのは圌女コむルです。 すなわち 2人の仮想ナヌザヌを「レむズ」するず、テスト時間がなくなるたで同じスクリプトが実行されたす。


ガトリングでは、仮想ナヌザヌの管理方法が少し異なりたす。 2人の仮想ナヌザヌを遞択するず、スクリプトが実行され、䜜業が完了したす。 ナヌザヌがスクリプトをルヌプで実行するには、チェヌンをルヌプブロックに配眮する必芁がありたす。 https://gatling.io/docs/current/quickstart/#gatling-scenario-explainedで入手できる簡単なテストスクリプトを怜蚎しおください 。これは基瀎ずしお䜿甚できたす。


 //BasicSimulation.scala package ru.tcsbank.gatling /*    : io.gatling.core.Predef._ -   import io.gatling.http.Predef._ -  HTTP import scala.concurrent.duration._ -    ,     `4 minutes`, `15 seconds` */ import io.gatling.core.Predef._ import io.gatling.http.Predef._ import scala.concurrent.duration._ /*   .   ,   Simulation,     . */ class BasicSimulation extends Simulation { /*   HTTP */ val httpConf = http .baseURL("http://computer-database.gatling.io") .acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") .doNotTrackHeader("1") .acceptLanguageHeader("en-US,en;q=0.5") .acceptEncodingHeader("gzip, deflate") .userAgentHeader("Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0") /*  .    ""  , ,  (pacing)       . */ val scn = scenario("BasicSimulation") .exec( http("request_1") .get("/") ) .pause(5) /*  .     ,   ,     . */ setUp( scn.inject(atOnceUsers(1)) ).protocols(httpConf) } 

スクリプトは䞀芋耇雑に芋えるかもしれたせんが、芋おみるずDSL Gatlingは非垞にシンプルであり、ストレステストを蚘述するためにScalaの深い知識は必芁ありたせん。


スクリプトスクリプトは、 scenario()関数を定数に割り圓おるこずから始たりたす。 スクリプト名は䞀意でなければなりたせん 次に、 exec()関数が呌び出され、テストスクリプトを実装する他の関数、httpおよびwebsocketを受け取りたす。 すべおのアクションが実行され、リク゚ストを゚ミュレヌトしたす。


スクリプトが䜜成されるず、 setUp()関数で、スクリプトを実行するナヌザヌの数ず、これらのナヌザヌがロヌドする方法を瀺したす。 次に、これらすべおの䜜業方法を詳现に分析したす。


HTTP


デフォルトでは、フレヌムワヌクは次のメ゜ッドGET、POST、PUT、PATCH、DELETE、OPTIONSをサポヌトしおいたす。 䟋ずしおGETおよびPOSTリク゚ストを曞くこずを怜蚎しおください。 たず、スクリプト関数を定数scnにscn 、 exec()簡単なGETリク゚ストを蚘述したす。


 val scn = scenario("GetScenario") .exec( http("GETRequest") /* .get("/foo.php")      */ .get("http://bar.com/foo.php") ) 

ヘッダヌをむンストヌルする必芁がある堎合は、次を远加したす。


 val scn = scenario("GetScenario") .exec( http("GETRequest") .get("http://bar.com/foo.php") /* .headers("foo","bar")   Map */ .headers( Map( "foo1" -> "bar1", "foo2" -> "bar2" ) ) ) 

リク゚ストにパラメヌタヌを枡したす。


 val scn = scenario("GetScenario") .exec( http("GETRequest") .get("http://bar.com/foo.php") .headers( Map( "foo1" -> "bar1", "foo2" -> "bar2" ) ) /* .queryParam("param","value")   Map */ .queryParamMap( Map( "param1" -> "value1", "param2" -> "value2" ) ) 

パラメヌタは.get("http://bar.com/foo.php?param=value")関数.get("http://bar.com/foo.php?param=value")介しお盎接枡すこずもできたす。 静的リ゜ヌスの読み蟌みがある堎合、䞊列読み蟌みにresources()を䜿甚したす。


 val scn = scenario("GetScenario") .exec( http("GETRequest") .get("http://bar.com/foo.php") .headers( Map( "foo1" -> "bar1", "foo2" -> "bar2" ) ) .queryParamMap( Map( "param1" -> "value1", "param2" -> "value2" ) ) .resources( http("css").get("/main.css"), http("js").get("http://bar.com/main.js") ) 

POSTメ゜ッドでは、パラメヌタヌを枡すずきにformParam()関数が䜿甚されたす。


 val scn = scenario("PostScenario") .exec( http("GETRequest") .post("http://bar.com/foo.php") .headers( Map( "foo1" -> "bar1", "foo2" -> "bar2" ) ) /* .formParam("param","value")   Map */ .formParamMap( "param1" -> "value", "param2" -> "value2" ) 

リク゚スト本文を介しおデヌタを盎接転送するには、 body()䜿甚する必芁がありたす。


 val scn = scenario("PostScenario") .exec( http("GETRequest") .post("http://bar.com/foo.php") .headers( Map( "foo1" -> "bar1", "foo2" -> "bar2" ) ) .body( /*       .  stripMargin    ,      */ StringBody( """ |{ | "login": "password" |}""".stripMargin ) ) 

チェック


芁求を゚ミュレヌトするずきは、応答コヌドたたは応答本文内のテキストの存圚を確認する必芁がありたす。 倚くの堎合、応答からデヌタを抜出する必芁もありたす。 これはすべお、 check()関数を䜿甚しお実行できたす。 チェックは、httpメ゜ッドの機胜の埌に行う必芁がありたす。


 val scn = scenario("CheckScenario") .exec( http("ForChecks") .get("/check.php") /* ,     200 */ .check(status.is(200)) /*   ,    200  500 */ .check(status.in(200, 500)) /*      foo_bar */ .check(substring("foo_bar")) /*         */ .check(regex(""" \d{4} – \d{4}""")) /*          */ .check(regex("""key=(\d{4}-\w{4})""").saveAs("AuthKey")) /*  ,    ,    .    checkIf()()      Response  Session.   Response      "id="   ,     "id_some_value" */ .check( checkIf( (r: Response, s: Session) => r.body.string.contains("id=") )( regex("""id=(\d+)""").saveAs("id_some_value") ) ) ) 

セッション


セッションには、すべおの仮想ナヌザヌデヌタず倉数が栌玍されたす。 スクリプトで䜕かを裏切る堎合は、セッションを通じおこれを行う必芁がありたす。


 val scn = scenario("SessionScenario") /*   -    password_param   . , - . */ .exec( session => session.set("password_param","anyPassword") ) .exec( http("param") .get("/anything.php") /*   "${}"    */ .queryParam("login","${password_param}") ) 

ScalaはCallByValueを䜿甚するため、動的な倀を盎接DSL関数に転送するこずはできたせん。これらはコンパむル時に取埗され、垞に新しい倀なしで䜿甚されたす。


 exec( http("timestamp") .get("/") /* ,          */ .queryParam("timestamp", System.currentTimeMillis() ) /*    Session,    */ .queryParam("timestamp", session => { session("var").as[String] } ) /* Just magic!    . X -   */ .queryParam("timestamp", x => { System.currentTimeMillis() } ) ) 

論理構造


他のテストツヌルず同様に、条件に応じおリク゚ストを凊理するために論理構造がよく䜿甚されたす。


 val scn = scenario("doIfSimulation") /*    foo  bar */ .exec( session => session.set("foo","bar") ) /*  ,   foo   bar.   ,   . */ .doIf(session => session("cond").as[String].startsWith("bar")){ // exec( http("IFrequest") .get("/") ) } 

詊隓デヌタ


高品質のロヌドを実行し、キャッシュではなくシステムをテストするには、テスト䞭に動的に倉化するリク゚ストでデヌタを送信する必芁がありたす。 このようなデヌタを保存および取埗するための最も簡単な方法は、ファむルから読み取るこずです。 ファむルには、文字で区切られたデヌタが含たれおいる必芁がありたす。 ガトリングには、このようなデヌタを読み取るための機胜がありたす。


csv "foo.csv"//コンマ区切りデヌタ
tsv "foo.tsv"//タブ区切りデヌタ
ssv "foo.ssv"//セミコロンで区切られたデヌタ
SeparatedValues "foo.txt"、 ''//別の文字で区切られたデヌタ


䟋ずしお小さなcsvファむルを䜿甚しお、テストデヌタを操䜜する方法を瀺したす。


 //csv  model, Macbook, MacBook Pro, ASUS Eee PC, Acer, Asus, Sony Vaio, Chromebook 

ファむルを読み取るずき、Gatlingは最初の行をパラメヌタヌ名ずしお䜿甚し、その埌、倀を読み取るずきにこれらの名前で保存したす。 したがっお、csvファむルに蚘述されおいるラップトップの名前を持぀倀は、パラメヌタヌ${model}眮き換えられたす。


csvファむルを読み取るには、 csv()関数を呌び出す必芁がありたす。


 /*         .queue //    .random //    .shuffle //   ,    .circular //         */ val feeder = csv("data.csv").circular val scn = scenario("doIfSimulation").feed(feeder) //#1 .repeat(5)( exec( http("request") .get("/") ) // .feed(feeder) #2 .exec(http("search").get("/computers?f=${model}")) ) 

そこで、フィヌダヌ倉数を䜜成し、 src\test\resources\data.csvにあるファむルの名前を指定しsrc\test\resources\data.csv 。 スクリプトでは、 feed()関数を呌び出しお、 feeder定数を指定したす。 feed()関数が呌び出されるたびに、新しい倀が読み取られたす。


オプション1では、 feed()関数がrepeat()前に呌び出されるため、5回の反埩で最初に読み取られた倀が倉数${model}で䜿甚されたす。


オプション2を䜿甚するず、各リク゚ストの前に倀が読み取られたす。


モデルを読み蟌む


ガトリングはさたざたな負荷モデルをサポヌトしおいたす。 これらのモデルは、ナヌザヌの「䞊昇」ず生成される匷床に責任がありたす。


nothingForduration -ロヌド開始前の䞀時停止時間の長さを瀺したす


atOnceUsersnbUsers-nbUsersの量の仮想ナヌザヌは可甚性によりすぐに「䞊昇」したす。


rampUsersnbUsersoverduration - duration時間䞭、nbUsersの量の仮想ナヌザヌが同じ時間間隔で「䞊昇」したす。


constantUsersPerSecrateduringduration -「䞊げる」仮想ナヌザヌの頻床1秒あたりの仮想ナヌザヌず時間間隔durationたす。 期間duration仮想ナヌザヌduration数は毎秒増加したす。


期間ランダム化䞭のconstantUsersPerSec率 -䞊䜍の蚭蚈ず同様に、仮想ナヌザヌの「発生」間の時間間隔のみがランダムになりたす。


rampUsersPerSecrate1torate2duringduration - duration時間䞭、仮想ナヌザヌは頻床rate1から頻床rate2増加したす。


rampUsersPerSecrate1torate2duringdurationduring randomized-䞊郚のデザむンず同様に、仮想ナヌザヌの「アップ」間の時間間隔のみがランダムになりたす。


splitUsersnbUsersをinjectionStepseparatedBydurationに -各時間間隔duration埌、仮想ナヌザヌは、その数がnbUsers達するたで、 injectionStepモデルに埓っお远加されたす。 injectionStep䞊蚘のモデルを指定できたす。


splitUsersnbUsersintoinjectionStep1separatorByinjectionStep2 -トップデザむンず同様に、 injectionStep2モデルが唯䞀のセパレヌタヌです。


heavisideUsersnbUsersoverduration -nbUsersの量の仮想ナヌザヌは、 duration䞭にステップを登りたす。


ロヌド開始


オプション1


ロヌドを開始する最も簡単な方法は、バンドルを䜿甚するこずです。 スクリプトファむルをgatling-charts-highcharts-bundle-2.3.0\user-files\simulations\ gatling-charts-highcharts-bundle-2.3.0\bin\gatling.bat gatling-charts-highcharts-bundle-2.3.0\user-files\simulations\から、 gatling-charts-highcharts-bundle-2.3.0\bin\gatling.batたす。 コン゜ヌルは、実行するスクリプトの遞択を提䟛したす。


ガリングタヌミナル


スクリプトはオプション6の䞋にありたす。遞択埌、コン゜ヌルに情報が衚瀺されお負荷が生成されたす。


オプション2


このオプションでは、IntelliJ IDEA Community IDEから盎接ロヌドを開始したす。


ラむブラリを蚭定するためのすべおの手順が完了したら、ALT + F12を抌しおタヌミナルを開きたす。 タヌミナルで、 sbtコマンドを入力したす。


SBTの打ち䞊げ


すべおのコンポヌネントをロヌドした埌、 gatling:testOnlyを䜿甚しおスクリプトを実行したす。


ガリングコングル゜ンル


コン゜ヌルに珟圚の負荷状態が衚瀺されたす。
IDEAランチパッドから起動するには、SBTタスクに起動コマンドを远加する必芁がありたす。


SBTタスクの䜜成


ガトリング拡匵機胜の実装


Gatlingのドキュメントには、すぐに䜿甚できるプロトコルはHTTP / 1.1およびWebSocketのみに察応しおいるず曞かれおいたす。 Gatlingの公匏および非公匏の拡匵機胜もあり、リンク https://gatling.io/docs/2.3/extensions/ で入手できたす。


倚くの堎合、アプリケヌションレベルのプロトコルがHTTPたたはWebSocksず異なるシステムを負荷䞋でテストする必芁がある堎合にタスクがありたす。 この堎合、Gatlingの拡匵機胜を䜜成し、必芁な機胜を実装できたす。


したがっお、この機䌚を実珟する必芁がありたす。


 val scn = scenario("BasicSimulation") .exec( new ExampleActionBuilder("MyAction") ) 

exec()関数はActionBuilderのタむプを取るこずができるため、クラスを蚘述し、 ActionBuilderのタむプでクラスを拡匵する必芁がありたす。


 class ExampleActionBuilder(myNameAction: String) extends ActionBuilder { override def build(ctx: ScenarioContext, next: Action): Action = { new ExampleChainableAction(myNameAction, next, ctx) } } 

オヌバヌラむドされたbuild関数では、必芁なコヌドを実装するクラスのむンスタンスを䜜成する必芁がありたす。 このクラスはChainableActionから拡匵する必芁がありたす。


 class ExampleChainableAction(myNameAction: String, myNextAction: Action, ctx: ScenarioContext) extends ChainableAction { override def next: Action = myNextAction override def name: String = myNameAction override def execute(mySession: Session): Unit = { /*     */ } } 

以䞋は、このアプロヌチの実際の䟋です。 この方法は最適な゜リュヌションではありたせんが、実装は可胜な限り簡単であるこずに泚意するこずが重芁です。


拡匵コヌド
 package ru.tcsbank.load /*    : io.gatling.core.Predef._ -   import io.gatling.http.Predef._ -  HTTP import scala.concurrent.duration._ -    ,     `4 minutes`, `15 seconds` */ import io.gatling.commons.stats.{KO, OK} import io.gatling.core.Predef._ import io.gatling.core.action.builder.ActionBuilder import io.gatling.core.action.{Action, ChainableAction} import io.gatling.core.stats.message.ResponseTimings import io.gatling.core.structure.ScenarioContext import io.gatling.http.Predef._ import scala.concurrent.duration._ /*   .   ,   Simulation,     . */ class ExampleProtocolScript extends Simulation { /*   HTTP */ val httpConf = http .baseURL("http://computer-database.gatling.io") .acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8") .doNotTrackHeader("1") .acceptLanguageHeader("en-US,en;q=0.5") .acceptEncodingHeader("gzip, deflate") .userAgentHeader("Mozilla/5.0 (Windows NT 5.1; rv:31.0) Gecko/20100101 Firefox/31.0") /*  .    ""  , , (pacing)   ,    . */ val scn = scenario("BasicSimulation") .exec( /*    Action */ new ExampleActionBuilder("MyAction") ) /*  .     ,   ,     . */ setUp( scn.inject(atOnceUsers(1)).protocols(httpConf) ) } /*    exec().  ExampleActionBuilder()    ,      ExampleChainableAction() */ class ExampleActionBuilder(myNameAction: String) extends ActionBuilder { override def build(ctx: ScenarioContext, next: Action): Action = { new ExampleChainableAction(myNameAction, next, ctx) } } /*    Action.  ExampleChainableAction()     . */ class ExampleChainableAction(myNameAction: String, myNextAction: Action, ctx: ScenarioContext) extends ChainableAction { /*    .      . */ override def next: Action = myNextAction /*  Action. ,        .    http(<>).get(...) */ override def name: String = myNameAction /*        Action. */ override def execute(mySession: Session): Unit = { /*     Action */ val startTime = System.currentTimeMillis() try { /*   Action */ System.out.println(myNameAction+" Hello world!") /*   Action */ /*     Action */ val stopTime = System.currentTimeMillis() /*       Action */ ctx.coreComponents.statsEngine.logResponse( session = mySession, requestName = name, timings = new ResponseTimings(startTime, stopTime), status = OK, None, None, Nil ) /*        .     . */ myNextAction ! mySession } catch { /*      ,   . */ case e: Exception => { /*     Action */ val stopTime = System.currentTimeMillis() /*        Action */ ctx.coreComponents.statsEngine.logResponse( session = mySession, requestName = name, timings = new ResponseTimings(startTime, stopTime), status = KO, None, Some(e.getMessage), //   ,    /target/.../simulation.log Nil ) /*         .     . */ myNextAction ! mySession } } } } 

この蚘事では、ガトリングフレヌムワヌクのロヌドを実行するためのスクリプトを独自に開発するのに圹立぀䞻なポむントに぀いお説明したした。 , .



Source: https://habr.com/ru/post/J344818/


All Articles