问题
I have a spring-boot project powered by maven I would like to run load tests on, using Gatling. At this moment, I can run my simulation typing the following command :
mvn gatling:execute
It works correctly but I have to launch my springboot app manually or it won't work.
Here is my Simulation class :
package main.aperture.gatling
import aperture.config.SpringBootApertureTestingConfiguration
import io.gatling.core.Predef._
import io.gatling.core.structure.ScenarioBuilder
import io.gatling.http.Predef._
import io.gatling.http.protocol.HttpProtocolBuilder
import org.springframework.boot.SpringApplication
import org.springframework.context.ConfigurableApplicationContext
import scala.concurrent.duration._
import scala.language.postfixOps
/**
* This class is meant to load test main get routes of this app
* The app has to be launched first (Manually, unfortunately)
*/
class MainSimulation extends Simulation {
val baseUrl: String = "localhost"
val port: String = System.getProperty("server.port", "8080")
val httpConf: HttpProtocolBuilder = http
.baseURL(s"http://$baseUrl:$port") // Here is the root for all relative URLs
val scenario_main_get: ScenarioBuilder = scenario("Testing main get routes")
.exec(http("Request on main route")
.get("/"))
.pause(100 milliseconds)
.exec(http("Request on rooms main route")
.get("/api/rooms"))
.pause(100 milliseconds)
.exec(http("Request on subjects main route")
.get("/api/subjects"))
.pause(100 milliseconds)
.exec(http("Request on supervisors main route")
.get("/api/supervisors"))
setUp(scenario_main_get.inject(rampUsers(1000) over (5 seconds)).protocols(httpConf))
}
Here is my pom.xml (I deleted unrelevant elements) :
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework</groupId>
<artifactId>gs-accessing-mongodb-data-res</artifactId>
<packaging>jar</packaging>
<version>0.1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.3.RELEASE</version>
</parent>
<!-- Properties -->
<properties>
<springfox-version>2.6.1</springfox-version>
<springfox-swaggerui-version>2.6.1</springfox-swaggerui-version>
<spring-version>4.3.2.RELEASE</spring-version>
<swagger-core-version>1.5.10</swagger-core-version>
<slf4j-version>1.6.3</slf4j-version>
<junit-version>4.8.1</junit-version>
<hibernate-validator-version>5.2.4.Final</hibernate-validator-version>
<fizzedwatcher.version>1.0.6</fizzedwatcher.version>
<gatling.version>2.2.5</gatling.version>
<gatling-plugin.version>2.2.4</gatling-plugin.version>
<scala-maven-plugin.version>3.2.2</scala-maven-plugin.version>
</properties>
<!-- Project dependencies -->
<dependencies>
<!-- SpringBoot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!-- Gatling highcharts -->
<dependency>
<groupId>io.gatling.highcharts</groupId>
<artifactId>gatling-charts-highcharts</artifactId>
<version>${gatling.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- Project build options and plugins -->
<build>
<plugins>
<!--Spring boot plugin -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>aperture.config.SpringBootApertureTestingConfiguration</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- Scala maven plugin -->
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>${scala-maven-plugin.version}</version>
</plugin>
<!-- Gatling maven plugin -->
<plugin>
<groupId>io.gatling</groupId>
<artifactId>gatling-maven-plugin</artifactId>
<version>${gatling-plugin.version}</version>
<configuration>
<simulationClass>main.aperture.gatling.MainSimulation</simulationClass>
</configuration>
<executions>
<execution>
<goals>
<goal>execute</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
I would like to be able to automatically run the spring-boot app before running the load tests with gatling. Some have suggested to add the following code at the beggining of my simulation :
val app: ConfigurableApplicationContext = SpringApplication.run(classOf[SpringBootApertureTestingConfiguration])
Runtime.getRuntime.addShutdownHook(new Thread() {
override def run(): Unit = app.stop()
})
But when I do that, I get the following error when I execute it :
[INFO] --- gatling-maven-plugin:2.2.4:execute (default-cli) @ gs-accessing-mongodb-data-res ---
20:32:23,625 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
20:32:23,625 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
20:32:23,625 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [jar:file:/home/msb/.m2/repository/io/gatling/gatling-maven-plugin/2.2.4/gatling-maven-plugin-2.2.4.jar!/logback.xml]
20:32:23,634 |-INFO in ch.qos.logback.core.joran.spi.ConfigurationWatchList@1a04f701 - URL [jar:file:/home/msb/.m2/repository/io/gatling/gatling-maven-plugin/2.2.4/gatling-maven-plugin-2.2.4.jar!/logback.xml] is not of type file
20:32:23,670 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
20:32:23,674 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
20:32:23,678 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [CONSOLE]
20:32:23,681 |-ERROR in ch.qos.logback.core.joran.spi.Interpreter@5:19 - no applicable action for [immediateFlush], current ElementPath is [[configuration][appender][immediateFlush]]
20:32:23,682 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
20:32:23,721 |-INFO in ch.qos.logback.classic.joran.action.LevelAction - ROOT level set to WARN
20:32:23,721 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [CONSOLE] to Logger[ROOT]
20:32:23,722 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
20:32:23,722 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@4e91d63f - Registering current configuration as safe fallback point
20:32:29,151 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
20:32:29,152 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
20:32:29,152 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [jar:file:/home/msb/.m2/repository/io/gatling/gatling-maven-plugin/2.2.4/gatling-maven-plugin-2.2.4.jar!/logback.xml]
20:32:29,169 |-INFO in ch.qos.logback.core.joran.spi.ConfigurationWatchList@5419f379 - URL [jar:file:/home/msb/.m2/repository/io/gatling/gatling-maven-plugin/2.2.4/gatling-maven-plugin-2.2.4.jar!/logback.xml] is not of type file
20:32:29,221 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
20:32:29,225 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
20:32:29,233 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [CONSOLE]
20:32:29,239 |-ERROR in ch.qos.logback.core.joran.spi.Interpreter@5:19 - no applicable action for [immediateFlush], current ElementPath is [[configuration][appender][immediateFlush]]
20:32:29,241 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
20:32:29,282 |-INFO in ch.qos.logback.classic.joran.action.LevelAction - ROOT level set to WARN
20:32:29,282 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [CONSOLE] to Logger[ROOT]
20:32:29,282 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
20:32:29,283 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@7dc7cbad - Registering current configuration as safe fallback point
main.aperture.gatling.MainSimulation is the only simulation, executing it.
Select simulation id (default is 'mainsimulation'). Accepted characters are a-z, A-Z, 0-9, - and _
Select run description (optional)
20:33:42.287 [restartedMain][WARN ][SpringApplicationRunListeners.java:91] o.s.b.SpringApplication - Error handling failed (no error message)
20:33:42.291 [restartedMain][ERROR][SpringApplication.java:839] o.s.b.SpringApplication - Application startup failed
java.lang.IllegalStateException: Logback configuration error detected:
ERROR in ch.qos.logback.core.joran.spi.Interpreter@5:19 - no applicable action for [immediateFlush], current ElementPath is [[configuration][appender][immediateFlush]]
at org.springframework.boot.logging.logback.LogbackLoggingSystem.loadConfiguration(LogbackLoggingSystem.java:161)
at org.springframework.boot.logging.logback.LogbackLoggingSystem.reinitialize(LogbackLoggingSystem.java:205)
at org.springframework.boot.logging.AbstractLoggingSystem.initializeWithConventions(AbstractLoggingSystem.java:65)
at org.springframework.boot.logging.AbstractLoggingSystem.initialize(AbstractLoggingSystem.java:50)
at org.springframework.boot.logging.logback.LogbackLoggingSystem.initialize(LogbackLoggingSystem.java:114)
at org.springframework.boot.logging.LoggingApplicationListener.initializeSystem(LoggingApplicationListener.java:299)
at org.springframework.boot.logging.LoggingApplicationListener.initialize(LoggingApplicationListener.java:272)
at org.springframework.boot.logging.LoggingApplicationListener.onApplicationEnvironmentPreparedEvent(LoggingApplicationListener.java:235)
at org.springframework.boot.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:208)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:166)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:138)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:121)
at org.springframework.boot.context.event.EventPublishingRunListener.environmentPrepared(EventPublishingRunListener.java:68)
at org.springframework.boot.SpringApplicationRunListeners.environmentPrepared(SpringApplicationRunListeners.java:54)
at org.springframework.boot.SpringApplication.prepareEnvironment(SpringApplication.java:337)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:308)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1186)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1175)
at main.aperture.gatling.MainSimulation.<init>(MainSimulation.scala:20)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at io.gatling.app.Runner.run0(Runner.scala:79)
at io.gatling.app.Runner.run(Runner.scala:64)
at io.gatling.app.Gatling$.start(Gatling.scala:59)
at io.gatling.app.Gatling$.fromArgs(Gatling.scala:43)
at io.gatling.app.Gatling$.main(Gatling.scala:35)
at io.gatling.app.Gatling.main(Gatling.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at io.gatling.mojo.MainWithArgsInFile.runMain(MainWithArgsInFile.java:50)
at io.gatling.mojo.MainWithArgsInFile.main(MainWithArgsInFile.java:33)
Caused by: org.springframework.boot.devtools.restart.SilentExitExceptionHandler$SilentExitException
at org.springframework.boot.devtools.restart.SilentExitExceptionHandler.exitCurrentThread(SilentExitExceptionHandler.java:90)
at org.springframework.boot.devtools.restart.Restarter.immediateRestart(Restarter.java:182)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:161)
at org.springframework.boot.devtools.restart.Restarter.initialize(Restarter.java:543)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationStartedEvent(RestartApplicationListener.java:68)
at org.springframework.boot.devtools.restart.RestartApplicationListener.onApplicationEvent(RestartApplicationListener.java:45)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:166)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:138)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:121)
at org.springframework.boot.context.event.EventPublishingRunListener.started(EventPublishingRunListener.java:63)
at org.springframework.boot.SpringApplicationRunListeners.started(SpringApplicationRunListeners.java:48)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:304)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1186)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1175)
at main.aperture.gatling.MainSimulation.<init>(MainSimulation.scala:20)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at io.gatling.app.Runner.run0(Runner.scala:79)
at io.gatling.app.Runner.run(Runner.scala:64)
at io.gatling.app.Gatling$.start(Gatling.scala:59)
at io.gatling.app.Gatling$.fromArgs(Gatling.scala:43)
at io.gatling.app.Gatling$.main(Gatling.scala:35)
at io.gatling.app.Gatling.main(Gatling.scala)
... 6 more
Maybe I forgot something or there is something missing in my configuration, but I can't find any help for my specific problem in the documentation. Some say it is a spring-boot dev tools bug.
Does anyone know how to correct this?
Even if my simulation is in a package inside my spring-boot project, It does not mean it is linked to the project. So, maybe it is not relevant to want to launch it directly in the code. However, has anyone found a workaround ?
Thanks in advance for your help.
回答1:
Have you considered wrapping this in a simple shell script and starting the app from the spring boot maven plugin?
http://docs.spring.io/spring-boot/docs/1.5.3.RELEASE/maven-plugin/run-mojo.html.
Otherwise you should be able to hook the spring boot plugin into the maven lifecycle to run before gattling as well without a shell script.
回答2:
I was looking for something similar and found this to work:
mvn spring-boot:start gatling:test spring-boot:stop
Starting the application in the before
hook also works.
This has the benefit you can set app configuration like urls to external services which then could be mocked e.q. with WireMock:
In Simulation class, add:
before {
val app = SpringApplication.run(classOf[Application])
app.registerShutdownHook()
}
回答3:
As mentioned a simplified script could be:
#!/usr/bin/env bash
set -e -x
mvn spring-boot:start
mvn gatling:execute
mvn spring-boot:stop
回答4:
Patrick's answer gave enough elements to consider using a simple shell script. If you ever encounter that kind of problem, here is the code I wrote :
#!/usr/bin/env bash
# ########### FUNCTIONS
trim(){
[[ "$1" =~ ^[[:space:]]*(.*[^[:space:]])[[:space:]]*$ ]]
printf "%s" "${BASH_REMATCH[1]}"
}
# ########### MAIN PROGRAM
# ------ First, launch the spring-boot app in background
mvn spring-boot:run &
# ------ Then, wait for it to be correctly initialized
portExtracted=`awk '/server/,/port/' ./src/main/resources/application.yml | cut -d ":" -f 2`
portToListenTo=`trim ${portExtracted}`
i=0
maxCounter=100
while [[ ! $(lsof -i :"$portToListenTo") ]] && [ ${i} -lt ${maxCounter} ]
do
echo 'Waiting for spring-boot app to be up and running.'
sleep 2
i=$[$i+1]
done
mvn gatling:execute
You'll have to adapt it to your needs though : change the path to your configuration file and the awk command to dynamically extract your app's port.
Any suggestion to enhance the script is appreciated.
EDIT : I also replaced the spring-boot maven plugin description with the following in the pom :
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>aperture.config.SpringBootApertureTestingConfiguration</mainClass>
</configuration>
<executions>
<execution>
<id>pre-integration-test</id>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
<execution>
<id>post-integration-test</id>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
This way the app is started before the gatling tests are run. To do this, simply run the mvn verify
command. It also works with mvn clean install
if you don't skip the tests.
Hope this helps !
来源:https://stackoverflow.com/questions/43986975/launch-spring-boot-app-before-gatling-simulation