ocust 官网: www.locust.io/
安装 locust
pip install locust
$ python3 -V Python 3.6.9 $ pip -V pip 20.1.1 from /home/w/.local/lib/python3.6/site-packages/pip (python 3.6) $ pip install locust ... $ locust -V locust 1.0.3
测试脚本
locustfile.py
import random from locust import HttpUser, task, between, constant class QuickstartUser(HttpUser): # wait_time = constant(0.01) wait_time = between(5, 9) @task def get_users(self): print("get_users") name = "get_users" url = "/api/v1/users_manage" params = {} headers = {} res = self.client.get(url=url, headers=headers, params=params, verify=False, name=name) @task def get_user_info(self): print("get_user_info") name = "get_user_info" url = "/api/v1/user_info" params = {} headers = {} self.client.get(url=url, headers=headers, params=params, verify=False, name=name) def on_start(self): # response = self.client.post("/api/v1/login", {"phone": "13912345678", "password": "12345678"}) print("start")
运行测试脚本
运行 locustfile.py 测试脚本文件
locust
或
locust -f locustfile.py
没有 webui 界面执行压测
locust -f locustfile.py --headless -u 100 -r 10 --run-time 5m --host http://192.168.199.172:3000 --stop-timeout 99
--headless
直接开始测试而不使用Web界面
-u
要产生的用户数
-r
每秒添加的用户数
--run-time
测试运行时间
--host
IP地址
--stop-timeout
测试运行时间完成后,延迟99s再关闭整个任务
服务端
运行 Node 服务,接口返回静态数据
node server.js
server.js
let express = require('express') let app = express() let bodyParser = require('body-parser') let fs = require('fs') let multer = require('multer') app.use(express.static('public')) app.use(bodyParser.urlencoded({ extended: false })) app.use(multer({ dest: '/tmp/' }).array('file')) let cors = require('cors') app.use(cors()) // get / app.get('/', (req, res) => { res.sendFile(__dirname + '/index.html') }) // get users app.get('/api/v1/users_manage', async (req, res) => { let data = [ { user_id: 1, username: 'admin', password: '12345678', nickname: 'admin user' }, { user_id: 2, username: 'aaa', password: '12341234', nickname: 'aaa user' } ] console.log('1') await new Promise((resolve, reject) => { setTimeout(() => { console.log('2s .') resolve() }, 2000) }) res.end(JSON.stringify(data)) }) // get users app.get('/api/v1/user_info', async (req, res) => { let data = { user_id: 1, username: 'admin', password: '12345678', nickname: 'admin user' } console.log('3') await new Promise((resolve, reject) => { setTimeout(() => { console.log('3s .') resolve() }, 3000) }) res.end(JSON.stringify(data)) }) app.post('/api/v1/login', (req, res) => { let data = { status: 1 } res.end(JSON.stringify(data)) }) // post 403 app.post('/post_403', (req, res) => { res.status(403).send({ error: 'Permission denied' }); }) // post test app.post('/post_test', (req, res) => { console.log('post') let data = { 'post_data': '123' } console.log('123') // res.end(JSON.stringify(data)) res.end('123') }) // put file_upload app.put('/file_upload_put', (req, res) => { console.log('file_upload_put') let des_file = __dirname + '/public/images/' + req.files[0].originalname console.log('des_file: ', des_file) fs.readFile(req.files[0].path, (err, data) => { fs.writeFile(des_file, data, (err) => { if (err) { console.log(err) } else { response = { message: 'File Upload Success!', filename: req.files[0].originalname } } res.end(JSON.stringify(response)) }) }) }) // post file_upload app.post('/file_upload', (req, res) => { console.log('file') // console.log(JSON.stringify(req)) console.log(req.body) let file = req.files[0] let size = file.size // size limit 10MB if (size > 10240000) { res.end('File beyond size limit !') return false } // let mimetypeFlag = 0 // let mimetypeArray = ['image/bmp', 'image/gif', 'image/jpeg', 'image/svg+xml', 'image/tiff', 'image/png'] // let mimetype = file.mimetype // for (let i = 0; i < mimetypeArray.length; i++) { // if (mimetype === mimetypeArray[i]) { // mimetypeFlag = 1 // } // } // if (mimetypeFlag === 0) { // res.end('Documents must be image format !') // return false // } let des_file = __dirname + '/public/images/' + req.files[0].originalname fs.readFile(req.files[0].path, (err, data) => { fs.writeFile(des_file, data, (err) => { if (err) { console.log(err) } else { response = { message: 'File Upload Success!', filename: req.files[0].originalname } } res.end(JSON.stringify(response)) }) }) }) let server = require('http').createServer(app) server.listen(3000, (req, res) => { let host = server.address().address let port = server.address().port console.log('server is running . http://%s:%s', host, port) })
package.json
{ "name": "fileserver", "version": "1.0.0", "description": "File server project", "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "gyw", "license": "ISC", "dependencies": { "body-parser": "^1.18.2", "cookie-parser": "^1.4.3", "cors": "^2.8.5", "express": "^4.16.2", "express-session": "^1.15.6", "multer": "^1.3.0", "request": "^2.83.0", "serve-index": "^1.9.1", "serve-static": "^1.13.1", "socket.io": "^2.0.3" } }
指标分析
施压端(测试机)
RPS: 吞吐量, 每秒钟系统可以处理的请求数、任务数
RPS(Requests Per Second) = 并发数 / 响应时间
注意
使用 locust 测试过程中,在压力机性能瓶颈范围内可以通过调整用户数和脚本 wait_time 参数来测试 RPS 值,超过压力机性能范围后 RPS 数值无法继续变大
被测端(服务端)
TPS(Transactions Per Second)事务数/秒。一个事务是指一个客户机向服务器发送请求然后服务器做出反应的过程。客户机在发送请求时开始计时,收到服务器响应后结束计时,以此来计算使用的时间和完成的事务个数
并发数 = RPS * 响应时间
参考链接: