Launch spring-boot app before Gatling simulation

流过昼夜 提交于 2021-02-10 04:57:47

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!