mirror of
https://github.com/corda/corda.git
synced 2024-12-28 00:38:55 +00:00
Enable JMeter batch runs (#196)
* Fix arg name in jmeter build.gradle * Run ssh tunnels in daemon threads * Tweak jmeter properties to ensure batch runs always exit * Testplan for 40000 test issuances plus README explanation of non GUI run.
This commit is contained in:
parent
158993edb1
commit
2f246afc28
@ -1,9 +1,12 @@
|
||||
## JMeter for controlling CORDA performance runs
|
||||
This module contains gradle tasks to make running the JMeter (http://jmeter.apache.org)
|
||||
load generation tool against Corda nodes much easier and more useful. It does this by
|
||||
providing a simple way to launch JMeter with the actual JMeter install coming
|
||||
from downloaded dependencies, and by providing some Samplers that interact with
|
||||
the Corda node via RPC.
|
||||
|
||||
### Running via the interactive GUI
|
||||
|
||||
To run up the JMeter UI, using the jmeter.properties in the resources folder,
|
||||
type the following:
|
||||
|
||||
@ -32,6 +35,8 @@ Embedded in the JAR is all of the corda code for flows and RPC, as well as the j
|
||||
JAR will also include a properties file based on the hostname in the JMeter configuration,
|
||||
so we allocate different SSH tunneled port numbers this way.
|
||||
|
||||
#### SSH Tunnels
|
||||
|
||||
To launch JMeter with the tunnels automatically created:
|
||||
|
||||
`./gradlew tools:jmeter:run -PjmeterHosts="['hostname1', 'hostname2']"`
|
||||
@ -56,6 +61,8 @@ can be used to set this, or in the gradle call:
|
||||
|
||||
`./gradlew tools:jmeter:runSsh -PjmeterHosts="['hostname1', 'hostname2']" -PsshUser="'username'"`
|
||||
|
||||
#### Running locally with driver
|
||||
|
||||
To run up 3 nodes (2 nodes, 1 non-validating notary) locally for testing anything in the `perftestcordapp` (e.g. samplers,
|
||||
custom flows), you can use gradle to run:
|
||||
|
||||
@ -64,3 +71,18 @@ custom flows), you can use gradle to run:
|
||||
This uses the driver test infrastructure to fire up the nodes. See `StartLocalPerfCorDapp` for X500 names of nodes,
|
||||
RPC user logins etc. The RPC port of Bank A is typically 10004, but they are all reporting in the console output. A
|
||||
sample JMeter config for this setup has been included as `LocalIssueAndPay Request.jmx` under resources.
|
||||
|
||||
### Running in non-interactive test/batch mode
|
||||
|
||||
To run Jmeter in performance test mode, we want to run a predefined test without starting the UI and record the results
|
||||
in a csv file. In order to do this, additional arguments need to be passed to JMeter. Using gradle, the command line
|
||||
would look something like this:
|
||||
|
||||
```./gradlew tools:jmeter:run -PjmeterArgs="['-n', '-t', 'build/resources/main/Testplans/CashIssuance_40k.jmx', '-l', 'CashIssuance_40k.jtl', '-R', '127.0.0.1:20100']" -PjmeterHosts="['perf-node-4.corda.r3cev.com']"```
|
||||
|
||||
The interesting bit here are the `jmeterArgs`:
|
||||
- `-n` tells JMeter to run non-interactively
|
||||
- `-t <filename>` loads the testplan to run
|
||||
- `-l <filename>` specifies the output to write to (if it exists, it will be appended)
|
||||
- `-R <hostname:port>` specifies the host to run against - note this is localhost in this case as we are using ssh
|
||||
tunnels to reach the test nodes.
|
@ -100,7 +100,7 @@ run {
|
||||
args+= [ "-p", sourceSets.main.resources.getSrcDirs().first().getPath()+"/jmeter.properties",
|
||||
"-d", sourceSets.main.resources.getSrcDirs().first().getPath() ]
|
||||
if ( project.hasProperty("jmeterArgs") ) {
|
||||
args+= Eval.me(jmeterHosts)
|
||||
args+= Eval.me(jmeterArgs)
|
||||
}
|
||||
if ( project.hasProperty("sshUser") ){
|
||||
args+= "-XsshUser"
|
||||
|
@ -113,6 +113,7 @@ class Ssh {
|
||||
val session = jSch.getSession(remoteUserName, remoteHost, 22)
|
||||
// We don't check the host fingerprints because they may change often
|
||||
session.setConfig("StrictHostKeyChecking", "no")
|
||||
session.setDaemonThread(true)
|
||||
log.info("Connecting to $remoteHost...")
|
||||
session.connect()
|
||||
log.info("Connected to $remoteHost!")
|
||||
|
177
tools/jmeter/src/main/resources/Testplans/CashIssuance_40k.jmx
Normal file
177
tools/jmeter/src/main/resources/Testplans/CashIssuance_40k.jmx
Normal file
@ -0,0 +1,177 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<jmeterTestPlan version="1.2" properties="3.2" jmeter="3.3 r1808647">
|
||||
<hashTree>
|
||||
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
|
||||
<stringProp name="TestPlan.comments"></stringProp>
|
||||
<boolProp name="TestPlan.functional_mode">false</boolProp>
|
||||
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
|
||||
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
|
||||
<collectionProp name="Arguments.arguments"/>
|
||||
</elementProp>
|
||||
<stringProp name="TestPlan.user_define_classpath"></stringProp>
|
||||
</TestPlan>
|
||||
<hashTree>
|
||||
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
|
||||
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
|
||||
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
|
||||
<boolProp name="LoopController.continue_forever">false</boolProp>
|
||||
<stringProp name="LoopController.loops">40000</stringProp>
|
||||
</elementProp>
|
||||
<stringProp name="ThreadGroup.num_threads">3</stringProp>
|
||||
<stringProp name="ThreadGroup.ramp_time"></stringProp>
|
||||
<longProp name="ThreadGroup.start_time">1509455820000</longProp>
|
||||
<longProp name="ThreadGroup.end_time">1509455820000</longProp>
|
||||
<boolProp name="ThreadGroup.scheduler">false</boolProp>
|
||||
<stringProp name="ThreadGroup.duration"></stringProp>
|
||||
<stringProp name="ThreadGroup.delay"></stringProp>
|
||||
</ThreadGroup>
|
||||
<hashTree>
|
||||
<JavaSampler guiclass="JavaTestSamplerGui" testclass="JavaSampler" testname="Cash Issue Request" enabled="true">
|
||||
<elementProp name="arguments" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" enabled="true">
|
||||
<collectionProp name="Arguments.arguments">
|
||||
<elementProp name="host" elementType="Argument">
|
||||
<stringProp name="Argument.name">host</stringProp>
|
||||
<stringProp name="Argument.value">localhost</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
</elementProp>
|
||||
<elementProp name="port" elementType="Argument">
|
||||
<stringProp name="Argument.name">port</stringProp>
|
||||
<stringProp name="Argument.value">10003</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
</elementProp>
|
||||
<elementProp name="username" elementType="Argument">
|
||||
<stringProp name="Argument.name">username</stringProp>
|
||||
<stringProp name="Argument.value">corda</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
</elementProp>
|
||||
<elementProp name="password" elementType="Argument">
|
||||
<stringProp name="Argument.name">password</stringProp>
|
||||
<stringProp name="Argument.value">corda_is_awesome</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
</elementProp>
|
||||
<elementProp name="notaryName" elementType="Argument">
|
||||
<stringProp name="Argument.name">notaryName</stringProp>
|
||||
<stringProp name="Argument.value">O=Perf-10.155.0.8,OU=Corda,L=London,C=GB,CN=corda.notary.simple</stringProp>
|
||||
<stringProp name="Argument.metadata">=</stringProp>
|
||||
</elementProp>
|
||||
</collectionProp>
|
||||
</elementProp>
|
||||
<stringProp name="classname">com.r3.corda.jmeter.CashIssueSampler</stringProp>
|
||||
</JavaSampler>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
<ResultCollector guiclass="StatGraphVisualizer" testclass="ResultCollector" testname="Aggregate Graph" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="GraphVisualizer" testclass="ResultCollector" testname="Graph Results" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
<ResultCollector guiclass="TableVisualizer" testclass="ResultCollector" testname="View Results in Table" enabled="true">
|
||||
<boolProp name="ResultCollector.error_logging">false</boolProp>
|
||||
<objProp>
|
||||
<name>saveConfig</name>
|
||||
<value class="SampleSaveConfiguration">
|
||||
<time>true</time>
|
||||
<latency>true</latency>
|
||||
<timestamp>true</timestamp>
|
||||
<success>true</success>
|
||||
<label>true</label>
|
||||
<code>true</code>
|
||||
<message>true</message>
|
||||
<threadName>true</threadName>
|
||||
<dataType>true</dataType>
|
||||
<encoding>false</encoding>
|
||||
<assertions>true</assertions>
|
||||
<subresults>true</subresults>
|
||||
<responseData>false</responseData>
|
||||
<samplerData>false</samplerData>
|
||||
<xml>false</xml>
|
||||
<fieldNames>true</fieldNames>
|
||||
<responseHeaders>false</responseHeaders>
|
||||
<requestHeaders>false</requestHeaders>
|
||||
<responseDataOnError>false</responseDataOnError>
|
||||
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
|
||||
<assertionsResultsToSave>0</assertionsResultsToSave>
|
||||
<bytes>true</bytes>
|
||||
<sentBytes>true</sentBytes>
|
||||
<threadCounts>true</threadCounts>
|
||||
<idleTime>true</idleTime>
|
||||
<connectTime>true</connectTime>
|
||||
</value>
|
||||
</objProp>
|
||||
<stringProp name="filename"></stringProp>
|
||||
</ResultCollector>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
<WorkBench guiclass="WorkBenchGui" testclass="WorkBench" testname="WorkBench" enabled="true">
|
||||
<boolProp name="WorkBench.save">true</boolProp>
|
||||
</WorkBench>
|
||||
<hashTree/>
|
||||
</hashTree>
|
||||
</jmeterTestPlan>
|
@ -1035,13 +1035,13 @@ cookies=cookies
|
||||
# Whether to call System.exit(1) on failure to stop threads in non-GUI mode.
|
||||
# This only takes effect if the test was explicitly requested to stop.
|
||||
# If this is disabled, it may be necessary to kill the JVM externally
|
||||
#jmeterengine.stopfail.system.exit=true
|
||||
jmeterengine.stopfail.system.exit=true
|
||||
|
||||
# Whether to force call System.exit(0) at end of test in non-GUI mode, even if
|
||||
# there were no failures and the test was not explicitly asked to stop.
|
||||
# Without this, the JVM may never exit if there are other threads spawned by
|
||||
# the test which never exit.
|
||||
#jmeterengine.force.system.exit=false
|
||||
jmeterengine.force.system.exit=true
|
||||
|
||||
# How long to pause (in ms) in the daemon thread before reporting that the JVM has failed to exit.
|
||||
# If the value is <= 0, the JMeter does not start the daemon thread
|
||||
|
Loading…
Reference in New Issue
Block a user