当前位置:网站首页>Gatling performance test

Gatling performance test

2022-06-24 13:48:00 Or turn around

Gatling It's based on AKKA and Netty Developed high-performance pressure measuring tools , Very simple to use .


Overview and use

Gatling You can download it directly , It can also be done through maven Plug ins are integrated in the project , Execute... From the command line .

See the official website for direct use ( See references 1). Here we introduce how to use maven Plug in to use .

gatling-maven-plugin

First, we introduce dependency packages , This is required by the test script :

<dependency>
    <groupId>io.gatling.highcharts</groupId>
    <artifactId>gatling-charts-highcharts</artifactId>
    <version>MANUALLY_REPLACE_WITH_LATEST_VERSION</version>
    <scope>test</scope>
  </dependency>

Then introduce the dependent plug-ins :

<plugin>
  <groupId>io.gatling</groupId>
  <artifactId>gatling-maven-plugin</artifactId>
  <version>MANUALLY_REPLACE_WITH_LATEST_VERSION</version>
</plugin>

It is best to specify an explicit plug-in version . If you do not specify the plug-in version , The system will automatically find the latest version , This may not guarantee that the build is repeatable . Because there is no guarantee that the future behavior of the plug-in will be consistent with the current .

about Scala development , from 4.x Version start , The plug-in is no longer compiled Scala Code , If the test class uses scala To write it , You must use scala-maven-plugin Plug in to replace .

For convenience , This article is based on the project code created above . Add a new one hello-world-test-perform Sub module , Specially used for load testing .

stay test Create test class under package BasicSimulation:

import io.gatling.javaapi.core.*;
import io.gatling.javaapi.http.*;

import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;

public class BasicSimulation extends Simulation {
    

    public final String hostname = System.getProperty("url");

    HttpProtocolBuilder httpProtocol = http
            .baseUrl(hostname)
            .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");

    ScenarioBuilder scn = scenario("HelloWorldSimulation")
            .exec(http("request_1").get("/data/hello"))
            .pause(5);

    {
    
        // Inject users , At the beginning, there was one , Agreement is http
        setUp(scn.injectOpen(atOnceUsers(1))).protocols(httpProtocol);
    }
}

among ,hostname It's taken from the system variables , This is in plugin Configured in :

<plugin>
    <groupId>io.gatling</groupId>
    <artifactId>gatling-maven-plugin</artifactId>
    <configuration>
        <skip>${skipLTandPTs}</skip>
        <jvmArgs>
            <jvmArg>-Durl=${testTarget}</jvmArg>
        </jvmArgs>
    </configuration>
    <executions>
        <execution>
            <phase>test</phase>
            <goals>
                <goal>test</goal>
            </goals>
        </execution>
    </executions>
</plugin>

In this plug-in skipLTandPTs and testTarget Parameters are inherited from the parent file . The parent file pom.xml The configuration is as follows :

...
<profiles>
    <profile>
        <id>local</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
            <environment>dev</environment>
            <testTarget>http://localhost:8080/tt</testTarget>
        </properties>
    </profile>

    <profile>
        <id>pt</id>
        <properties>
            <skipTests>true</skipTests>
            <skipLTandPTs>false</skipLTandPTs>
        </properties>
    </profile>
</profiles>

such , function hello-world After the project , Execute on the command line :

 mvn clean test -P local,pt

Load test can be carried out . The generated results are placed in by default target/gatling Under the table of contents , Access in a browser index.html as follows :
 Insert picture description here

Basic concepts

gatling The main objects in include :Simulation,Injection,Scenario,Session etc. .

Simulation

setUp

setUp yes Simulation A necessary part of :

ScenarioBuilder scn = scenario("scn"); // etc...

{
    
	setUp(
	  scn.injectOpen(atOnceUsers(1))
	);
}

Protocol configuration

The protocol configuration can be written in setUp Method outside , Indicates that it is effective for all scenarios ; It can also be written on every scene , Indicates that it is effective for the current scene . As shown below :

// HttpProtocol configured globally
setUp(
  scn1.injectOpen(atOnceUsers(1)),
  scn2.injectOpen(atOnceUsers(1))
).protocols(httpProtocol);

// different HttpProtocols configured on each population
setUp(
  scn1.injectOpen(atOnceUsers(1))
    .protocols(httpProtocol1),
  scn2.injectOpen(atOnceUsers(1))
    .protocols(httpProtocol2)
);

Acceptance conditions

The acceptance conditions determine whether the load test can pass , stay setUp Method configuration .

setUp(scn.injectOpen(atOnceUsers(1)))
  .assertions(global().failedRequests().count().is(0L));
  
setUp(scn.injectOpen(atOnceUsers(1)))
 .assertions(
    global().responseTime().mean().lt(2000),
    global().successfulRequests().percent().gt(99.0)
);

Scenario

The scene name can be except \t Any character other than :

ScenarioBuilder scn = scenario();

exec

exec Method is used to perform simulated interface calls , Support HTTP,LDAP,POP,IMAP Such agreement .

Here is HTTP An example of a simulated request for a protocol :

//  Bound to the scenario
scenario("Scenario")
  .exec(http("Home").get("https://gatling.io"));

//  Create directly for subsequent reference 
ChainBuilder chain = exec(http("Home").get("https://gatling.io"));

//  Bind to other 
exec(http("Home").get("https://gatling.io"))
  .exec(http("Enterprise").get("https://gatling.io/enterprise"));

exec It can also be used for transfer functions . This feature makes it easy to set up and debug Session

exec(session -> {
    
  // displays the content of the session in the console (debugging only)
  System.out.println(session);
  // return the original session
  return session;
});

exec(session ->
  // return a new session instance
  // with a new "foo" attribute whose value is "bar"
  session.set("foo", "bar")
);

pause

Usually , When a user browses a page , There will be a time interval between the two operations . To simulate this behavior ,gatling Improved pause method .

Here are some ways to use :

The pause time is fixed

pause(10); // with a number of seconds
pause(Duration.ofMillis(100)); // with a java.time.Duration
pause("#{pause}"); // with a Gatling EL string resolving to a number of seconds or a java.time.Duration
pause(session -> Duration.ofMillis(100)); // with a function that returns a java.time.Duration

Temporally random

pause(10, 20); // with a number of seconds
pause(Duration.ofMillis(100), Duration.ofMillis(200)); // with a java.time.Duration
pause("#{min}", "#{max}"); // // with a Gatling EL strings
pause(session -> Duration.ofMillis(100), session -> Duration.ofMillis(200)); // with a function that returns a java.time.Duration

loop

repeat

If a request occurs repeatedly , Can pass repeat To simulate the .

// with an Int times
repeat(5).on(
  exec(http("name").get("/"))
);
// with a Gatling EL string resolving an Int
repeat("#{times}").on(
  exec(http("name").get("/"))
);
// with a function times
repeat(session -> 5).on(
  exec(http("name").get("/"))
);
// with a counter name
repeat(5, "counter").on(
  exec(session -> {
    
    System.out.println(session.getInt("counter"));
    return session;
  })
);

Traverse

Each element in the list can be executed in the specified order action. There are three main parameters :

  • seq: A sequence of elements to traverse , It can be a list or Gatling EL Expression or function
  • elementName:key
  • counterName( Optional ): Cycle counter , from 0 Start
// with a static List
foreach(Arrays.asList("elt1", "elt2"), "elt").on(
  exec(http("name").get("/"))
);
// with a Gatling EL string
foreach("#{elts}", "elt").on(
  exec(http("name").get("/"))
);
// with a function
foreach(session -> Arrays.asList("elt1", "elt2"), "elt").on(
  exec(http("name").get("/"))
);
// with a counter name
foreach(Arrays.asList("elt1", "elt2"), "elt", "counter").on(
  exec(session -> {
    
    System.out.println(session.getString("elt2"));
    return session;
  })
);

There are other loop operations , Here is not a list .

Error handling

tryMax

tryMax You can specify the number of retries , When action When execution fails , Will try again .

tryMax(5).on(
  exec(http("name").get("/"))
);

// with a counter name
tryMax(5, "counter").on(
  exec(http("name").get("/"))
);

exitBlockOnFail

Fail to exit immediately :

exitBlockOnFail(
  exec(http("name").get("/"))
);

