[add] first
This commit is contained in:
13
benchmark/config/BenchmarkConfig.ts
Normal file
13
benchmark/config/BenchmarkConfig.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export const benchmarkConfig = {
|
||||
/** 压测使用的APIServer */
|
||||
server: 'http://127.0.0.1:3000',
|
||||
|
||||
/** 一共运行几次压测事务 */
|
||||
total: 200000,
|
||||
/** 同时并发的请求数量 */
|
||||
concurrency: 100,
|
||||
/** API请求的超时时间(超时将断开HTTP连接,释放资源,前端默认为10) */
|
||||
timeout: 10000,
|
||||
/** 是否将错误的详情日志打印到Log */
|
||||
showError: false
|
||||
}
|
||||
13
benchmark/http.ts
Normal file
13
benchmark/http.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { benchmarkConfig } from './config/BenchmarkConfig';
|
||||
import { HttpRunner } from './models/HTTPRunner';
|
||||
|
||||
const req = {
|
||||
a: 123456,
|
||||
b: 'Hello, World!',
|
||||
c: true,
|
||||
d: new Uint8Array(100000)
|
||||
}
|
||||
|
||||
new HttpRunner(async function () {
|
||||
await this.callApi('Test', req);
|
||||
}, benchmarkConfig).start();
|
||||
285
benchmark/models/HTTPRunner.ts
Normal file
285
benchmark/models/HTTPRunner.ts
Normal file
@@ -0,0 +1,285 @@
|
||||
import 'colors';
|
||||
import * as http from "http";
|
||||
import * as https from "https";
|
||||
import 'k8w-extend-native';
|
||||
import { TsrpcError, TsrpcErrorType } from "tsrpc-proto";
|
||||
import { HttpClient } from '../../src/client/http/HttpClient';
|
||||
import { benchmarkConfig } from "../config/BenchmarkConfig";
|
||||
import { serviceProto } from '../protocols/proto';
|
||||
|
||||
export interface HttpRunnerConfig {
|
||||
total: number;
|
||||
concurrency: number;
|
||||
showError?: boolean;
|
||||
}
|
||||
|
||||
export class HttpRunner {
|
||||
|
||||
private _config: HttpRunnerConfig;
|
||||
|
||||
// 执行单个事务的方法
|
||||
private _single: (this: HttpRunner) => Promise<void>;
|
||||
|
||||
// 执行进度信息
|
||||
private _progress?: {
|
||||
startTime: number,
|
||||
lastSuccTime?: number,
|
||||
started: number,
|
||||
finished: number,
|
||||
succ: number,
|
||||
fail: number
|
||||
};
|
||||
|
||||
constructor(single: HttpRunner['_single'], config: HttpRunnerConfig) {
|
||||
this._single = single.bind(this);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
start() {
|
||||
this._progress = {
|
||||
startTime: Date.now(),
|
||||
started: 0,
|
||||
finished: 0,
|
||||
succ: 0,
|
||||
fail: 0
|
||||
}
|
||||
|
||||
// 启动并发
|
||||
for (let i = 0; i < this._config.concurrency; ++i) {
|
||||
this._doTrans();
|
||||
}
|
||||
|
||||
console.log('Benchmark start!');
|
||||
this._startReport();
|
||||
}
|
||||
|
||||
private _doTrans() {
|
||||
if (this._isStoped || !this._progress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._progress.started < this._config.total) {
|
||||
++this._progress.started;
|
||||
let startTime = Date.now();
|
||||
this._single().then(v => {
|
||||
++this._progress!.succ;
|
||||
this._progress!.lastSuccTime = Date.now();
|
||||
}).catch(e => {
|
||||
++this._progress!.fail;
|
||||
if (this._config.showError) {
|
||||
console.error('[Error]', e.message);
|
||||
}
|
||||
}).then(() => {
|
||||
++this._progress!.finished;
|
||||
if (this._progress!.finished === this._config.total) {
|
||||
this._finish();
|
||||
}
|
||||
else {
|
||||
this._doTrans();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private _reportInterval?: NodeJS.Timeout;
|
||||
private _startReport() {
|
||||
this._reportInterval = setInterval(() => {
|
||||
this._report();
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
private _isStoped = false;
|
||||
stop() {
|
||||
this._isStoped = true;
|
||||
}
|
||||
|
||||
private _finish() {
|
||||
if (!this._progress) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._reportInterval && clearInterval(this._reportInterval);
|
||||
|
||||
console.log('\n\n-------------------------------\n Benchmark finished! \n-------------------------------');
|
||||
|
||||
let usedTime = Date.now() - this._progress.startTime;
|
||||
console.log(` Transaction Execution Result `.bgBlue.white);
|
||||
console.log(`Started=${this._progress.started}, Finished=${this._progress.finished}, UsedTime=${usedTime}ms`.green);
|
||||
console.log(`Succ=${this._progress.succ}, Fail=${this._progress.fail}, TPS=${this._progress.succ / (this._progress.lastSuccTime! - this._progress.startTime) * 1000 | 0}\n`.green)
|
||||
|
||||
// TIME TPS(完成的)
|
||||
console.log(` API Execution Result `.bgBlue.white);
|
||||
|
||||
// [KEY] RPS(完成的) AVG P95 P99
|
||||
for (let key in this._apiStat) {
|
||||
let stat = this._apiStat[key];
|
||||
stat.resTime = stat.resTime.orderBy(v => v);
|
||||
|
||||
let send = stat.sendReq;
|
||||
let succ = stat.resTime.length;
|
||||
let netErr = stat.networkError;
|
||||
let apiErr = stat.otherError;
|
||||
let avg = stat.resTime[stat.resTime.length >> 1] | 0;
|
||||
let p95 = stat.resTime[stat.resTime.length * 0.95 | 0] | 0;
|
||||
let p99 = stat.resTime[stat.resTime.length * 0.99 | 0] | 0;
|
||||
|
||||
this._logTable([
|
||||
[{ text: 'Api' + key + ' '.repeat(this._maxApiNameLength - key.length), color: 'green' }, 'Send', 'Succ', 'QPS', 'NetErr', 'ApiErr', 'AVG ', 'P95 ', 'P99 '],
|
||||
['', '' + send,
|
||||
{ text: '' + succ, color: 'green' },
|
||||
{ text: '' + (succ / (stat.last.succTime - stat.startTime) * 1000 | 0), color: 'green' },
|
||||
netErr ? { text: '' + netErr, color: 'red' } : '0',
|
||||
apiErr ? { text: '' + apiErr, color: 'red' } : '0',
|
||||
{ text: avg ? avg + 'ms' : '-', color: 'yellow' },
|
||||
{ text: p95 ? p95 + 'ms' : '-', color: 'yellow' },
|
||||
{ text: p99 ? p99 + 'ms' : '-', color: 'yellow' }
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
private _apiStat: {
|
||||
[key: string]: {
|
||||
sendReq: number,
|
||||
resTime: number[],
|
||||
succ: number,
|
||||
networkError: number,
|
||||
otherError: number,
|
||||
startTime: number,
|
||||
last: {
|
||||
sendReq: number,
|
||||
resTime: number[],
|
||||
succ: number,
|
||||
networkError: number,
|
||||
otherError: number,
|
||||
startTime: number,
|
||||
succTime: number
|
||||
}
|
||||
}
|
||||
} = {};
|
||||
|
||||
private _maxApiNameLength = 0;
|
||||
/**
|
||||
* callApi 并且计入统计
|
||||
*/
|
||||
callApi: typeof benchmarkClient.callApi = async (apiName, req) => {
|
||||
this._maxApiNameLength = Math.max(apiName.length, this._maxApiNameLength);
|
||||
|
||||
if (!this._apiStat[apiName]) {
|
||||
this._apiStat[apiName] = {
|
||||
sendReq: 0,
|
||||
resTime: [],
|
||||
succ: 0,
|
||||
networkError: 0,
|
||||
otherError: 0,
|
||||
startTime: Date.now(),
|
||||
last: {
|
||||
sendReq: 0,
|
||||
resTime: [],
|
||||
succ: 0,
|
||||
networkError: 0,
|
||||
otherError: 0,
|
||||
startTime: Date.now(),
|
||||
succTime: 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
++this._apiStat[apiName].sendReq;
|
||||
++this._apiStat[apiName].last.sendReq;
|
||||
|
||||
let startTime = Date.now();
|
||||
let ret = await benchmarkClient.callApi(apiName, req);
|
||||
|
||||
if (ret.isSucc) {
|
||||
this._apiStat[apiName].last.succTime = Date.now();
|
||||
this._apiStat[apiName].resTime.push(Date.now() - startTime);
|
||||
this._apiStat[apiName].last.resTime.push(Date.now() - startTime);
|
||||
++this._apiStat[apiName].succ;
|
||||
++this._apiStat[apiName].last.succ;
|
||||
}
|
||||
else {
|
||||
if (ret.err.type === TsrpcErrorType.NetworkError) {
|
||||
++this._apiStat[apiName].networkError;
|
||||
++this._apiStat[apiName].last.networkError;
|
||||
}
|
||||
else {
|
||||
++this._apiStat[apiName].otherError;
|
||||
++this._apiStat[apiName].last.otherError;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private _report() {
|
||||
console.log(new Date().format('hh:mm:ss').gray, `Started=${this._progress!.started}/${this._config.total}, Finished=${this._progress!.finished}, Succ=${this._progress!.succ.toString().green}, Fail=${this._progress!.fail.toString()[this._progress!.fail > 0 ? 'red' : 'white']}`,
|
||||
this._progress!.lastSuccTime ? `TPS=${this._progress!.succ / (this._progress!.lastSuccTime - this._progress!.startTime) * 1000 | 0}` : '')
|
||||
|
||||
for (let key in this._apiStat) {
|
||||
let stat = this._apiStat[key];
|
||||
|
||||
let send = stat.last.sendReq;
|
||||
let succ = stat.last.resTime.length;
|
||||
let netErr = stat.last.networkError;
|
||||
let apiErr = stat.last.otherError;
|
||||
|
||||
this._logTable([
|
||||
[{ text: 'Api' + key + ' '.repeat(this._maxApiNameLength - key.length), color: 'green' }, 'Send', 'Succ', 'QPS', 'NetErr', 'ApiErr'],
|
||||
['', '' + send,
|
||||
{ text: '' + succ, color: 'green' },
|
||||
{ text: '' + (succ / (stat.last.succTime - stat.last.startTime) * 1000 | 0), color: 'green' },
|
||||
netErr ? { text: '' + netErr, color: 'red' } : '0',
|
||||
apiErr ? { text: '' + apiErr, color: 'red' } : '0'
|
||||
]
|
||||
])
|
||||
|
||||
Object.assign(stat.last, {
|
||||
sendReq: 0,
|
||||
resTime: [],
|
||||
succ: 0,
|
||||
networkError: 0,
|
||||
otherError: 0,
|
||||
startTime: Date.now(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private _logTable(rows: [TableCellItem[], TableCellItem[]]) {
|
||||
let cellWidths: number[] = [];
|
||||
for (let cell of rows[0]) {
|
||||
cellWidths.push(typeof cell === 'string' ? cell.length + 4 : cell.text.length + 4);
|
||||
}
|
||||
|
||||
for (let row of rows) {
|
||||
let line = '';
|
||||
for (let i = 0; i < row.length; ++i) {
|
||||
let cell = row[i];
|
||||
let cellWidth = cellWidths[i];
|
||||
if (typeof cell === 'string') {
|
||||
line += cell + ' '.repeat(cellWidth - cell.length);
|
||||
}
|
||||
else {
|
||||
line += cell.text[cell.color] + ' '.repeat(cellWidth - cell.text.length);
|
||||
}
|
||||
}
|
||||
console.log(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const benchmarkClient = new HttpClient(serviceProto, {
|
||||
server: benchmarkConfig.server,
|
||||
logger: {
|
||||
debug: function () { },
|
||||
log: function () { },
|
||||
warn: function () { },
|
||||
error: function () { },
|
||||
},
|
||||
timeout: benchmarkConfig.timeout,
|
||||
agent: new (benchmarkConfig.server.startsWith('https') ? https : http).Agent({
|
||||
keepAlive: true
|
||||
})
|
||||
})
|
||||
|
||||
type TableCellItem = (string | { text: string, color: 'green' | 'red' | 'yellow' });
|
||||
283
benchmark/models/WsRunner.ts
Normal file
283
benchmark/models/WsRunner.ts
Normal file
@@ -0,0 +1,283 @@
|
||||
import assert from 'assert';
|
||||
import 'colors';
|
||||
import 'k8w-extend-native';
|
||||
import { TsrpcErrorType } from "tsrpc-proto";
|
||||
import { WsClient } from '../../src/client/ws/WsClient';
|
||||
import { benchmarkConfig } from "../config/BenchmarkConfig";
|
||||
import { serviceProto } from '../protocols/proto';
|
||||
|
||||
export interface WsRunnerConfig {
|
||||
total: number;
|
||||
concurrency: number;
|
||||
showError?: boolean;
|
||||
}
|
||||
|
||||
export class WsRunner {
|
||||
|
||||
private _config: WsRunnerConfig;
|
||||
|
||||
// 执行单个事务的方法
|
||||
private _single: (this: WsRunner) => Promise<void>;
|
||||
|
||||
// 执行进度信息
|
||||
private _progress?: {
|
||||
startTime: number,
|
||||
lastSuccTime?: number,
|
||||
started: number,
|
||||
finished: number,
|
||||
succ: number,
|
||||
fail: number
|
||||
};
|
||||
|
||||
constructor(single: WsRunner['_single'], config: WsRunnerConfig) {
|
||||
this._single = single.bind(this);
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
async start() {
|
||||
this._progress = {
|
||||
startTime: Date.now(),
|
||||
started: 0,
|
||||
finished: 0,
|
||||
succ: 0,
|
||||
fail: 0
|
||||
}
|
||||
|
||||
assert.ok(await benchmarkClient.connect(), 'Connect failed');
|
||||
|
||||
// 启动并发
|
||||
for (let i = 0; i < this._config.concurrency; ++i) {
|
||||
this._doTrans();
|
||||
}
|
||||
|
||||
console.log('Benchmark start!');
|
||||
this._startReport();
|
||||
}
|
||||
|
||||
private _doTrans() {
|
||||
if (this._isStoped || !this._progress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._progress.started < this._config.total) {
|
||||
++this._progress.started;
|
||||
let startTime = Date.now();
|
||||
this._single().then(v => {
|
||||
++this._progress!.succ;
|
||||
this._progress!.lastSuccTime = Date.now();
|
||||
}).catch(e => {
|
||||
++this._progress!.fail;
|
||||
if (this._config.showError) {
|
||||
console.error('[Error]', e.message);
|
||||
}
|
||||
}).then(() => {
|
||||
++this._progress!.finished;
|
||||
if (this._progress!.finished === this._config.total) {
|
||||
this._finish();
|
||||
}
|
||||
else {
|
||||
this._doTrans();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private _reportInterval?: NodeJS.Timeout;
|
||||
private _startReport() {
|
||||
this._reportInterval = setInterval(() => {
|
||||
this._report();
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
private _isStoped = false;
|
||||
stop() {
|
||||
this._isStoped = true;
|
||||
}
|
||||
|
||||
private _finish() {
|
||||
if (!this._progress) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._reportInterval && clearInterval(this._reportInterval);
|
||||
|
||||
console.log('\n\n-------------------------------\n Benchmark finished! \n-------------------------------');
|
||||
|
||||
let usedTime = Date.now() - this._progress.startTime;
|
||||
console.log(` Transaction Execution Result `.bgBlue.white);
|
||||
console.log(`Started=${this._progress.started}, Finished=${this._progress.finished}, UsedTime=${usedTime}ms`.green);
|
||||
console.log(`Succ=${this._progress.succ}, Fail=${this._progress.fail}, TPS=${this._progress.succ / (this._progress.lastSuccTime! - this._progress.startTime) * 1000 | 0}\n`.green)
|
||||
|
||||
// TIME TPS(完成的)
|
||||
console.log(` API Execution Result `.bgBlue.white);
|
||||
|
||||
// [KEY] RPS(完成的) AVG P95 P99
|
||||
for (let key in this._apiStat) {
|
||||
let stat = this._apiStat[key];
|
||||
stat.resTime = stat.resTime.orderBy(v => v);
|
||||
|
||||
let send = stat.sendReq;
|
||||
let succ = stat.resTime.length;
|
||||
let netErr = stat.networkError;
|
||||
let apiErr = stat.otherError;
|
||||
let avg = stat.resTime[stat.resTime.length >> 1] | 0;
|
||||
let p95 = stat.resTime[stat.resTime.length * 0.95 | 0] | 0;
|
||||
let p99 = stat.resTime[stat.resTime.length * 0.99 | 0] | 0;
|
||||
|
||||
this._logTable([
|
||||
[{ text: 'Api' + key + ' '.repeat(this._maxApiNameLength - key.length), color: 'green' }, 'Send', 'Succ', 'QPS', 'NetErr', 'ApiErr', 'AVG ', 'P95 ', 'P99 '],
|
||||
['', '' + send,
|
||||
{ text: '' + succ, color: 'green' },
|
||||
{ text: '' + (succ / (stat.last.succTime - stat.startTime) * 1000 | 0), color: 'green' },
|
||||
netErr ? { text: '' + netErr, color: 'red' } : '0',
|
||||
apiErr ? { text: '' + apiErr, color: 'red' } : '0',
|
||||
{ text: avg ? avg + 'ms' : '-', color: 'yellow' },
|
||||
{ text: p95 ? p95 + 'ms' : '-', color: 'yellow' },
|
||||
{ text: p99 ? p99 + 'ms' : '-', color: 'yellow' }
|
||||
]
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
private _apiStat: {
|
||||
[key: string]: {
|
||||
sendReq: number,
|
||||
resTime: number[],
|
||||
succ: number,
|
||||
networkError: number,
|
||||
otherError: number,
|
||||
startTime: number,
|
||||
last: {
|
||||
sendReq: number,
|
||||
resTime: number[],
|
||||
succ: number,
|
||||
networkError: number,
|
||||
otherError: number,
|
||||
startTime: number,
|
||||
succTime: number
|
||||
}
|
||||
}
|
||||
} = {};
|
||||
|
||||
private _maxApiNameLength = 0;
|
||||
/**
|
||||
* callApi 并且计入统计
|
||||
*/
|
||||
callApi: typeof benchmarkClient.callApi = async (apiName, req) => {
|
||||
this._maxApiNameLength = Math.max(apiName.length, this._maxApiNameLength);
|
||||
|
||||
if (!this._apiStat[apiName]) {
|
||||
this._apiStat[apiName] = {
|
||||
sendReq: 0,
|
||||
resTime: [],
|
||||
succ: 0,
|
||||
networkError: 0,
|
||||
otherError: 0,
|
||||
startTime: Date.now(),
|
||||
last: {
|
||||
sendReq: 0,
|
||||
resTime: [],
|
||||
succ: 0,
|
||||
networkError: 0,
|
||||
otherError: 0,
|
||||
startTime: Date.now(),
|
||||
succTime: 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
++this._apiStat[apiName].sendReq;
|
||||
++this._apiStat[apiName].last.sendReq;
|
||||
|
||||
let startTime = Date.now();
|
||||
let ret = await benchmarkClient.callApi(apiName, req);
|
||||
|
||||
if (ret.isSucc) {
|
||||
this._apiStat[apiName].last.succTime = Date.now();
|
||||
this._apiStat[apiName].resTime.push(Date.now() - startTime);
|
||||
this._apiStat[apiName].last.resTime.push(Date.now() - startTime);
|
||||
++this._apiStat[apiName].succ;
|
||||
++this._apiStat[apiName].last.succ;
|
||||
}
|
||||
else {
|
||||
if (ret.err.type === TsrpcErrorType.NetworkError) {
|
||||
++this._apiStat[apiName].networkError;
|
||||
++this._apiStat[apiName].last.networkError;
|
||||
}
|
||||
else {
|
||||
++this._apiStat[apiName].otherError;
|
||||
++this._apiStat[apiName].last.otherError;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private _report() {
|
||||
console.log(new Date().format('hh:mm:ss').gray, `Started=${this._progress!.started}/${this._config.total}, Finished=${this._progress!.finished}, Succ=${this._progress!.succ.toString().green}, Fail=${this._progress!.fail.toString()[this._progress!.fail > 0 ? 'red' : 'white']}`,
|
||||
this._progress!.lastSuccTime ? `TPS=${this._progress!.succ / (this._progress!.lastSuccTime - this._progress!.startTime) * 1000 | 0}` : '')
|
||||
|
||||
for (let key in this._apiStat) {
|
||||
let stat = this._apiStat[key];
|
||||
|
||||
let send = stat.last.sendReq;
|
||||
let succ = stat.last.resTime.length;
|
||||
let netErr = stat.last.networkError;
|
||||
let apiErr = stat.last.otherError;
|
||||
|
||||
this._logTable([
|
||||
[{ text: 'Api' + key + ' '.repeat(this._maxApiNameLength - key.length), color: 'green' }, 'Send', 'Succ', 'QPS', 'NetErr', 'ApiErr'],
|
||||
['', '' + send,
|
||||
{ text: '' + succ, color: 'green' },
|
||||
{ text: '' + (succ / (stat.last.succTime - stat.last.startTime) * 1000 | 0), color: 'green' },
|
||||
netErr ? { text: '' + netErr, color: 'red' } : '0',
|
||||
apiErr ? { text: '' + apiErr, color: 'red' } : '0'
|
||||
]
|
||||
])
|
||||
|
||||
Object.assign(stat.last, {
|
||||
sendReq: 0,
|
||||
resTime: [],
|
||||
succ: 0,
|
||||
networkError: 0,
|
||||
otherError: 0,
|
||||
startTime: Date.now(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private _logTable(rows: [TableCellItem[], TableCellItem[]]) {
|
||||
let cellWidths: number[] = [];
|
||||
for (let cell of rows[0]) {
|
||||
cellWidths.push(typeof cell === 'string' ? cell.length + 4 : cell.text.length + 4);
|
||||
}
|
||||
|
||||
for (let row of rows) {
|
||||
let line = '';
|
||||
for (let i = 0; i < row.length; ++i) {
|
||||
let cell = row[i];
|
||||
let cellWidth = cellWidths[i];
|
||||
if (typeof cell === 'string') {
|
||||
line += cell + ' '.repeat(cellWidth - cell.length);
|
||||
}
|
||||
else {
|
||||
line += cell.text[cell.color] + ' '.repeat(cellWidth - cell.text.length);
|
||||
}
|
||||
}
|
||||
console.log(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const benchmarkClient = new WsClient(serviceProto, {
|
||||
server: benchmarkConfig.server,
|
||||
logger: {
|
||||
debug: function () { },
|
||||
log: function () { },
|
||||
warn: function () { },
|
||||
error: function () { },
|
||||
},
|
||||
timeout: benchmarkConfig.timeout
|
||||
})
|
||||
|
||||
type TableCellItem = (string | { text: string, color: 'green' | 'red' | 'yellow' });
|
||||
14
benchmark/protocols/PtlTest.ts
Normal file
14
benchmark/protocols/PtlTest.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { uint } from 'tsbuffer-schema';
|
||||
export interface ReqTest {
|
||||
a?: uint;
|
||||
b?: string;
|
||||
c?: boolean;
|
||||
d?: Uint8Array;
|
||||
}
|
||||
|
||||
export interface ResTest {
|
||||
a?: uint;
|
||||
b?: string;
|
||||
c?: boolean;
|
||||
d?: Uint8Array;
|
||||
}
|
||||
104
benchmark/protocols/proto.ts
Normal file
104
benchmark/protocols/proto.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { ServiceProto } from 'tsrpc-proto';
|
||||
import { ReqTest, ResTest } from './PtlTest'
|
||||
|
||||
export interface ServiceType {
|
||||
api: {
|
||||
"Test": {
|
||||
req: ReqTest,
|
||||
res: ResTest
|
||||
}
|
||||
},
|
||||
msg: {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export const serviceProto: ServiceProto<ServiceType> = {
|
||||
"services": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "Test",
|
||||
"type": "api"
|
||||
}
|
||||
],
|
||||
"types": {
|
||||
"PtlTest/ReqTest": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "a",
|
||||
"type": {
|
||||
"type": "Number",
|
||||
"scalarType": "uint"
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "b",
|
||||
"type": {
|
||||
"type": "String"
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "c",
|
||||
"type": {
|
||||
"type": "Boolean"
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "d",
|
||||
"type": {
|
||||
"type": "Buffer",
|
||||
"arrayType": "Uint8Array"
|
||||
},
|
||||
"optional": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"PtlTest/ResTest": {
|
||||
"type": "Interface",
|
||||
"properties": [
|
||||
{
|
||||
"id": 0,
|
||||
"name": "a",
|
||||
"type": {
|
||||
"type": "Number",
|
||||
"scalarType": "uint"
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "b",
|
||||
"type": {
|
||||
"type": "String"
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "c",
|
||||
"type": {
|
||||
"type": "Boolean"
|
||||
},
|
||||
"optional": true
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "d",
|
||||
"type": {
|
||||
"type": "Buffer",
|
||||
"arrayType": "Uint8Array"
|
||||
},
|
||||
"optional": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
26
benchmark/server/http.ts
Normal file
26
benchmark/server/http.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { HttpServer } from '../../src/index';
|
||||
import { serviceProto } from "../protocols/proto";
|
||||
|
||||
async function main() {
|
||||
let server = new HttpServer(serviceProto, {
|
||||
logger: {
|
||||
debug: () => { },
|
||||
log: () => { },
|
||||
error: console.error.bind(console),
|
||||
warn: console.warn.bind(console)
|
||||
}
|
||||
});
|
||||
|
||||
server.implementApi('Test', call => {
|
||||
call.succ(call.req);
|
||||
});
|
||||
|
||||
await server.start();
|
||||
|
||||
setInterval(() => {
|
||||
let used = process.memoryUsage().heapUsed / 1024 / 1024;
|
||||
console.log(`内存: ${Math.round(used * 100) / 100} MB`);
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
main();
|
||||
26
benchmark/server/ws.ts
Normal file
26
benchmark/server/ws.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { WsServer } from '../../src/index';
|
||||
import { serviceProto } from "../protocols/proto";
|
||||
|
||||
async function main() {
|
||||
let server = new WsServer(serviceProto, {
|
||||
logger: {
|
||||
debug: () => { },
|
||||
log: () => { },
|
||||
error: console.error.bind(console),
|
||||
warn: console.warn.bind(console)
|
||||
}
|
||||
});
|
||||
|
||||
server.implementApi('Test', call => {
|
||||
call.succ(call.req);
|
||||
});
|
||||
|
||||
await server.start();
|
||||
|
||||
setInterval(() => {
|
||||
let used = process.memoryUsage().heapUsed / 1024 / 1024;
|
||||
console.log(`内存: ${Math.round(used * 100) / 100} MB`);
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
main();
|
||||
63
benchmark/tsconfig.json
Normal file
63
benchmark/tsconfig.json
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
// "sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
// "outDir": "./", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
// "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
}
|
||||
}
|
||||
13
benchmark/ws.ts
Normal file
13
benchmark/ws.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { benchmarkConfig } from './config/BenchmarkConfig';
|
||||
import { WsRunner } from './models/WsRunner';
|
||||
|
||||
const req = {
|
||||
a: 123456,
|
||||
b: 'Hello, World!',
|
||||
c: true,
|
||||
d: new Uint8Array(100000)
|
||||
}
|
||||
|
||||
new WsRunner(async function () {
|
||||
await this.callApi('Test', req);
|
||||
}, benchmarkConfig).start();
|
||||
Reference in New Issue
Block a user