feat(cli): v1.4.0 增加基准测试脚本

pull/49/head
moonrailgun 3 years ago
parent 9b79563fe2
commit 8491e5780c

@ -1,6 +1,6 @@
{
"name": "tailchat-cli",
"version": "1.3.0",
"version": "1.4.0",
"description": "A Command line interface of tailchat",
"bin": {
"tailchat": "./bin/cli"
@ -40,13 +40,17 @@
"lodash": "^4.17.21",
"node-plop": "^0.26.3",
"ora": "5.4.1",
"p-all": "2.1.0",
"p-series": "2.1.0",
"plop": "^3.0.5",
"pretty-ms": "7.0.1",
"tailchat-server-sdk": "^0.0.12",
"yargs": "^17.4.0"
},
"devDependencies": {
"@types/fs-extra": "^9.0.13",
"@types/inquirer": "^8.2.1",
"@types/lodash": "^4.14.170",
"@types/node": "16.11.7",
"@types/yargs": "^17.0.10",
"ts-node": "^10.7.0",

@ -0,0 +1,166 @@
import { CommandModule } from 'yargs';
import { TcBroker } from 'tailchat-server-sdk';
import defaultBrokerConfig from 'tailchat-server-sdk/dist/runner/moleculer.config';
import { config } from 'dotenv';
import _ from 'lodash';
import os from 'os';
import pAll from 'p-all';
import pSeries from 'p-series';
import ora from 'ora';
import prettyMs from 'pretty-ms';
export const benchCommand: CommandModule = {
command: 'bench',
describe: '压力测试',
builder: (yargs) =>
yargs
.command(
'message',
'通过内网请求进行压力测试(适用于纯业务测试)',
(yargs) =>
yargs
.positional('groupId', {
describe: '群组ID',
demandOption: true,
type: 'string',
})
.positional('converseId', {
describe: '会话ID',
demandOption: true,
type: 'string',
})
.positional('userId', {
describe: '用户ID',
demandOption: true,
type: 'string',
})
.positional('num', {
describe: '测试次数',
type: 'number',
default: 100,
})
.positional('parallel', {
describe: '是否并发',
type: 'boolean',
default: false,
}),
async (args) => {
config();
const broker = new TcBroker({
...defaultBrokerConfig,
transporter: process.env.TRANSPORTER,
logger: false,
});
await broker.start();
printSystemInfo();
console.log('===============');
await startBenchmark<number>({
parallel: args.parallel,
number: args.num,
task: async (i) => {
const start = process.hrtime();
await broker.call(
'chat.message.sendMessage',
{
converseId: args.converseId,
groupId: args.groupId,
content: `benchmessage ${i + 1}`,
},
{
meta: {
userId: args.userId,
},
}
);
const usage = calcUsage(start);
return usage;
},
onCompleted: (res) => {
console.log(`测试数量: \t${res.length}`);
console.log(`最大用时: \t${prettyMs(Math.max(...res))}`);
console.log(`最小用时: \t${prettyMs(Math.min(...res))}`);
console.log(`平均用时: \t${prettyMs(_.mean(res))}`);
},
});
await broker.stop();
}
)
.demandCommand(),
handler() {},
};
/**
*
*/
function printSystemInfo() {
console.log(`主机: \t${os.hostname()}`);
console.log(`系统: \t${os.type()} - ${os.release()}`);
console.log(`架构: \t${os.arch()} - ${os.version()}`);
console.log(`CPU: \t${os.cpus().length}`);
console.log(`内存: \t${os.totalmem()}`);
}
function calcUsage(startTime: [number, number]) {
const diff = process.hrtime(startTime);
const usage = (diff[0] + diff[1] / 1e9) * 1000;
return usage;
}
interface BenchmarkOptions<T> {
parallel: boolean; // 是否并发
parallelLimit?: number; // 并发上限, 默认不限制(Infinity)
task: (index: number) => Promise<T>;
number?: number;
onCompleted: (res: T[]) => void;
}
/**
*
*/
async function startBenchmark<T>(options: BenchmarkOptions<T>) {
const {
parallel,
parallelLimit = Infinity,
task,
number = 100,
onCompleted,
} = options;
const spinner = ora();
spinner.info(
`测试方式: ${parallel ? `并行, 上限 ${parallelLimit}` : `串行`}`
);
spinner.info(`执行任务数: ${number}`);
spinner.start('正在执行基准测试...');
try {
const startTime = process.hrtime();
let res: T[] = [];
if (parallel) {
res = await pAll<T>(
[...Array.from({ length: number }).map((_, i) => () => task(i))],
{
concurrency: parallelLimit,
}
);
} else {
res = await pSeries<T>([
...Array.from({ length: number }).map((_, i) => () => task(i)),
]);
}
spinner.succeed(`基准测试完毕, 用时 ${prettyMs(calcUsage(startTime))}`);
onCompleted(res);
} catch (err) {
console.error(err);
spinner.fail(`基准测试出现问题`).stop();
}
}

@ -2,11 +2,13 @@ import yargs from 'yargs';
import { createCommand } from './commands/create';
import { connectCommand } from './commands/connect';
import { declarationCommand } from './commands/declaration';
import { benchCommand } from './commands/bench';
yargs
.demandCommand()
.command(createCommand)
.command(connectCommand)
.command(benchCommand)
.command(declarationCommand)
.alias('h', 'help')
.scriptName('tailchat')

@ -60,7 +60,9 @@ importers:
specifiers:
'@types/fs-extra': ^9.0.13
'@types/inquirer': ^8.2.1
'@types/lodash': ^4.14.170
'@types/node': 16.11.7
'@types/pretty-hrtime': ^1.0.1
'@types/yargs': ^17.0.10
dotenv: ^16.0.0
fs-extra: ^10.1.0
@ -69,7 +71,10 @@ importers:
lodash: ^4.17.21
node-plop: ^0.26.3
ora: 5.4.1
p-all: 2.1.0
p-series: 2.1.0
plop: ^3.0.5
pretty-ms: 7.0.1
tailchat-server-sdk: ^0.0.12
ts-node: ^10.7.0
typescript: ^4.6.3
@ -82,13 +87,18 @@ importers:
lodash: 4.17.21
node-plop: 0.26.3
ora: 5.4.1
p-all: 2.1.0
p-series: 2.1.0
plop: 3.1.1
pretty-ms: 7.0.1
tailchat-server-sdk: link:../server/packages/sdk
yargs: 17.5.1
devDependencies:
'@types/fs-extra': 9.0.13
'@types/inquirer': 8.2.3
'@types/lodash': 4.14.184
'@types/node': 16.11.7
'@types/pretty-hrtime': 1.0.1
'@types/yargs': 17.0.12
ts-node: 10.9.1_rk33jgomelnuriwr3foeinccb4
typescript: 4.8.2
@ -4181,6 +4191,11 @@ packages:
resolution: {integrity: sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==}
engines: {node: '>=6'}
/@sindresorhus/is/0.15.0:
resolution: {integrity: sha512-lu8BpxjAtRCAo5ifytTpCPCj99LF7o/2Myn+NXyNCBqvPYn7Pjd76AMmUB5l7XF1U6t0hcWrlEM5ESufW7wAeA==}
engines: {node: '>=6'}
dev: false
/@sindresorhus/is/0.7.0:
resolution: {integrity: sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==}
engines: {node: '>=4'}
@ -18170,7 +18185,6 @@ packages:
engines: {node: '>=6'}
dependencies:
p-map: 2.1.0
dev: true
/p-cancelable/0.4.1:
resolution: {integrity: sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==}
@ -18276,6 +18290,11 @@ packages:
eventemitter3: 4.0.7
p-timeout: 3.2.0
/p-reduce/2.1.0:
resolution: {integrity: sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==}
engines: {node: '>=8'}
dev: false
/p-retry/4.6.2:
resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==}
engines: {node: '>=8'}
@ -18283,6 +18302,14 @@ packages:
'@types/retry': 0.12.0
retry: 0.13.1
/p-series/2.1.0:
resolution: {integrity: sha512-vEAnkG1ikRT1kPBrKwpj7AFYQkd1hjt/oHeppxtpoPxy5gEt+OWiHZJN3tMqvFa+UJfVwO3lwHoMUpMYBLKnaQ==}
engines: {node: '>=8'}
dependencies:
'@sindresorhus/is': 0.15.0
p-reduce: 2.1.0
dev: false
/p-timeout/2.0.1:
resolution: {integrity: sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==}
engines: {node: '>=4'}
@ -18391,6 +18418,11 @@ packages:
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
/parse-ms/2.1.0:
resolution: {integrity: sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==}
engines: {node: '>=6'}
dev: false
/parse-node-version/1.0.1:
resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==}
engines: {node: '>= 0.10'}
@ -19512,6 +19544,13 @@ packages:
resolution: {integrity: sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==}
engines: {node: '>= 0.8'}
/pretty-ms/7.0.1:
resolution: {integrity: sha512-973driJZvxiGOQ5ONsFhOF/DtzPMOMtgC11kCpUrPGMTgqp2q/1gwzCquocrN33is0VZ5GFHXZYMM9l6h67v2Q==}
engines: {node: '>=10'}
dependencies:
parse-ms: 2.1.0
dev: false
/pretty-time/1.1.0:
resolution: {integrity: sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==}
engines: {node: '>=4'}

Loading…
Cancel
Save