exitHere

Specify the virtual user from scenario sign out :

exitHere();

exitHereIf

Exit according to conditions :

exitHereIf("#{myBoolean}");
exitHereIf(session -> true);

Injection

Use injectOpen and injectClosed Method to define the injection configuration information of the user ( And Scala Medium inject The same effect ), The method parameters are a series of injection steps , It is also processed in sequence .

Open and Closed Workload model

When it comes to load models , There are usually two types of systems :

  • Closed System , It can be used to control the number of concurrent users
  • Open System , It can be used to control the arrival rate of users

There is an upper limit on the number of concurrent users in a closed system , When the concurrency reaches the upper limit , Only when a user exits , New users can enter the system .
This is similar to the thread pool working mode , When the worker thread is full , New requests enter the task queue , Wait for a thread to be idle before proceeding .
The ticketing business system generally needs to adopt a closed model .

The closed model is suitable for systems that can obtain results asynchronously

The development system is the opposite , The number of concurrent users cannot be controlled in an open system , Even if the business system can no longer handle redundant requests , New users will continue to come and make requests .
This is the case in most business systems .

Be careful : Please decide which test model to use according to the system business type . If the actual business type does not match the model tested , You can't achieve the desired effect .

Open model and closed model have opposite meanings , Do not mix in the same injection configuration .

Open model

Here is an example of an open model ( See resources for other languages 2):

setUp(
  scn.injectOpen(
    nothingFor(4), //  Set a stop time , Within this time , Don't do anything? 
    atOnceUsers(10), //  Inject the specified number of virtual users immediately 
    rampUsers(10).during(5), //  Within a specified period of time , Gradually inject a specified number of virtual users 
    constantUsersPerSec(20).during(15), //  Within a specified period of time , Inject a specified number of virtual users per second 
    constantUsersPerSec(20).during(15).randomized(), //  Within a specified period of time , Inject randomly increasing or decreasing number of virtual users per second 
    rampUsersPerSec(10).to(20).during(10), //  Within a specified period of time , The number of virtual users injected gradually increases from one value ( linear ) Increase to another value 
    rampUsersPerSec(10).to(20).during(10).randomized(), //  Within a specified period of time , The number of injected virtual users increases from one value to another , But the growth process is not linear , It's a random jump 
    stressPeakUsers(1000).during(20) //  Within a specified period of time , according to  heaviside step  The smooth approximation of the function is injected into a specified number of users 
  ).protocols(httpProtocol)
);

Closed model

Here is an example of a closed model :

setUp(
  scn.injectClosed(
    constantConcurrentUsers(10).during(10), //  Maintain a constant number of virtual users for a specified period of time 
    rampConcurrentUsers(10).to(20).during(10) //  Within a specified period of time , The number of virtual users increases linearly from one value to another 
  )
);

Meta DSL

Before the test , Usually we don't know the throughput of the system . To test the bottleneck value , You may try to repeat the operation with different values , for example :

rampUsersPerSec(10).to(20).during(10),
rampUsersPerSec(20).to(30).during(10),
rampUsersPerSec(30).to(50).during(10),
rampUsersPerSec(50).to(70).during(10),
rampUsersPerSec(70).to(100).during(10),
);

To solve this problem ,Gatling stay 3.0 Added a kind of Meta DSL New methods to facilitate our operation .

incrementUsersPerSec(usersPerSecAddedByStage)

setUp(
  // generate an open workload injection profile
  // with levels of 10, 15, 20, 25 and 30 arriving users per second
  // each level lasting 10 seconds
  // separated by linear ramps lasting 10 seconds
  scn.injectOpen(
    incrementUsersPerSec(5.0)
      .times(5)
      .eachLevelLasting(10)
      .separatedByRampsLasting(10)
      .startingFrom(10) // Double
  )
);

incrementConcurrentUsers(concurrentUsersAddedByStage)

setUp(
  // generate a closed workload injection profile
  // with levels of 10, 15, 20, 25 and 30 concurrent users
  // each level lasting 10 seconds
  // separated by linear ramps lasting 10 seconds
  scn.injectClosed(
    incrementConcurrentUsers(5)
      .times(5)
      .eachLevelLasting(10)
      .separatedByRampsLasting(10)
      .startingFrom(10) // Int
  )
);

