## 新增功能 - **月度健康数据测试脚本** (`health-monthly-data-test.jmx`) - 模拟真实用户过去30天健康数据场景 - 支持日常数据批量导入测试 (50并发用户,10分钟) - 高频数据流测试 (100并发用户,30分钟) - 历史数据大批量导入测试 (20并发用户,200循环) - **数据库压力测试脚本** (`database-stress-test.jmx`) - 数据库连接池压力测试 (800并发用户,15分钟) - 极限并发测试 (1500并发用户,20分钟) - 事务完整性压力测试 (300并发用户,200循环) ## 测试结果 - **月度数据测试**: 10.9 TPS, 0% 错误率, 平均响应时间 3.9ms - **数据库压力测试**: 2400+ TPS, 0% 错误率, 系统稳定运行 ## 技术特性 - 动态数据生成 (心率、血氧、体温、步数等) - 真实时间戳模拟 (过去1-3个月历史数据) - 复杂JSON数据结构 (睡眠、运动、锻炼数据) - 并发用户渐进式增加策略 - 完整的性能监控和报告生成 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
236 lines
8.3 KiB
JavaScript
236 lines
8.3 KiB
JavaScript
/*
|
|
Licensed to the Apache Software Foundation (ASF) under one or more
|
|
contributor license agreements. See the NOTICE file distributed with
|
|
this work for additional information regarding copyright ownership.
|
|
The ASF licenses this file to You under the Apache License, Version 2.0
|
|
(the "License"); you may not use this file except in compliance with
|
|
the License. You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
var showControllersOnly = false;
|
|
var seriesFilter = "";
|
|
var filtersOnlySampleSeries = true;
|
|
|
|
/*
|
|
* Add header in statistics table to group metrics by category
|
|
* format
|
|
*
|
|
*/
|
|
function summaryTableHeader(header) {
|
|
var newRow = header.insertRow(-1);
|
|
newRow.className = "tablesorter-no-sort";
|
|
var cell = document.createElement('th');
|
|
cell.setAttribute("data-sorter", false);
|
|
cell.colSpan = 1;
|
|
cell.innerHTML = "Requests";
|
|
newRow.appendChild(cell);
|
|
|
|
cell = document.createElement('th');
|
|
cell.setAttribute("data-sorter", false);
|
|
cell.colSpan = 3;
|
|
cell.innerHTML = "Executions";
|
|
newRow.appendChild(cell);
|
|
|
|
cell = document.createElement('th');
|
|
cell.setAttribute("data-sorter", false);
|
|
cell.colSpan = 7;
|
|
cell.innerHTML = "Response Times (ms)";
|
|
newRow.appendChild(cell);
|
|
|
|
cell = document.createElement('th');
|
|
cell.setAttribute("data-sorter", false);
|
|
cell.colSpan = 1;
|
|
cell.innerHTML = "Throughput";
|
|
newRow.appendChild(cell);
|
|
|
|
cell = document.createElement('th');
|
|
cell.setAttribute("data-sorter", false);
|
|
cell.colSpan = 2;
|
|
cell.innerHTML = "Network (KB/sec)";
|
|
newRow.appendChild(cell);
|
|
}
|
|
|
|
/*
|
|
* Populates the table identified by id parameter with the specified data and
|
|
* format
|
|
*
|
|
*/
|
|
function createTable(table, info, formatter, defaultSorts, seriesIndex, headerCreator) {
|
|
var tableRef = table[0];
|
|
|
|
// Create header and populate it with data.titles array
|
|
var header = tableRef.createTHead();
|
|
|
|
// Call callback is available
|
|
if(headerCreator) {
|
|
headerCreator(header);
|
|
}
|
|
|
|
var newRow = header.insertRow(-1);
|
|
for (var index = 0; index < info.titles.length; index++) {
|
|
var cell = document.createElement('th');
|
|
cell.innerHTML = info.titles[index];
|
|
newRow.appendChild(cell);
|
|
}
|
|
|
|
var tBody;
|
|
|
|
// Create overall body if defined
|
|
if(info.overall){
|
|
tBody = document.createElement('tbody');
|
|
tBody.className = "tablesorter-no-sort";
|
|
tableRef.appendChild(tBody);
|
|
var newRow = tBody.insertRow(-1);
|
|
var data = info.overall.data;
|
|
for(var index=0;index < data.length; index++){
|
|
var cell = newRow.insertCell(-1);
|
|
cell.innerHTML = formatter ? formatter(index, data[index]): data[index];
|
|
}
|
|
}
|
|
|
|
// Create regular body
|
|
tBody = document.createElement('tbody');
|
|
tableRef.appendChild(tBody);
|
|
|
|
var regexp;
|
|
if(seriesFilter) {
|
|
regexp = new RegExp(seriesFilter, 'i');
|
|
}
|
|
// Populate body with data.items array
|
|
for(var index=0; index < info.items.length; index++){
|
|
var item = info.items[index];
|
|
if((!regexp || filtersOnlySampleSeries && !info.supportsControllersDiscrimination || regexp.test(item.data[seriesIndex]))
|
|
&&
|
|
(!showControllersOnly || !info.supportsControllersDiscrimination || item.isController)){
|
|
if(item.data.length > 0) {
|
|
var newRow = tBody.insertRow(-1);
|
|
for(var col=0; col < item.data.length; col++){
|
|
var cell = newRow.insertCell(-1);
|
|
cell.innerHTML = formatter ? formatter(col, item.data[col]) : item.data[col];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add support of columns sort
|
|
table.tablesorter({sortList : defaultSorts});
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
|
|
// Customize table sorter default options
|
|
$.extend( $.tablesorter.defaults, {
|
|
theme: 'blue',
|
|
cssInfoBlock: "tablesorter-no-sort",
|
|
widthFixed: true,
|
|
widgets: ['zebra']
|
|
});
|
|
|
|
var data = {"OkPercent": 100.0, "KoPercent": 0.0};
|
|
var dataset = [
|
|
{
|
|
"label" : "FAIL",
|
|
"data" : data.KoPercent,
|
|
"color" : "#FF6347"
|
|
},
|
|
{
|
|
"label" : "PASS",
|
|
"data" : data.OkPercent,
|
|
"color" : "#9ACD32"
|
|
}];
|
|
$.plot($("#flot-requests-summary"), dataset, {
|
|
series : {
|
|
pie : {
|
|
show : true,
|
|
radius : 1,
|
|
label : {
|
|
show : true,
|
|
radius : 3 / 4,
|
|
formatter : function(label, series) {
|
|
return '<div style="font-size:8pt;text-align:center;padding:2px;color:white;">'
|
|
+ label
|
|
+ '<br/>'
|
|
+ Math.round10(series.percent, -2)
|
|
+ '%</div>';
|
|
},
|
|
background : {
|
|
opacity : 0.5,
|
|
color : '#000'
|
|
}
|
|
}
|
|
}
|
|
},
|
|
legend : {
|
|
show : true
|
|
}
|
|
});
|
|
|
|
// Creates APDEX table
|
|
createTable($("#apdexTable"), {"supportsControllersDiscrimination": true, "overall": {"data": [1.0, 500, 1500, "Total"], "isController": false}, "titles": ["Apdex", "T (Toleration threshold)", "F (Frustration threshold)", "Label"], "items": [{"data": [1.0, 500, 1500, "Daily Health Data Upload"], "isController": false}]}, function(index, item){
|
|
switch(index){
|
|
case 0:
|
|
item = item.toFixed(3);
|
|
break;
|
|
case 1:
|
|
case 2:
|
|
item = formatDuration(item);
|
|
break;
|
|
}
|
|
return item;
|
|
}, [[0, 0]], 3);
|
|
|
|
// Create statistics table
|
|
createTable($("#statisticsTable"), {"supportsControllersDiscrimination": true, "overall": {"data": ["Total", 1500, 0, 0.0, 3.935333333333326, 1, 34, 4.0, 5.0, 5.0, 7.0, 10.878867437373986, 2.645349601470823, 24.92828021650759], "isController": false}, "titles": ["Label", "#Samples", "FAIL", "Error %", "Average", "Min", "Max", "Median", "90th pct", "95th pct", "99th pct", "Transactions/s", "Received", "Sent"], "items": [{"data": ["Daily Health Data Upload", 1500, 0, 0.0, 3.935333333333326, 1, 34, 4.0, 5.0, 5.0, 7.0, 10.878867437373986, 2.645349601470823, 24.92828021650759], "isController": false}]}, function(index, item){
|
|
switch(index){
|
|
// Errors pct
|
|
case 3:
|
|
item = item.toFixed(2) + '%';
|
|
break;
|
|
// Mean
|
|
case 4:
|
|
// Mean
|
|
case 7:
|
|
// Median
|
|
case 8:
|
|
// Percentile 1
|
|
case 9:
|
|
// Percentile 2
|
|
case 10:
|
|
// Percentile 3
|
|
case 11:
|
|
// Throughput
|
|
case 12:
|
|
// Kbytes/s
|
|
case 13:
|
|
// Sent Kbytes/s
|
|
item = item.toFixed(2);
|
|
break;
|
|
}
|
|
return item;
|
|
}, [[0, 0]], 0, summaryTableHeader);
|
|
|
|
// Create error table
|
|
createTable($("#errorsTable"), {"supportsControllersDiscrimination": false, "titles": ["Type of error", "Number of errors", "% in errors", "% in all samples"], "items": []}, function(index, item){
|
|
switch(index){
|
|
case 2:
|
|
case 3:
|
|
item = item.toFixed(2) + '%';
|
|
break;
|
|
}
|
|
return item;
|
|
}, [[1, 1]]);
|
|
|
|
// Create top5 errors by sampler
|
|
createTable($("#top5ErrorsBySamplerTable"), {"supportsControllersDiscrimination": false, "overall": {"data": ["Total", 1500, 0, "", "", "", "", "", "", "", "", "", ""], "isController": false}, "titles": ["Sample", "#Samples", "#Errors", "Error", "#Errors", "Error", "#Errors", "Error", "#Errors", "Error", "#Errors", "Error", "#Errors"], "items": [{"data": [], "isController": false}]}, function(index, item){
|
|
return item;
|
|
}, [[0, 0]], 0);
|
|
|
|
});
|