Python组装jmx并调用JMeter实现压力测试

JMeter可以通过os命令调用Python脚本,Python同样可以通过系统命令调用JMeter执行压测

Python调用JMeter

首先要安装JMeter,官方下载地址

解压并配置配置环境路径或建立软连,使得在命令输入jmeter便可以执行,如

unzip apache-jmeter-5.3.zip
mv apache-jmeter-5.3 /usr/loca/jmeter
ln -s /usr/local/jmeter/bin/jmeter /usr/bin/jmeter
ln -s /usr/local/jmeter/bin/jmeter-server /usr/bin/jmeter-server

打开JMeter并设计一个测试计划保存为testplan.jmx

使用Python调用JMeter压测并生成报告

Python中可以使用os.system()或supprocess.Popen()调用系统命令,前者实时显示在屏幕上,后者可以获取到屏幕输出信息。
使用Python调用JMeter运行及生成报告的命令如下。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

import subprocess

jmx_file = 'testplan.jmx' # jmx文件路径

result_file = 'result.jtl' #

log_file = 'run.log'

report_dir = 'report'

run_cmd = f'jmeter -n -t {jmx_file} -l {result_file} -j {log_file}' # 无界面运行JMeter压测命令

report_cmd = f'jmeter -g {result_file} -o {report_dir}' # 生成HTML报告命令

# 不需要获取屏幕输出是,可以使用os.system()

# os.system(run_cmd)

# os.system(report_cmd)

# 需要获取屏幕输出是,可以使用subprocess.Popen()

p1 = subprocess.Popen(run_cmd, shell=True, stdout=subprocess.PIPE)

print(p1.stdout.read().decode('utf-8'))

p2 = subprocess.Popen(report_cmd, shell=True, stdout=subprocess.PIPE)

print(p2.stdout.read().decode('utf-8'))

组装jmx

每一测试计划为一个jmx文件,jmx实际上是xml格式的,包含一些JMeter自定义的格式规范。
常用的组件有:

  • : 测试计划
  • : 线程组
  • : CSV数据文件
  • : HTTP请求
  • : HTTP请求头管理器
  • : Cookies管理器
  • : DNS缓存管理器
  • : 监听器(包括查看结果树、聚合报告等)
  • : 响应断言
  • <io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample></io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample>: 三方Dubbo请求插件

Dubbo插件jmeter-plugins-dubbo下载链接

jmx中,如果一个组件有子组件,格式为

1

2

3

4

5

6

7

8

9

10

11

12

13

<ThreadGroup 组件基本属性>

   ...线程组配置

</ThreadGroup>

<hashTree>

   ...内部子组件

</hashTree>

···

如果不包含子组件,则后面接一个<hashTree/> 单标签直接结束,例如:

```xml

<CSVDataSet>

  ... CSV数据组件配置

</CSVDataSet>

<hashTree/>

详细的格式可以自己使用JMeter创建一个测试计划,使用文本文件打开jmx文件查看。

使用Python组装jmx文件的方式有两种,一种是固定模板的数据渲染,一种类似JMeter的逐个组件添加,第一种比较简单。
通过分析jmx文件中的变量,我们使用jinja2模板语法,将其中的变量进行参数化,对需要判断和循环的变量设置if和for循环。

Jinja2中文文档

假设我们的测试计划结构为:

1

2

3

4

5

6

7

8

9

10

11

12

13

测试计划

  DNS缓存管理器

  Cookies管理器

  CSV文件(多个)

  ...

  聚合报告

  线程组(多个)

    CSV文件(多个)

    HTTP请求(或Dubbo请求)

      HTTP请求头管理器

      CSV文件(多个)

      响应断言

      察看结果树

将jmx中的关键数据抽取并组合,我使用的完整数据格式如下:

data.yaml

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

test_plan_name: 测试计划

comments: 测试计划描述

hosts:

 - name: las.secoo.com

  address: 112.126.120.128

cookies:

 clear_each_iteration: 'true'

csv_files:

 - {'name': '数据文件1', 'path': 'data.csv', 'varnames': 'a,b', 'delimiter': ','}

 - {'name': '数据文件2', 'path': 'data.csv', 'varnames': 'c,d', 'delimiter': ','}

thread_groups:

 - thread_group_name: 线程组1

  comments: 线程组1描述

  enabled: 'true'

  num_threads: 50

  loops: -1

  ramp_time: 0

  scheduler: 'true'

  duration: 30

  delay: ''

  http_samples:

   - request_name: HTTP-GET

    enabled: 'true'

    csv_files:

     - {'name': '数据文件4', 'path': 'data.csv', 'varnames': 'a,b', 'delimiter': ','}

    request:

     protocol: https

     domain: httpbin.org

     port: ''

     encoding: ''

     path: /get

     method: GET

     connect_timeout: ''

     response_timeout: ''

     params: {'a': 1, 'b': 2}

     headers: {'token': 'aaaaaa', 'x-text': 'bbbbb'}

     follow_redirects: 'false'

     auto_redirects: 'false'

     use_keepalive: 'false'

    validate:

     - test_field: response_data # response_data响应文本 response_code响应代码response_message响应信息response_headers

      # 请求头request_headers sample_label URL样本 response_data_as_document文档(文本) 请求数据request_data

      test_type: 2 # 2 包括 1匹配 8 相等 16字符串 否+4 或者+32

      strings: ['a', 'b']

   - request_name: HTTP-POST

    enabled: 'true'

    request:

     protocol: https

     domain: httpbin.org

     port: ''

     encoding: ''

     path: /post

     method: POST

     data: {'c': 3, 'd': 4}

     follow_redirects: 'false'

     auto_redirects: 'false'

     use_keepalive: 'false'

     connect_timeout: ''

     response_timeout: ''

   - request_name: HTTP-JSON

    enabled: 'true'

    request:

     protocol: https

     domain: httpbin.org

     port: ''

     encoding: ''

     path: /post

     method: POST

     connect_timeout: ''

     response_timeout: ''

     raw_data: '{"e": 5, "f": 6}'

     follow_redirects: 'false'

     auto_redirects: 'false'

     use_keepalive: 'false'

 - thread_group_name: 线程组2

  comments: 线程组2描述

  enabled: 'false'

  num_threads: 50

  loops: -1

  ramp_time: 0

  scheduler: 'true'

  duration: 30

  delay: ''

  csv_files:

   - {'name': '数据文件3', 'path': 'data.csv', 'varnames': 'a,b','delimiter': '\t'}

  dubbo_samples:

   - request_name: 查询运费接口-dubbo

    enabled: 'true'

    registry:

     type: zookeeper

     group: ''

     address: 'zk-mall1.secoolocal.com:5181?backup=zk-mall2.secoolocal.com:5181,zk-mall3.secoolocal.com:5181'

    dubbo:

     timeout: 100

     retires: 0

     group: ''

     connections: 100

     load_balance: random

     cluster: failfast

     service: 'com.secoo.business.config.rpc.service.BusinessConfigStoreService'

     method: queryFreight

     headers:

      Content-Type: 'application/json'

     params:

      - type: java.util.List

       value: ${freight_wareHouseId_sendAreaId}

      - type: java.lang.String

       value: 110100

      - type: java.util.List

       value: ${freight_wareHouseId_sendAreaId}

    validate:

     - test_field: response_data # response_data响应文本 response_code响应代码response_message响应信息response_headers

      test_type: 16 # 2 包括 1匹配 8 相等 16字符串 否+4 或者+32

      strings: ['"code": 0']

对应的模板文件tpl.xml代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

<?xml version="1.0" encoding="UTF-8"?>

<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.3">

 <hashTree>

  <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="{{test_plan_name}}" enabled="true">

   <stringProp name="TestPlan.comments">{{comments}}</stringProp>

   <boolProp name="TestPlan.functional_mode">false</boolProp>

   <boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>

   <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>

   <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">

    <collectionProp name="Arguments.arguments"/>

   </elementProp>

   <stringProp name="TestPlan.user_define_classpath"></stringProp>

  </TestPlan>

  <hashTree>{% if hosts %}

   <DNSCacheManager guiclass="DNSCachePanel" testclass="DNSCacheManager" testname="DNS缓存管理器" enabled="true">

    <collectionProp name="DNSCacheManager.servers"/>

    <collectionProp name="DNSCacheManager.hosts">{% for host in hosts %}

     <elementProp name="las.secoo.com" elementType="StaticHost">

      <stringProp name="StaticHost.Name">{{host.name}}</stringProp>

      <stringProp name="StaticHost.Address">{{host.address}}</stringProp>

     </elementProp>{% endfor %}

    </collectionProp>

    <boolProp name="DNSCacheManager.clearEachIteration">false</boolProp>

    <boolProp name="DNSCacheManager.isCustomResolver">true</boolProp>

   </DNSCacheManager>

   <hashTree/>{% endif %} {% if cookies %}

   <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie管理器" enabled="true">

    <collectionProp name="CookieManager.cookies"/>

    <boolProp name="CookieManager.clearEachIteration">{{cookies.clear_each_iteration}}</boolProp>

   </CookieManager>

   <hashTree/>{% endif %} {% if csv_files %}{% for csv_file in csv_files %}

   <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="{{csv_file.name}}" enabled="true">

    <stringProp name="filename">dat/{{csv_file.path}}</stringProp>

    <stringProp name="fileEncoding">UTF-8</stringProp>

    <stringProp name="variableNames">{{csv_file.varnames}}</stringProp>

    <boolProp name="ignoreFirstLine">true</boolProp>

    <stringProp name="delimiter">{{csv_file.delimiter}}</stringProp>

    <boolProp name="quotedData">false</boolProp>

    <boolProp name="recycle">true</boolProp>

    <boolProp name="stopThread">false</boolProp>

    <stringProp name="shareMode">shareMode.group</stringProp>

   </CSVDataSet>

   <hashTree/>{% endfor %}{% endif %}

   <ResultCollector guiclass="StatVisualizer" testclass="ResultCollector" testname="聚合报告" 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>true</responseDataOnError>

      <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>

      <assertionsResultsToSave>0</assertionsResultsToSave>

      <bytes>true</bytes>

      <sentBytes>true</sentBytes>

      <threadCounts>true</threadCounts>

      <idleTime>true</idleTime>

     </value>

    </objProp>

    <stringProp name="filename"></stringProp>

   </ResultCollector>

   <hashTree/>{% for thread_group in thread_groups %}

   <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="{{thread_group.thread_group_name}}" enabled="{{thread_group.enabled}}">

    <stringProp name="TestPlan.comments">{{thread_group.comments}}</stringProp>

    <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>

    <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循环控制器" enabled="true">

     <boolProp name="LoopController.continue_forever">false</boolProp>

     <intProp name="LoopController.loops">{{thread_group.loops}}</intProp>

    </elementProp>

    <stringProp name="ThreadGroup.num_threads">{{thread_group.num_threads}}</stringProp>

    <stringProp name="ThreadGroup.ramp_time">{{thread_group.ramp_time}}</stringProp>

    <boolProp name="ThreadGroup.scheduler">{{thread_group.scheduler}}</boolProp>

    <stringProp name="ThreadGroup.duration">{{thread_group.duration}}</stringProp>

    <stringProp name="ThreadGroup.delay">{{thread_group.delay}}</stringProp>

    <boolProp name="ThreadGroup.same_user_on_next_iteration">false</boolProp>

   </ThreadGroup>

   <hashTree>{% if thread_group.csv_files %}{% for csv_file in thread_group.csv_files %}

    <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="{{csv_file.name}}" enabled="true">

    <stringProp name="filename">dat/{{csv_file.path}}</stringProp>

    <stringProp name="fileEncoding">UTF-8</stringProp>

    <stringProp name="variableNames">{{csv_file.varnames}}</stringProp>

    <boolProp name="ignoreFirstLine">true</boolProp>

    <stringProp name="delimiter">{{csv_file.delimiter}}</stringProp>

    <boolProp name="quotedData">false</boolProp>

    <boolProp name="recycle">true</boolProp>

    <boolProp name="stopThread">false</boolProp>

    <stringProp name="shareMode">shareMode.group</stringProp>

   </CSVDataSet>

    <hashTree/>{% endfor %}{% endif %} {% if thread_group.http_samples %}{% for http_sample in thread_group.http_samples %}

    <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="{{http_sample.request_name}}" enabled="{{http_sample.enabled}}">

     <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="用户定义的变量" enabled="true">

      <collectionProp name="Arguments.arguments">{% if http_sample.request.params %}{% for name, value in http_sample.request.params.items() %}

       <elementProp name="{{name}}" elementType="HTTPArgument">

        <boolProp name="HTTPArgument.always_encode">false</boolProp>

        <stringProp name="Argument.value">{{value}}</stringProp>

        <stringProp name="Argument.metadata">=</stringProp>

        <boolProp name="HTTPArgument.use_equals">true</boolProp>

        <stringProp name="Argument.name">{{name}}</stringProp>

       </elementProp>{% endfor %}{% endif %} {% if http_sample.request.data %}{% for name, value in http_sample.request.data.items() %}

       <elementProp name="{{name}}" elementType="HTTPArgument">

        <boolProp name="HTTPArgument.always_encode">false</boolProp>

        <stringProp name="Argument.value">{{value}}</stringProp>

        <stringProp name="Argument.metadata">=</stringProp>

        <boolProp name="HTTPArgument.use_equals">true</boolProp>

        <stringProp name="Argument.name">{{name}}</stringProp>

       </elementProp>{% endfor %}{% endif %} {% if http_sample.request.raw_data %}

       <elementProp name="" elementType="HTTPArgument">

        <boolProp name="HTTPArgument.always_encode">false</boolProp>

        <stringProp name="Argument.value">{{http_sample.request.raw_data}}</stringProp>

        <stringProp name="Argument.metadata">=</stringProp>

       </elementProp>{% endif %}

      </collectionProp>

     </elementProp>

     <stringProp name="HTTPSampler.domain">{{http_sample.request.domain}}</stringProp>

     <stringProp name="HTTPSampler.port">{{http_sample.request.port}}</stringProp>

     <stringProp name="HTTPSampler.protocol">{{http_sample.request.protocol}}</stringProp>

     <stringProp name="HTTPSampler.contentEncoding">{{http_sample.request.encoding}}</stringProp>

     <stringProp name="HTTPSampler.path">{{http_sample.request.path}}</stringProp>

     <stringProp name="HTTPSampler.method">{{http_sample.request.method}}</stringProp>

     <boolProp name="HTTPSampler.follow_redirects">{{http_sample.request.follow_redirects}}</boolProp>

     <boolProp name="HTTPSampler.auto_redirects">{{http_sample.request.auto_redirects}}</boolProp>

     <boolProp name="HTTPSampler.use_keepalive">{{http_sample.request.use_keepalive}}</boolProp>

     <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>

     <stringProp name="HTTPSampler.embedded_url_re"></stringProp>

     <stringProp name="HTTPSampler.connect_timeout">{{http_sample.request.connect_timeout}}</stringProp>

     <stringProp name="HTTPSampler.response_timeout">{{http_sample.request.response_timeout}}</stringProp>

    </HTTPSamplerProxy>

    <hashTree>{% if http_sample.request.headers %}

     <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP信息头管理器" enabled="true">

      <collectionProp name="HeaderManager.headers">{% for name, value in http_sample.request.headers.items() %}

       <elementProp name="" elementType="Header">

        <stringProp name="Header.name">{{name}}</stringProp>

        <stringProp name="Header.value">{{value}}</stringProp>

       </elementProp>{% endfor %}

      </collectionProp>

     </HeaderManager>

     <hashTree/>{% endif %} {% if http_sample.csv_files %}{% for csv_file in http_sample.csv_files %}

     <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="CSV 数据文件设置" enabled="true">

      <stringProp name="delimiter">{{csv_file.delimiter}}</stringProp>

      <stringProp name="fileEncoding">UTF_8</stringProp>

      <stringProp name="filename">dat/{{csv_file.path}}</stringProp>

      <boolProp name="ignoreFirstLine">true</boolProp>

      <boolProp name="quotedData">false</boolProp>

      <boolProp name="recycle">true</boolProp>

      <stringProp name="shareMode">shareMode.group</stringProp>

      <boolProp name="stopThread">false</boolProp>

      <stringProp name="variableNames">{{csv_file.varnames}}</stringProp>

     </CSVDataSet>

     <hashTree/>{% endfor %}{% endif %} {% if http_sample.validate %}{% for assertion in http_sample.validate %}

     <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="响应断言" enabled="true">

      <collectionProp name="Asserion.test_strings">{% if assertion.strings %}{% for string in assertion.strings %}

       <stringProp name="97">{{string}}</stringProp>{% endfor %}{% endif %}

      </collectionProp>

      <stringProp name="Assertion.custom_message"></stringProp>

      <stringProp name="Assertion.test_field">Assertion.{{assertion.test_field}}</stringProp>

      <boolProp name="Assertion.assume_success">false</boolProp>

      <intProp name="Assertion.test_type">{{assertion.test_type}}</intProp>

     </ResponseAssertion>

     <hashTree/>{% endfor %}{% endif %}

     <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="察看结果树" 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>

        <url>true</url>

        <threadCounts>true</threadCounts>

        <idleTime>true</idleTime>

        <connectTime>true</connectTime>

       </value>

      </objProp>

      <stringProp name="filename"></stringProp>

     </ResultCollector>

     <hashTree/>

    </hashTree>{% endfor %}{% endif %} {% if thread_group.dubbo_samples %} {% for dubbo_sample in thread_group.dubbo_samples %}

    <io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample guiclass="io.github.ningyu.jmeter.plugin.dubbo.gui.DubboSampleGui" testclass="io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample" testname="{{dubbo_sample.request_name}}" enabled="{{dubbo_sample.enabled}}">

     <stringProp name="FIELD_DUBBO_REGISTRY_PROTOCOL">{{dubbo_sample.registry.type}}</stringProp>

     <stringProp name="FIELD_DUBBO_REGISTRY_GROUP">{{dubbo_sample.registry.group}}</stringProp>

     <stringProp name="FIELD_DUBBO_RPC_PROTOCOL">dubbo://</stringProp>

     <stringProp name="FIELD_DUBBO_ADDRESS">{{dubbo_sample.registry.address}}</stringProp>

     <stringProp name="FIELD_DUBBO_TIMEOUT">{{dubbo_sample.dubbo.timeout}}</stringProp>

     <stringProp name="FIELD_DUBBO_VERSION"></stringProp>

     <stringProp name="FIELD_DUBBO_RETRIES">{{dubbo_sample.dubbo.retries}}</stringProp>

     <stringProp name="FIELD_DUBBO_GROUP">{{dubbo_sample.dubbo.group}}</stringProp>

     <stringProp name="FIELD_DUBBO_CONNECTIONS">{{dubbo_sample.dubbo.connections}}</stringProp>

     <stringProp name="FIELD_DUBBO_LOADBALANCE">{{dubbo_sample.dubbo.load_balance}}</stringProp>

     <stringProp name="FIELD_DUBBO_ASYNC">sync</stringProp>

     <stringProp name="FIELD_DUBBO_CLUSTER">{{dubbo_sample.dubbo.cluster}}</stringProp>

     <stringProp name="FIELD_DUBBO_INTERFACE">{{dubbo_sample.dubbo.service}}</stringProp>

     <stringProp name="FIELD_DUBBO_METHOD">{{dubbo_sample.dubbo.method}}</stringProp>

     <intProp name="FIELD_DUBBO_METHOD_ARGS_SIZE">1</intProp>{% for param in dubbo_sample.dubbo.params %}

      <stringProp name="FIELD_DUBBO_METHOD_ARGS_PARAM_TYPE1">{{param.type}}</stringProp>

      <stringProp name="FIELD_DUBBO_METHOD_ARGS_PARAM_VALUE1">{{param.value}}</stringProp>{% endfor %}

     <intProp name="FIELD_DUBBO_ATTACHMENT_ARGS_SIZE">0</intProp>

     <stringProp name="FIELD_DUBBO_CONFIG_CENTER_PROTOCOL"></stringProp>

     <stringProp name="FIELD_DUBBO_CONFIG_CENTER_GROUP"></stringProp>

     <stringProp name="FIELD_DUBBO_CONFIG_CENTER_NAMESPACE"></stringProp>

     <stringProp name="FIELD_DUBBO_CONFIG_CENTER_USER_NAME"></stringProp>

     <stringProp name="FIELD_DUBBO_CONFIG_CENTER_PASSWORD"></stringProp>

     <stringProp name="FIELD_DUBBO_CONFIG_CENTER_ADDRESS"></stringProp>

     <stringProp name="FIELD_DUBBO_CONFIG_CENTER_TIMEOUT"></stringProp>

     <stringProp name="FIELD_DUBBO_REGISTRY_USER_NAME"></stringProp>

     <stringProp name="FIELD_DUBBO_REGISTRY_PASSWORD"></stringProp>

     <stringProp name="FIELD_DUBBO_REGISTRY_TIMEOUT"></stringProp>

    </io.github.ningyu.jmeter.plugin.dubbo.sample.DubboSample>

    <hashTree>{% if dubbo_sample.dubbo.headers %}

     <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP信息头管理器" enabled="true">

      <collectionProp name="HeaderManager.headers">{% for name, value in dubbo_sample.dubbo.headers.items() %}

       <elementProp name="" elementType="Header">

        <stringProp name="Header.name">{{name}}</stringProp>

        <stringProp name="Header.value">{{value}}</stringProp>

       </elementProp>{% endfor %}

      </collectionProp>

     </HeaderManager>

     <hashTree/>{% endif %} {% if dubbo_sample.validate %} {% for assertion in dubbo_sample.validate %}

     <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="响应断言" enabled="true">

      <collectionProp name="Asserion.test_strings">{% if assertion.strings %}{% for string in assertion.strings %}

       <stringProp name="97">{{string}}</stringProp>{% endfor %}{% endif %}

      </collectionProp>

      <stringProp name="Assertion.custom_message"></stringProp>

      <stringProp name="Assertion.test_field">Assertion.{{assertion.test_field}}</stringProp>

      <boolProp name="Assertion.assume_success">false</boolProp>

      <intProp name="Assertion.test_type">{{assertion.test_type}}</intProp>

     </ResponseAssertion>{% endfor %} {% endif %}

     <hashTree/>{% endfor %}{% endif %} {% endfor %}

   </hashTree>

  </hashTree>

 </hashTree>

</jmeterTestPlan>

组装出类似data.yaml格式的数据,并使用jinja2渲染模板即可得到完整的jmx文件

pip install pyyaml jinja2

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

import yaml

import jinja2

# 组装或读取数据

with open('data.yaml', encoding='utf-8') as f:

  data = yaml.safe_load(f)

 # 读取模板

with open('tpl.xml', encoding='utf-8') as f:

  tpl = f.read()

# 渲染模板生成jmx

jmx = jinja2.Template(tpl).render(data)

with open(jmx_file, 'w', encoding='utf-8') as f:

  f.write(jmx)

后计

在实际项目中,还涉及数据文件的拷贝,节点环境的部署,脚本的分发,报告的下载等等,可以使用paramiko或者fabric或ansible完成,压测节点数据分发的服务管理。

​现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
qq群号:485187702【暗号:csdn11】
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走! 希望能帮助到你

  • 17
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JMeter 是一个功能强大的开源性能测试工具,用于模拟多个用户同时访问 Web 应用程序或 API 来评估其性能、稳定性和可靠性。要使用 JMeter 进行负载压力测试并生成 JMX(Apache JMeter 运行时配置文件)文件,你可以按照以下步骤操作: 1. **安装和启动 JMeter**: 首先,你需要从 Apache JMeter 的官方网站下载并安装 JMeter。安装完成后,打开 JMeter。 2. **创建测试计划(Test Plan)**: 在 JMeter 工作界面,从“文件”菜单选择“新建”创建一个新的测试计划(Test Plan)。这是测试的基础结构,包含了线程组、采样器、断言、监听器等元素。 3. **添加采样器**: 在测试计划中添加 HTTP Request 或其他类型的采样器,例如 FTP、数据库等,用于模拟实际的请求。配置相应的 URL、参数、认证信息等。 4. **配置负载参数**: 使用线程组(Thread Group)定义并发用户数、循环次数等压力测试参数。这决定了模拟多少个用户同时执行请求。 5. **设置定时器**: 可能需要添加定时器(如恒定定时器)来控制请求之间的延迟,模仿用户真实行为。 6. **添加监听器**: 监听器(如查看结果树、聚合报告、响应时间显示器等)帮助你监控测试过程中的数据和性能指标。 7. **保存为 JMX 文件**: 在测试计划上右键,选择“Save As…” -> “Config File”(或者“Run Time Config File”),然后指定一个文件名和路径,保存为 JMX 格式,以便后续重复运行或分享。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值