incrementUsersPerSec For open model load testing ,incrementConcurrentUsers Used for closed model load test .separatedByRampsLasting and startingFrom It's all optional .
If no slope is specified , Then the growth mode of virtual users is jumping . If the starting number of users is not specified , Will be taken from 0 Start .

Concurrent scenarios

In the same setUp You can set multiple scene injections at the same time , Then execute simultaneously

setUp(
	scenario1.injectOpen(injectionProfile1),
	scenario2.injectOpen(injectionProfile2)
);

Ordered scenes

Except for concurrent scenarios , Some scenes are orderly , Can pass andThen To set up an orderly scene . In an orderly scene , Only when the execution of the parent scenario is completed , The sub scenario starts to execute .

setUp(
  parent.injectClosed(injectionProfile)
    // child1 and child2 will start at the same time when last parent user will terminate
    .andThen(
      child1.injectClosed(injectionProfile)
        // grandChild will start when last child1 user will terminate
        .andThen(grandChild.injectClosed(injectionProfile)),
      child2.injectClosed(injectionProfile)
    )
);

Session

Session API

Session API User data can be processed programmatically .

Most of the time , There is one important point in load testing , That is to ensure that the request parameters of virtual users are different . If every virtual user uses the same parameters , That could be testing the cache , Instead of testing the actual system load .

Even worse , When you are in Java When a test case is executed on a virtual machine ,JVM Itself through the immediate compiler (JIT) Optimized the code , As a result, the performance results are different from those in the actual production environment .

Session

Session Is the status of the virtual user .

Generally speaking ,session It's a Map<String, Object> structure . stay Gatling in ,session All key value pairs in are Session attribute .

Gatling Medium scenario Is a workflow , Each step in the workflow is a Action,Session Data can be transferred in the workflow .

Set properties

// set one single attribute
Session newSession1 = session.set("key", "whateverValue");
// set multiple attributes
Session newSession2 = session.setAll(Collections.singletonMap("key", "value"));
// remove one single attribute
Session newSession3 = session.remove("key");
// remove multiple attributes
Session newSession4 = session.removeAll("key1", "key2");
// remove all non Gatling internal attributes
Session newSession5 = session.reset();

Session It's an immutable class , This means calling set After the method , Will return a new instance , Instead of the original instance .


//  Incorrect usage : result from Session#set is discarded
exec(session -> {
  session.set("foo", "bar");
  System.out.println(session); 
  return session;
});

//  Proper use 
exec(session -> {
  Session newSession = session.set("foo", "bar");
  System.out.println(newSession);
  return newSession;
});

from session Get user attributes in

// the unique id of this virtual user
long userId = session.userId();
// the name of the scenario this virtual user executes
String scenario = session.scenario();
// the groups this virtual user is currently in
List<String> groups = session.groups();

function

Use functions to generate dynamic parameters . As shown below :

// inline usage with a Java lamdba
exec(http("name")
  .get(session -> "/foo/" + session.getString("param").toLowerCase(Locale.getDefault())));

// passing a reference to a function
Function<Session, String> f =
    session -> "/foo/" + session.getString("param").toLowerCase(Locale.getDefault());
exec(http("name").get(f));

If you want to use randomly generated parameters , Must be generated in the function , Such as
.header("uuid", x -> RandomStringUtils.randomAlphanumeric(5)), It can't be used directly .header("uuid", RandomStringUtils.randomAlphanumeric(5)), So only the first request is a random value , Subsequent requests use the first generated value .

Feeders

stay gatling in , This can be done externally, for example csv file , Inject data into virtual users , You need to use Feeder.

Feeder It's actually an iterator Iterator<Map<String, T>> The nickname .

Here is a structure feeder Example :

// import org.apache.commons.lang3.RandomStringUtils
Iterator<Map<String, Object>> feeder =
  Stream.generate((Supplier<Map<String, Object>>) () -> {
    
      String email = RandomStringUtils.randomAlphanumeric(20) + "@foo.com";
      return Collections.singletonMap("email", email);
    }
  ).iterator();

External data source usage policy

There are many strategies for using external data sources :

