使用Gatling + Gradle + Jenkins Pipeline为您的JAX-RS(和JavaEE)应用程序进行连续压力测试...
在這篇文章中,我將解釋如何使用Gatling項(xiàng)目為您的JAX-RS Java EE端點(diǎn)編寫(xiě)壓力測(cè)試,以及如何將它們與Gradle和Jenkins Pipeline集成,因此,除了進(jìn)行簡(jiǎn)單的壓力測(cè)試外,您還可以使用以下方法: 連續(xù)的壓力測(cè)試,其中每個(gè)提交都可能自動(dòng)觸發(fā)此類(lèi)測(cè)試,并提供自動(dòng)斷言和每個(gè)執(zhí)行的更重要的圖形反饋,因此您可以監(jiān)視應(yīng)用程序中性能的變化。
首先要開(kāi)發(fā)的是JAX-RS JavaEE服務(wù):
@Path("/planet") @Singleton @Lock(LockType.READ) public class PlanetResources {@InjectSwapiGateway swapiGateway;@InjectPlanetService planetService;@Inject@AverageFormatterDecimalFormat averageFormatter;@GET@Path("/orbital/average")@Produces(MediaType.TEXT_PLAIN)@Asynchronouspublic void calculateAverageOfOrbitalPeriod(@Suspended final AsyncResponse response) {// Timeout controlresponse.setTimeoutHandler(asyncResponse -> asyncResponse.resume(Response.status(Response.Status.SERVICE_UNAVAILABLE).entity("TIME OUT !").build()));response.setTimeout(30, TimeUnit.SECONDS);try {// SwapiGateway is an interface to swapi.co (Star Wars API)JsonObject planets = swapiGateway.getAllPlanets();final JsonArray results = planets.getJsonArray("results");// Make some calculations with the result retrieved from swapi.codouble average = planetService.calculateAverageOfOrbitalPeriod(results);final Response averageResponse = Response.ok(averageFormatter.format(average)).build();response.resume(averageResponse);} catch(Throwable e) {response.resume(e);}} }沒(méi)什么特別的,這是一個(gè)異步的JAX-RS端點(diǎn),它連接到swapi.co網(wǎng)站,檢索“星球大戰(zhàn)”行星的所有信息,計(jì)算出軌道周期的平均值,最后以文本形式返回。 為了簡(jiǎn)單起見(jiàn),我不會(huì)向您展示所有其他類(lèi),但是它們非常簡(jiǎn)單,在文章結(jié)尾,我將為您提供github存儲(chǔ)庫(kù)。
該應(yīng)用程序打包在war文件中,并部署到應(yīng)用程序服務(wù)器中。 在這種情況下,將部署到官方Apache TomEE Docker映像內(nèi)部署的Apache TomEE 7 。
下一步是使用Gatling依賴(lài)項(xiàng)配置Gradle構(gòu)建腳本。 由于Gatling是用Scala編寫(xiě)的,因此您需要使用Scala插件。
apply plugin: 'java' apply plugin: 'scala'def gatlingVersion = "2.1.7"dependencies {compile "org.scala-lang:scala-library:2.11.7"testCompile "io.gatling:gatling-app:${gatlingVersion}"testCompile "io.gatling.highcharts:gatling-charts-highcharts:${gatlingVersion}" }之后,是時(shí)候編寫(xiě)我們的第一個(gè)壓力測(cè)試了。 重要的是要注意,為加特林編寫(xiě)壓力測(cè)試正在使用提供的DSL編寫(xiě)Scala類(lèi)。 即使對(duì)于從未看過(guò)Scala的人來(lái)說(shuō),如何使用它也非常直觀。
因此,創(chuàng)建一個(gè)名為src / test / scala的目錄,并創(chuàng)建一個(gè)具有下一個(gè)內(nèi)容的名為AverageOrbitalPeriodSimulation.scala的新類(lèi):
package org.starwarsimport io.gatling.core.Predef._ import io.gatling.http.Predef._ import scala.concurrent.duration._ import scala.util.Properties// Extends from Simulation class AverageOrbitalPeriodSimulation extends Simulation {// Gets the base URL where our service is running from environment/system propertyval LOCATION_PROPERTY = "starwars_planets_url";val location = Properties.envOrElse(LOCATION_PROPERTY, Properties.propOrElse(LOCATION_PROPERTY, "http://localhost:8080/"))// configures the base URLval conf = http.baseURL(location)// defines the scenario to run, which in this case is a GET to endpoint defined in JAX-RS serviceval scn = scenario("calculate average orbital period").exec(http("get average orbital period").get("rest/planet/orbital/average")).pause(1)// instead of simulating 10 users at once, it adds gradullay the 10 users during 3 seconds// asserts that there is no failing requests and that at max each request takes less than 3 secondssetUp(scn.inject(rampUsers(10).over(3 seconds))).protocols(conf).assertions(global.successfulRequests.percent.is(100), global.responseTime.max.lessThan(3000)) }每個(gè)模擬都必須擴(kuò)展模擬對(duì)象。 此模擬從starwars_planets_url環(huán)境或系統(tǒng)屬性中獲取服務(wù)的基本URL,創(chuàng)建指向JAX-RS中定義的端點(diǎn)的方案,最后在3秒鐘內(nèi)它將逐步添加用戶,直到同時(shí)運(yùn)行10個(gè)用戶。 僅在所有請(qǐng)求在3秒內(nèi)成功通過(guò)后,測(cè)試才能通過(guò)。
現(xiàn)在我們需要運(yùn)行此測(cè)試。 您會(huì)注意到這不是JUnit測(cè)試,因此您無(wú)法執(zhí)行Run As JUnit測(cè)試。 您需要做的是使用Gatling提供的可運(yùn)行類(lèi),該類(lèi)要求您將模擬類(lèi)作為參數(shù)傳遞。 使用Gradle確實(shí)很容易做到。
task runLoadTest(type: JavaExec) {// before runnign the task we need to compile the testsdependsOn testClassesdescription = 'Stress Test Calculating Orbital Period'classpath = sourceSets.main.runtimeClasspath + sourceSets.test.runtimeClasspath// if starwars_planets_url is not provided we add the DOCKER_HOST one automaticallydef starwarsUrl;if (!System.env.containsKey('starwars_planets_url') && !System.properties.containsKey('starwars_planets_url')) {if (System.env.containsKey('DOCKER_HOST')) {starwarsUrl = System.env.DOCKER_HOST.replace("tcp", "http").replace("2376", "9090") + "/starwars/"} else {starwarsUrl = "http://localhost:8080/starwars/"}}jvmArgs = [ "-Dgatling.core.directory.binaries=${sourceSets.test.output.classesDir.toString()}" ]// Means that the url has been calculated here and we set itif (starwarsUrl != null) {environment["starwars_planets_url"] = starwarsUrl}// Gatling applicationmain = "io.gatling.app.Gatling"// Specify the simulation to run and outputargs = ["--simulation", "org.starwars.AverageOrbitalPeriodSimulation","--results-folder", "${buildDir}/reports/gatling-results","--binaries-folder", sourceSets.test.output.classesDir.toString(),"--output-name", "averageorbitalperiodsimulation","--bodies-folder", sourceSets.test.resources.srcDirs.toList().first().toString() + "/gatling/bodies",] }// when running test task we want to execute the Gatling test test.dependsOn runLoadTest我們正在定義JavaExec類(lèi)型的Gradle任務(wù),因?yàn)槲覀兿胍氖沁\(yùn)行一個(gè)可運(yùn)行的類(lèi)。 然后,通過(guò)自動(dòng)檢測(cè)到是否未設(shè)置starwars_planets_url,我們將該測(cè)試運(yùn)行到已安裝Docker的計(jì)算機(jī)上,因此可能使開(kāi)發(fā)人員的工作變得更加輕松。
最后,如果需要,我們將覆蓋環(huán)境變量,我們?yōu)榭蛇\(yùn)行類(lèi)設(shè)置必需的屬性,并配置Gradle在每次執(zhí)行測(cè)試任務(wù)(./gradlew test)時(shí)執(zhí)行此任務(wù)。
如果運(yùn)行它,您可能會(huì)看到來(lái)自Gatling的一些輸出消息,以及所有類(lèi)似以下的消息:請(qǐng)打開(kāi)以下文件: /Users/…./stress-test/build/reports/gatlingresults / averageorbitalperiodsimulation-1459413095563 / index。 html ,這是您獲取報(bào)告的地方。 請(qǐng)注意,在目錄末尾附加了一個(gè)隨機(jī)數(shù),這很重要,因?yàn)樯院笪覀儗⒁吹健?該報(bào)告可能如下所示:
目前,我們已將Gatling與Gradle集成在一起,但是這里缺少一個(gè)片段,它在方程式上添加了連續(xù)部分。 為了添加連續(xù)的壓力測(cè)試,我們將使用Jenkins和Jenkins Pipeline作為CI服務(wù)器,因此對(duì)于每個(gè)提交都執(zhí)行壓力測(cè)試 ? 其他任務(wù),例如編譯,運(yùn)行單元,集成測(cè)試或代碼質(zhì)量門(mén)。
過(guò)去, Jenkins作業(yè)是使用Web UI配置的,要求用戶手動(dòng)創(chuàng)建作業(yè),填寫(xiě)作業(yè)的詳細(xì)信息并通過(guò)Web瀏覽器創(chuàng)建管道。 同樣,這也使得將作業(yè)的配置與正在構(gòu)建的實(shí)際代碼分開(kāi)。
隨著Jenkins Pipeline插件的引入。 該插件是Groovy DSL,可讓您在文件中實(shí)施整個(gè)構(gòu)建過(guò)程,并將其與代碼一起存儲(chǔ)。 Jenkins 2.0默認(rèn)帶有此插件,但是如果您使用的是Jenkins 1.X,則可以將其安裝為其他任何插件( https://wiki.jenkins-ci.org/display/JENKINS/Pipeline+Plugin )
因此,現(xiàn)在我們可以開(kāi)始對(duì)發(fā)布插件進(jìn)行編碼了,但是出于本文的目的,僅涉及壓力部分。 您需要在項(xiàng)目的根目錄上創(chuàng)建一個(gè)名為Jenkinsfile的文件(名稱(chēng)不是強(qiáng)制性的,但實(shí)際上是名稱(chēng)),在本例中為下一個(gè)內(nèi)容:
stage 'Compile And Unit Test'stage 'Code Quality'stage 'Integration Test'stage 'Acceptance Test'// defines an stage for info purposes stage 'Stress Test'def dockerHost = '...' //defines a node to run the stage node {// get source code from location where Jenkinsfile (this) is located.// you could use stash/unstash to get sources from previous stages instead of getting from SCMcheckout scm// defines the environment variable for stress testwithEnv(["starwars_planets_url=http://${dockerHost}:9090/starwars/"]) {// executes shell scriptsh './gradlew test'}}在這種情況下,我們定義了一個(gè)新階段,稱(chēng)為壓力測(cè)試。 階段步驟僅用作參考,將用于記錄目的。 接下來(lái)定義一個(gè)節(jié)點(diǎn)。 節(jié)點(diǎn)是執(zhí)行代碼的Jenkins執(zhí)行程序。 在此節(jié)點(diǎn)內(nèi),從放置Jenkinsfile的相同位置檢出源代碼,設(shè)置一個(gè)新的環(huán)境變量,該變量指向應(yīng)用程序的部署位置,最后是執(zhí)行Gradle測(cè)試任務(wù)的shell步驟。
Jenkins的最后一步是創(chuàng)建類(lèi)型為Pipeline的新作業(yè)并設(shè)置Jenkinsfile的位置。 因此,轉(zhuǎn)到“ 詹金斯”>“新項(xiàng)目”>“管道”,并為作業(yè)命名。
然后,您只需要轉(zhuǎn)到“ 管道”部分,并配置用于存儲(chǔ)項(xiàng)目的SCM存儲(chǔ)庫(kù)。
然后,如果您已經(jīng)正確配置了Jenkins和您的SCM服務(wù)器的掛鉤,那么將在每次提交時(shí)執(zhí)行此作業(yè),因此您的壓力測(cè)試將連續(xù)運(yùn)行。
當(dāng)然,您可能已經(jīng)注意到壓力測(cè)試已執(zhí)行,但Jenkins沒(méi)有發(fā)布任何報(bào)告,因此您無(wú)法查看或比較不同執(zhí)行的結(jié)果。 因此,您可以使用publishHtml插件將生成的報(bào)告存儲(chǔ)在Jenkins中 。 如果尚未安裝該插件,則需要與其他任何Jenkins插件一樣安裝。
PublishHtml插件使我們可以將構(gòu)建工具生成的一些html文件發(fā)布到Jenkins,以便用戶可以使用,也可以按內(nèi)部版本號(hào)進(jìn)行分類(lèi)。 您需要配置要發(fā)布的文件目錄的位置,在這里我們找到第一個(gè)問(wèn)題,您還記得蓋特林生成帶有隨機(jī)數(shù)的目錄嗎? 因此,我們需要首先解決此問(wèn)題。 您可以采用不同的策略,但是最簡(jiǎn)單的方法是在測(cè)試后將目錄重命名為已知的靜態(tài)名稱(chēng)。
打開(kāi)Gradle構(gòu)建文件并添加下一個(gè)內(nèi)容。
task(renameGatlingDirectory) << {// find the directorydef report = {file -> file.isDirectory() && file.getName().startsWith('averageorbitalperiodsimulation')}def reportDirectory = new File("${buildDir}/reports/gatling-results").listFiles().toList().findAll(report).sort().last()// rename to a known directory// should always work because in CI it comes from a clean executionreportDirectory.renameTo("${buildDir}/reports/gatling-results/averageorbitalperiodsimulation") }// it is run after test phase test.finalizedBy renameGatlingDirectory我們正在創(chuàng)建一個(gè)在測(cè)試任務(wù)結(jié)束時(shí)執(zhí)行的新任務(wù),該任務(wù)將最后創(chuàng)建的目錄重命名為averageorbitalperiodsimulation 。
最后一步是在Jenkinsfile中的shell調(diào)用之后的下一個(gè)調(diào)用中添加:
publishHTML(target: [reportDir:'stress-test/build/reports/gatling-results/averageorbitalperiodsimulation', reportFiles: 'index.html', reportName: 'Gatling report', keepAll: true])之后,您可能會(huì)在作業(yè)頁(yè)面中看到一個(gè)指向報(bào)告的鏈接。
就是如此,多虧了Gradle和Jenkins,您可以輕松地實(shí)施持續(xù)的壓力測(cè)試策略,而只需使用所有開(kāi)發(fā)人員都講的語(yǔ)言代碼即可。
我們不斷學(xué)習(xí),
亞歷克斯
翻譯自: https://www.javacodegeeks.com/2016/04/continuous-stress-testing-jax-rs-javaee-applications-gatling-gradle-jenkins-pipeline.html
總結(jié)
以上是生活随笔為你收集整理的使用Gatling + Gradle + Jenkins Pipeline为您的JAX-RS(和JavaEE)应用程序进行连续压力测试...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: mco是什么意思 mco的含义
- 下一篇: java web服务_将Java服务公开