| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430 |
- <?xml version="1.0" encoding="UTF-8"?>
- <jmeterTestPlan version="1.2" properties="5.0" jmeter="5.6.3">
- <hashTree>
- <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="AIGCSpace 压力测试" enabled="true">
- <stringProp name="TestPlan.comments">AIGCSpace 稳定性 + 压力测试计划
- 覆盖:登录、模型列表、用户信息、余额查询、非流式对话
- 运行参数通过命令行 -J 传入</stringProp>
- <boolProp name="TestPlan.functional_mode">false</boolProp>
- <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
- <elementProp name="TestPlan.user_defined_variables" elementType="Arguments">
- <collectionProp name="Arguments.arguments">
- <!-- 基础配置 -->
- <elementProp name="base_url" elementType="Argument">
- <stringProp name="Argument.name">base_url</stringProp>
- <stringProp name="Argument.value">${__P(base_url,https://aigc-api.wangxunai.com)}</stringProp>
- </elementProp>
- <elementProp name="thread_count" elementType="Argument">
- <stringProp name="Argument.name">thread_count</stringProp>
- <stringProp name="Argument.value">${__P(thread_count,20)}</stringProp>
- </elementProp>
- <elementProp name="ramp_up" elementType="Argument">
- <stringProp name="Argument.name">ramp_up</stringProp>
- <stringProp name="Argument.value">${__P(ramp_up,60)}</stringProp>
- </elementProp>
- <elementProp name="duration" elementType="Argument">
- <stringProp name="Argument.name">duration</stringProp>
- <stringProp name="Argument.value">${__P(duration,1800)}</stringProp>
- </elementProp>
- <!-- 对话测试用的模型名,改成你们实际有的模型 -->
- <elementProp name="test_model" elementType="Argument">
- <stringProp name="Argument.name">test_model</stringProp>
- <stringProp name="Argument.value">${__P(test_model,qwen-turbo)}</stringProp>
- </elementProp>
- </collectionProp>
- </elementProp>
- </TestPlan>
- <hashTree>
- <!-- ============================================================ -->
- <!-- HTTP 默认配置 -->
- <!-- ============================================================ -->
- <ConfigTestElement guiclass="HttpDefaultsGui" testclass="ConfigTestElement" testname="HTTP 默认配置" enabled="true">
- <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
- <collectionProp name="Arguments.arguments"/>
- </elementProp>
- <stringProp name="HTTPSampler.domain">${__regexExtract(${base_url},https?://([^/]+),1,1,localhost)}</stringProp>
- <stringProp name="HTTPSampler.port"></stringProp>
- <stringProp name="HTTPSampler.protocol">${__regexExtract(${base_url},(https?),1,1,https)}</stringProp>
- <stringProp name="HTTPSampler.contentEncoding">UTF-8</stringProp>
- <boolProp name="HTTPSampler.image_parser">false</boolProp>
- <boolProp name="HTTPSampler.concurrentDwn">false</boolProp>
- </ConfigTestElement>
- <hashTree/>
- <!-- HTTP Header 管理器(全局) -->
- <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="全局 Header" enabled="true">
- <collectionProp name="HeaderManager.headers">
- <elementProp name="Content-Type" elementType="Header">
- <stringProp name="Header.name">Content-Type</stringProp>
- <stringProp name="Header.value">application/json</stringProp>
- </elementProp>
- <elementProp name="Accept" elementType="Header">
- <stringProp name="Header.name">Accept</stringProp>
- <stringProp name="Header.value">application/json</stringProp>
- </elementProp>
- </collectionProp>
- </HeaderManager>
- <hashTree/>
- <!-- ============================================================ -->
- <!-- CSV 用户数据 -->
- <!-- ============================================================ -->
- <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="用户数据" enabled="true">
- <stringProp name="filename">../../backend/test_users.csv</stringProp>
- <stringProp name="fileEncoding">UTF-8</stringProp>
- <stringProp name="variableNames">username,password,email,apikey,user_id</stringProp>
- <boolProp name="ignoreFirstLine">true</boolProp>
- <stringProp name="delimiter">,</stringProp>
- <boolProp name="quotedData">false</boolProp>
- <boolProp name="recycle">true</boolProp>
- <boolProp name="stopThread">false</boolProp>
- <stringProp name="shareMode">shareMode.all</stringProp>
- </CSVDataSet>
- <hashTree/>
- <!-- ============================================================ -->
- <!-- 场景一:纯读接口(无需认证)- 模型列表 -->
- <!-- 目的:测试无认证接口的基础吞吐量 -->
- <!-- ============================================================ -->
- <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="场景1 - 模型列表(无认证)" enabled="true">
- <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
- <elementProp name="ThreadGroup.main_controller" elementType="LoopController">
- <boolProp name="LoopController.continue_forever">true</boolProp>
- <intProp name="LoopController.loops">-1</intProp>
- </elementProp>
- <stringProp name="ThreadGroup.num_threads">${thread_count}</stringProp>
- <stringProp name="ThreadGroup.ramp_time">${ramp_up}</stringProp>
- <boolProp name="ThreadGroup.scheduler">true</boolProp>
- <stringProp name="ThreadGroup.duration">${duration}</stringProp>
- <stringProp name="ThreadGroup.delay">0</stringProp>
- </ThreadGroup>
- <hashTree>
- <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="GET 模型列表" enabled="true">
- <stringProp name="HTTPSampler.path">/api/models?page=1&page_size=20</stringProp>
- <stringProp name="HTTPSampler.method">GET</stringProp>
- <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
- <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
- </HTTPSamplerProxy>
- <hashTree>
- <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="断言-状态码200" enabled="true">
- <collectionProp name="Asserion.test_strings">
- <stringProp name="49586">200</stringProp>
- </collectionProp>
- <stringProp name="Assertion.custom_message">模型列表接口返回非200</stringProp>
- <stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
- <boolProp name="Assertion.assume_success">false</boolProp>
- <intProp name="Assertion.test_type">8</intProp>
- </ResponseAssertion>
- <hashTree/>
- <JSONPathAssertion guiclass="JSONPathAssertionGui" testclass="JSONPathAssertion" testname="断言-业务code=200" enabled="true">
- <stringProp name="JSON_PATH">$.code</stringProp>
- <stringProp name="EXPECTED_VALUE">200</stringProp>
- <boolProp name="JSONVALIDATION">true</boolProp>
- <boolProp name="EXPECT_NULL">false</boolProp>
- <boolProp name="INVERT">false</boolProp>
- <boolProp name="ISREGEX">false</boolProp>
- </JSONPathAssertion>
- <hashTree/>
- </hashTree>
- <!-- 思考时间:模拟真实用户,避免打爆服务器 -->
- <ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer" testname="思考时间 500ms" enabled="true">
- <stringProp name="ConstantTimer.delay">500</stringProp>
- </ConstantTimer>
- <hashTree/>
- </hashTree>
- <!-- ============================================================ -->
- <!-- 场景二:认证接口 - 登录 + 用户信息 + 余额查询 -->
- <!-- 目的:测试认证链路和高频读接口,重点验证 check_balance 优化效果 -->
- <!-- ============================================================ -->
- <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="场景2 - 认证链路(登录+用户信息+余额)" enabled="true">
- <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
- <elementProp name="ThreadGroup.main_controller" elementType="LoopController">
- <boolProp name="LoopController.continue_forever">true</boolProp>
- <intProp name="LoopController.loops">-1</intProp>
- </elementProp>
- <stringProp name="ThreadGroup.num_threads">${thread_count}</stringProp>
- <stringProp name="ThreadGroup.ramp_time">${ramp_up}</stringProp>
- <boolProp name="ThreadGroup.scheduler">true</boolProp>
- <stringProp name="ThreadGroup.duration">${duration}</stringProp>
- <stringProp name="ThreadGroup.delay">5</stringProp>
- </ThreadGroup>
- <hashTree>
- <!-- 步骤1:登录,提取 Token -->
- <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="POST 登录" enabled="true">
- <stringProp name="HTTPSampler.path">/api/auth/login</stringProp>
- <stringProp name="HTTPSampler.method">POST</stringProp>
- <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
- <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
- <collectionProp name="Arguments.arguments">
- <elementProp name="" elementType="HTTPArgument">
- <boolProp name="HTTPArgument.always_encode">false</boolProp>
- <stringProp name="Argument.value">{"username":"${username}","password":"${password}","encrypted":false}</stringProp>
- <stringProp name="Argument.metadata">=</stringProp>
- </elementProp>
- </collectionProp>
- </elementProp>
- <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
- <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
- </HTTPSamplerProxy>
- <hashTree>
- <!-- 提取 access_token -->
- <JSONPathExtractor guiclass="JSONPathExtractorGui" testclass="JSONPathExtractor" testname="提取 Token" enabled="true">
- <stringProp name="JSONPathExtractor.referenceName">access_token</stringProp>
- <stringProp name="JSONPathExtractor.jsonPathExpr">$.data.access_token</stringProp>
- <stringProp name="JSONPathExtractor.defaultValue">TOKEN_NOT_FOUND</stringProp>
- </JSONPathExtractor>
- <hashTree/>
- <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="断言-登录成功" enabled="true">
- <collectionProp name="Asserion.test_strings">
- <stringProp name="49586">200</stringProp>
- </collectionProp>
- <stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
- <boolProp name="Assertion.assume_success">false</boolProp>
- <intProp name="Assertion.test_type">8</intProp>
- </ResponseAssertion>
- <hashTree/>
- </hashTree>
- <!-- 步骤2:用户信息(需要 Token) -->
- <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="GET 用户信息" enabled="true">
- <stringProp name="HTTPSampler.path">/api/user/profile</stringProp>
- <stringProp name="HTTPSampler.method">GET</stringProp>
- <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
- <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
- </HTTPSamplerProxy>
- <hashTree>
- <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="Authorization Header" enabled="true">
- <collectionProp name="HeaderManager.headers">
- <elementProp name="Authorization" elementType="Header">
- <stringProp name="Header.name">Authorization</stringProp>
- <stringProp name="Header.value">Bearer ${access_token}</stringProp>
- </elementProp>
- </collectionProp>
- </HeaderManager>
- <hashTree/>
- <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="断言-用户信息200" enabled="true">
- <collectionProp name="Asserion.test_strings">
- <stringProp name="49586">200</stringProp>
- </collectionProp>
- <stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
- <boolProp name="Assertion.assume_success">false</boolProp>
- <intProp name="Assertion.test_type">8</intProp>
- </ResponseAssertion>
- <hashTree/>
- </hashTree>
- <!-- 步骤3:余额查询(高频,重点测 check_balance 优化) -->
- <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="GET 余额查询" enabled="true">
- <stringProp name="HTTPSampler.path">/api/billing/balance</stringProp>
- <stringProp name="HTTPSampler.method">GET</stringProp>
- <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
- <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
- </HTTPSamplerProxy>
- <hashTree>
- <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="Authorization Header" enabled="true">
- <collectionProp name="HeaderManager.headers">
- <elementProp name="Authorization" elementType="Header">
- <stringProp name="Header.name">Authorization</stringProp>
- <stringProp name="Header.value">Bearer ${access_token}</stringProp>
- </elementProp>
- </collectionProp>
- </HeaderManager>
- <hashTree/>
- <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="断言-余额查询200" enabled="true">
- <collectionProp name="Asserion.test_strings">
- <stringProp name="49586">200</stringProp>
- </collectionProp>
- <stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
- <boolProp name="Assertion.assume_success">false</boolProp>
- <intProp name="Assertion.test_type">8</intProp>
- </ResponseAssertion>
- <hashTree/>
- </hashTree>
- <ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer" testname="思考时间 1s" enabled="true">
- <stringProp name="ConstantTimer.delay">1000</stringProp>
- </ConstantTimer>
- <hashTree/>
- </hashTree>
- <!-- ============================================================ -->
- <!-- 场景三:非流式对话(重点测 db 连接池优化效果) -->
- <!-- 注意:此场景会消耗用户余额,并发不要太高 -->
- <!-- ============================================================ -->
- <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="场景3 - 非流式对话" enabled="true">
- <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
- <elementProp name="ThreadGroup.main_controller" elementType="LoopController">
- <boolProp name="LoopController.continue_forever">true</boolProp>
- <intProp name="LoopController.loops">-1</intProp>
- </elementProp>
- <!-- 对话场景并发数设低一些,避免余额耗尽 -->
- <stringProp name="ThreadGroup.num_threads">5</stringProp>
- <stringProp name="ThreadGroup.ramp_time">30</stringProp>
- <boolProp name="ThreadGroup.scheduler">true</boolProp>
- <stringProp name="ThreadGroup.duration">${duration}</stringProp>
- <stringProp name="ThreadGroup.delay">10</stringProp>
- </ThreadGroup>
- <hashTree>
- <!-- 先登录获取 Token -->
- <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="POST 登录(对话前)" enabled="true">
- <stringProp name="HTTPSampler.path">/api/auth/login</stringProp>
- <stringProp name="HTTPSampler.method">POST</stringProp>
- <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
- <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
- <collectionProp name="Arguments.arguments">
- <elementProp name="" elementType="HTTPArgument">
- <boolProp name="HTTPArgument.always_encode">false</boolProp>
- <stringProp name="Argument.value">{"username":"${username}","password":"${password}","encrypted":false}</stringProp>
- <stringProp name="Argument.metadata">=</stringProp>
- </elementProp>
- </collectionProp>
- </elementProp>
- <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
- <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
- </HTTPSamplerProxy>
- <hashTree>
- <JSONPathExtractor guiclass="JSONPathExtractorGui" testclass="JSONPathExtractor" testname="提取 Token" enabled="true">
- <stringProp name="JSONPathExtractor.referenceName">chat_token</stringProp>
- <stringProp name="JSONPathExtractor.jsonPathExpr">$.data.access_token</stringProp>
- <stringProp name="JSONPathExtractor.defaultValue">TOKEN_NOT_FOUND</stringProp>
- </JSONPathExtractor>
- <hashTree/>
- </hashTree>
- <!-- 非流式对话 -->
- <HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="POST 非流式对话" enabled="true">
- <stringProp name="HTTPSampler.path">/api/llm/chat</stringProp>
- <stringProp name="HTTPSampler.method">POST</stringProp>
- <boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
- <elementProp name="HTTPsampler.Arguments" elementType="Arguments">
- <collectionProp name="Arguments.arguments">
- <elementProp name="" elementType="HTTPArgument">
- <boolProp name="HTTPArgument.always_encode">false</boolProp>
- <stringProp name="Argument.value">{"model":"${test_model}","messages":[{"role":"user","content":"你好,请用一句话介绍自己"}],"stream":false}</stringProp>
- <stringProp name="Argument.metadata">=</stringProp>
- </elementProp>
- </collectionProp>
- </elementProp>
- <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
- <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
- <!-- 对话接口超时设长一些 -->
- <stringProp name="HTTPSampler.connect_timeout">10000</stringProp>
- <stringProp name="HTTPSampler.response_timeout">30000</stringProp>
- </HTTPSamplerProxy>
- <hashTree>
- <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="Authorization Header" enabled="true">
- <collectionProp name="HeaderManager.headers">
- <elementProp name="Authorization" elementType="Header">
- <stringProp name="Header.name">Authorization</stringProp>
- <stringProp name="Header.value">Bearer ${chat_token}</stringProp>
- </elementProp>
- </collectionProp>
- </HeaderManager>
- <hashTree/>
- <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="断言-对话返回200" enabled="true">
- <collectionProp name="Asserion.test_strings">
- <stringProp name="49586">200</stringProp>
- </collectionProp>
- <stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
- <boolProp name="Assertion.assume_success">false</boolProp>
- <intProp name="Assertion.test_type">8</intProp>
- </ResponseAssertion>
- <hashTree/>
- </hashTree>
- <!-- 对话间隔 5 秒,避免余额耗尽太快 -->
- <ConstantTimer guiclass="ConstantTimerGui" testclass="ConstantTimer" testname="对话间隔 5s" enabled="true">
- <stringProp name="ConstantTimer.delay">5000</stringProp>
- </ConstantTimer>
- <hashTree/>
- </hashTree>
- <!-- ============================================================ -->
- <!-- 监听器:聚合报告 -->
- <!-- ============================================================ -->
- <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>
- <url>true</url>
- <threadCounts>true</threadCounts>
- <idleTime>true</idleTime>
- <connectTime>true</connectTime>
- </value>
- </objProp>
- <stringProp name="filename">results/test_results.jtl</stringProp>
- </ResultCollector>
- <hashTree/>
- <!-- 响应时间图 -->
- <ResultCollector guiclass="RespTimeGraphVisualizer" 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>
- </value>
- </objProp>
- <stringProp name="filename"></stringProp>
- </ResultCollector>
- <hashTree/>
- <!-- 错误日志(只记录失败请求) -->
- <ResultCollector guiclass="SimpleDataWriter" testclass="ResultCollector" testname="错误日志" enabled="true">
- <boolProp name="ResultCollector.error_logging">true</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>
- <responseData>true</responseData>
- <responseDataOnError>true</responseDataOnError>
- <saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
- </value>
- </objProp>
- <stringProp name="filename">results/errors.jtl</stringProp>
- </ResultCollector>
- <hashTree/>
- </hashTree>
- </hashTree>
- </jmeterTestPlan>
|