//  Default :  Use iterators for base series 
csv("foo").queue();
//  Random selection of records in the sequence 
csv("foo").random();
//  Take records in the order after the disruption 
csv("foo").shuffle();
//  When the data ( From a to Z ) After that, I started from scratch 
csv("foo").circular();

When using queue and shuffle strategy , Please make sure you have enough data , Once the data runs out ,gatling Will automatically shut down .

Use lists and arrays

Of course , You can also use in memory lists or arrays to inject data into virtual users :

// using an array
arrayFeeder(new Map[] {
    
  Collections.singletonMap("foo", "foo1"),
  Collections.singletonMap("foo", "foo2"),
  Collections.singletonMap("foo", "foo3")
}).random();

// using a List
listFeeder(Arrays.asList(
  Collections.singletonMap("foo", "foo1"),
  Collections.singletonMap("foo", "foo2"),
  Collections.singletonMap("foo", "foo3")
)).random();

file-based Feeders

The above mentioned strategy of fetching data during external data injection , Such as csv("foo").queue(). So this one csv Where should I put the files ?

When using build tools such as maven,gradle or sbt when , Documents must be placed in src/main/resourcese perhaps src/test/resources Under the table of contents .

Do not use relative paths for file paths src/main/resources/data/file.csv, You should use the classpath :data/file.csv.

except csv The documents , also tsv/ssv/jsonFile/jsonUrl/jdbc/redis Import data in several ways , See resources for details Session->Feeders chapter .

Checks

Checks It can be used to verify the request results , And the returned result information can be extracted for reuse .

Checks Usually by calling... On the parent object check Method to implement , The following is a http Requested checks:

http("Gatling").get("https://gatling.io")
  .check(status().is(200))

Of course , You can also define more than one at a time checks:

http("Gatling").get("https://gatling.io")
  .check(
    status().not(404),
    status().not(500)
  )

check API Provides a dedicated DSL, You can link the following operations :

  • Definition check type
  • extract
  • transformation
  • verification
  • name
  • preservation

Type of routine inspection

Here are some general inspection types , And by most gatling Supported by official agreements .

responseTimeInMillis

.check(responseTimeInMillis().lte(100))   //  The response time is not greater than 100ms

bodyString

.check(
  bodyString().is("{\"foo\": \"bar\"}"),
  bodyString().is(ElFileBody("expected-template.json"))
)

bodyBytes

.check(
  bodyBytes().is("{\"foo\": \"bar\"}".getBytes(StandardCharsets.UTF_8)),
  bodyBytes().is(RawFileBody("expected.json"))
)

bodyLength

.check(bodyLength().is(1024))

bodyStream

Return the input stream of complete response body data bytes . In some cases , Use when you need to format the returned data before data processing .

.check(bodyStream().transform(is -> {
    
  //  take Base64 Format to String
  try (InputStream base64Is = Base64.getDecoder().wrap(is)) {
    
      return org.apache.commons.io.IOUtils.toString(base64Is, StandardCharsets.UTF_8.name());
  } catch (IOException e) {
    
      throw new RuntimeException("Impossible to decode Base64 stream");
  }
}))

subString

This check returns the index position where the specified substring appears in the response text .

Usually used to check whether a substring exists , It's better than regular expressions CPU More efficient .

.check(
  // with a static value
  // (identical to substring("expected").find().exists())
  substring("expected"),
  // with a Gatling EL
  substring("#{expectedKey}"),
  // with a function
  substring(session -> "expectedValue"),
  substring("Error:").notExists(),
  // this will save a List<Int>
  substring("foo").findAll().saveAs("indices"),
  // this will save the number of occurrences of foo
  substring("foo").count().saveAs("counts")
)

regex

.check(
  // with a static value without capture groups
  regex("<td class=\"number\">"),
  // with a Gatling EL without capture groups
  regex("<td class=\"number\">ACC#{account_id}</td>"),
  // with a static value with one single capture group
  regex("/private/bank/account/(ACC[0-9]*)/operations.html")
)

stay Java15+,Scala and Kotlin in , You can use this transition string :“”“my “non-escaped” string”“”, There is no need for ’’

XPath

This check is for XML The response body takes effect

.check(
  // simple expression for a document that doesn't use namespaces
  xpath("//input[@id='text1']/@value"),
  // mandatory namespaces parameter for a document that uses namespaces
  xpath("//foo:input[@id='text1']/@value", Collections.singletonMap("foo", "http://foo.com"))
)

more Checks See resources for details Checks chapter .

extract

Extraction allows you to filter out the desired results , Then you can process the results in the following steps .

If the extraction operation is not explicitly defined ,Gatling Will be executed by default find.

find

find You can filter out individual elements . If the target appears more than once ,find Equate to find(0).

.check(
  //  The following two are equivalent . because jjmesPath Return only one value , therefore find It can be omitted 
  jmesPath("foo"),
  jmesPath("foo").find(),
  
  // jsonPath Multiple values may be returned 
  //  The following three are equivalent , therefore find It can be omitted 
  jsonPath("$.foo"),
  jsonPath("$.foo").find(),
  jsonPath("$.foo").find(0),
  
  //  Capture the second occurrence of the element 
  jsonPath("$.foo").find(1)
)

findAll

It takes effect when there are multiple return values

.check(
  jsonPath("$.foo").findAll()
)

findRandom

.check(
  // identical to findRandom(1, false)
  jsonPath("$.foo").findRandom(),
  // identical to findRandom(1, false)
  jsonPath("$.foo").findRandom(1),
  // identical to findRandom(3, false)
  // best effort to pick 3 entries, less if not enough
  jsonPath("$.foo").findRandom(3),
  // fail if less than 3 overall captured values
  jsonPath("$.foo").findRandom(3, true)
)

count

.check(
  jsonPath("$.foo").count()
)

transformation

Conversion is an optional step . After the extraction step above , We get the corresponding results , Before matching or saving the results , You may want to format the results , At this point, the transformation is needed .

withDefault

If in the previous step ( extract ) No value was obtained in , So you can go through withDefault Set the default value .

.check(
  jsonPath("$.foo")  //  omitted find()
    .withDefault("defaultValue")
)

transform

transform Is a function , This function is used to convert the extracted value , The result of the previous step must not be empty .

.check(
  jsonPath("$.foo")
    // append "bar" to the value captured in the previous step
    .transform(string -> string + "bar")
)

transformWithSession

This step is actually transform A variation of , You can visit Session.

.check(
  jsonPath("$.foo")
    // append the value of the "bar" attribute
    // to the value captured in the previous step
    .transformWithSession((string, session) -> string + session.getString("bar"))
)

transformOption

And transfrom By contrast, , This operation can be executed even if no result is obtained in the previous step .

.check(
  jmesPath("foo")
    // extract can be null
    .transform(extract -> Optional.of(extract).orElse("default"))
)

Of course , If your goal is just to set a default value , That's direct use withDefault It might be more convenient .

transformOptionWithSession

.check(
  jmesPath("foo")
    // extract can be null
    .transformWithSession((extract, session) ->
      Optional.of(extract).orElse(session.getString("default"))
    )
)

verification

Same as extraction , If not explicitly specified ,gatling Will be executed by default exists.

is and not

// is
.check(
  // with a static value
  jmesPath("foo").is("expected"),
  // with a Gatling EL String (BEWARE DIFFERENT METHOD)
  jmesPath("foo").isEL("#{expected}"),
  // with a function
  jmesPath("foo").is(session -> session.getString("expected"))
)

// not
.check(
  // with a static value
  jmesPath("foo").not("unexpected"),
  // with a Gatling EL String (BEWARE DIFFERENT METHOD)
  jmesPath("foo").notEL("#{unexpected}"),
  // with a function
  jmesPath("foo").not(session -> session.getString("unexpected"))
)

isNull and notNull

// isNull
.check(
  jmesPath("foo")
    .isNull()
)

// notNull
.check(
  jmesPath("foo").notNull()
)

exists and notExists

.check(
  jmesPath("foo").exists()
)

// not exists
.check(
  jmesPath("foo").notExists()
)

in

.check(
  // with a static values varargs
  jmesPath("foo").in("value1", "value2"),
  // with a static values List
  jmesPath("foo").in(Arrays.asList("value1", "value2")),
  // with a Gatling EL String that points to a List in Session (BEWARE DIFFERENT METHOD)
  jmesPath("foo").inEL("#{expectedValues}"),
  // with a function
  jmesPath("foo").in(session -> Arrays.asList("value1", "value2"))
)

validate

.check(
  jmesPath("foo")
    .validate(
      "MyCustomValidator",
      (actual, session) -> {
    
        String prefix = session.getString("prefix");
        if (actual == null) {
    
          throw new NullPointerException("Value is missing");
        } else if (!actual.startsWith(prefix)) {
    
          throw new IllegalArgumentException("Value " + actual + " should start with " + prefix);
        }
        return actual;
      })
)

name

The naming is mainly to prevent mistakes , It can be displayed in the error message check The name of the .

.check(
  jmesPath("foo").name("My custom error message")
)

preservation

Saving is also an optional operation , It is used to save the results extracted or transformed in the previous step to the virtual user Session in , For subsequent reuse .

saveAs

.check(
  jmesPath("foo").saveAs("key")
)

Condition check

checkIf

// with a Gatling EL String condition that resolves a Boolean
.checkIf("#{bool}").then(
  jmesPath("foo")
)
// with a function
.checkIf(session -> session.getString("key").equals("executeCheck")).then(
  jmesPath("foo")
)

complete Check

All the above steps : Determine the type of inspection , extract , transformation , verification , preservation , All are Check Part of the workflow , Some steps are usually used in combination .

Here are some examples :

.check(
  // check the HTTP status is 200
  status().is(200),

  // check the HTTP status is in [200, 210]
  status().in(200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210),

  // check the response body contains 5 https links
  regex("https://(.*)").count().is(5),

  // check the response body contains 2 https links,
  // the first one to www.google.com and the second one to gatling.io
  regex("https://(.*)/.*").findAll().is(Arrays.asList("www.google.com", "gatling.io")),

  // check the response body contains a second occurrence of "someString"
  substring("someString").find(1).exists(),

  // check the response body does not contain "someString"
  substring("someString").notExists()
)

HTTP

HTTP yes Gatling The main objectives of the agreement , So this is our main focus .

Gatling Allow you to web application , Load test services and websites . It almost supports HTTP and HTTPS All characteristics , Including the cache 、cookies And forwarding .

Here's a basic http Examples of load tests :

HttpProtocolBuilder httpProtocol = http.baseUrl("https://gatling.io");

ScenarioBuilder scn = scenario("Scenario"); // etc...

{
    
	setUp(scn.injectOpen(atOnceUsers(1)).protocols(httpProtocol));
}

HTTP engine

warmUp

Java/NIO The engine starts and executes the first request with additional overhead , To counteract this effect ,Gatling Will automatically send to https://gatling.io. Send a request to warm up . Of course , You can change the preheating address , Or disable preheating .

//  change warmUp Address 
http.warmUp("https://www.google.com);
//  Disable preheating 
http.disableWarmUp();

maxConnectionsPerHost

To simulate a real browser ,gatling Multiple connections to the same host can be established for each virtual user at the same time . By default , The number of concurrent connections to the same remote host for the same virtual user ,gatling Limit it to 6. You can go through maxConnectionsPerHost To modify it .

http.maxConnectionsPerHost(10);

shareConnections

By default , Each virtual user has its own connection pool and SSLContext. This is actually a simulation web The scenario of browser accessing the server , Each virtual user is a browser .

And if you want to simulate a server to server scenario , under these circumstances , Per client ( Request originator ) There is a long-standing connection pool , It may be more appropriate for virtual users to share a global connection pool .

http.shareConnections();

enableHttp2

Can pass enableHttp2 Set to turn on HTTP2 Protocol support .

http.enableHttp2();

Be careful , To turn on HTTP2 function , Or use JDK9 Version above , Or make sure gatling In the configuration gatling.http.ahc.useOpenSsl No false.

Reference material

[1]. https://gatling.io/docs/gatling/tutorials/quickstart/
[2]. https://gatling.io/docs/gatling/reference/current/extensions/maven_plugin/

原网站

版权声明
本文为[Or turn around]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/175/202206241058429426.html