如何部署一个高性能mock服务
前言
在阅读本文之前,请先了解mock服务相关知识,及python的web框架相关知识。
本文描述的方案不一定是最正确的,仅以抛砖引玉。
web框架选型
web框架的选型,主要遵循一个条件:性能要好。
在找了很多技术博客和贴子之后,无意间看到了腾讯云社区的一篇文章,给予本人很大的启发。
文章链接:https://cloud.tencent.com/developer/article/1684200。
于是,就想要试一试这个框架是不是真的能达到单机100万qps。
此为个人测试的脚本,模仿第三方微信的api接口服务:
mock_japronto.py
# -*- coding: utf-8 -*-
"""
__author__: @hope_dong
__datetime__: 2021/9/29
"""
import multiprocessing
import random
import time
import uuid
from japronto import Application
def generate_random_code(n):
"""
随机生成6-10位含大小写字母、数字字符
:param n:int,支持6-10位字符
"""
random_str = ""
for i in range(n):
num = random.randint(0, 9)
letter = chr(random.randint(97, 122)) # 取小写字母
Letter = chr(random.randint(65, 90)) # 取大写字母
random_str += str(random.choice([num, letter, Letter]))
return random_str
def get_timestamp(unit=None):
""" 根据指定的单位获取时间戳
Args:
unit (str): 默认: 原始数据 s:秒级时间戳 ms:毫秒级时间戳 us:微秒级时间戳
Returns:
str: datetime string
"""
time_now_stamp = time.time()
# 原始时间数据
if not unit:
return time_now_stamp
# 秒级时间戳
if unit == "s":
return int(time_now_stamp)
# 毫秒级时间戳
if unit == "ms":
return int(round(time_now_stamp * 1000))
# 微秒级时间戳
if unit == "us":
return int(round(time_now_stamp * 1000000))
def sns_oauth2_access_token(request):
"""
mock https://api.weixin.qq.com/sns/oauth2/access_token
"""
access_token = generate_random_code(20)
refresh_token = generate_random_code(20)
openid = uuid.uuid1().hex
json_response = {
"access_token": access_token,
"expires_in": 7200,
"refresh_token": refresh_token,
"openid": openid,
"scope": "SCOPE",
"unionid": "%s" % (uuid.uuid1())
}
return request.Response(json=json_response)
def sns_oauth2_component_access_token(request):
"""
mock https://api.weixin.qq.com/sns/oauth2/component/access_token
"""
access_token = generate_random_code(20)
refresh_token = generate_random_code(20)
openid = uuid.uuid1().hex
json_response = {
"access_token": access_token,
"expires_in": 7200,
"refresh_token": refresh_token,
"openid": openid,
"scope": "SCOPE",
"unionid": "%s" % (uuid.uuid1())
}
return request.Response(json=json_response)
def sns_userinfo(request):
"""
mock https://api.weixin.qq.com/sns/userinfo
"""
openid = uuid.uuid1().hex
json_response = {
"openid": openid,
"nickname": "我是昵称3333",
"sex": 1,
"province": "海南省",
"city": "三亚市",
"country": "中国",
"headimgurl": "https://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
"privilege": ["PRIVILEGE1", "PRIVILEGE2", "PRIVILEGE3"],
"unionid": "%s" % (uuid.uuid1())
}
return request.Response(json=json_response)
if __name__ == '__main__':
app = Application()
app.router.add_route('/sns/oauth2/access_token', sns_oauth2_access_token)
app.router.add_route('/sns/oauth2/component/access_token', sns_oauth2_component_access_token)
app.router.add_route('/sns/userinfo', sns_userinfo)
app.run(port=9999, worker_num=multiprocessing.cpu_count())
将此脚本放到服务器(8核CPU 16GB内存配置)上运行:python37 mock_japronto.py
运行状态如下:
[root@VM-0-186-centos ~]# python37 mock_japronto.py
Accepting connections on http://0.0.0.0:9999
Accepting connections on http://0.0.0.0:9999
Accepting connections on http://0.0.0.0:9999
Accepting connections on http://0.0.0.0:9999
Accepting connections on http://0.0.0.0:9999
Accepting connections on http://0.0.0.0:9999
Accepting connections on http://0.0.0.0:9999
Accepting connections on http://0.0.0.0:9999
在另一台机器上进行压测,使用的压测工具是wrk,安装步骤如下:
git clone https://gitee.com/mirrors/wrk.git
cd wrk
make
进行测试:
./wrk -t800 -c1600 -d60 https://api.weixin.qq.com/sns/oauth2/access_token
最终测试结果是1.6
万左右的qps
,本人测试的时候两台服务器之间通讯使用的公网ip
,若使用内网,性能应该会更高。
此性能已经达到了项目需求,于是决定使用此框架: japronto
。
解决https认证问题
在上一步做性能测试的时候,用的是https的请求,直接使用命令python37 mock_japronto.py
启动起来是http的服务,怎么办呢?
此时,使用nginx完成ssl认证,再利用nginx的反向代理功能即可完成需求。
nginx配置ssl认证
1. 使用配置文件生成证书
配置文件mock.conf
:
[req]
default_bits = 2048
default_keyfile = mock.key
encrypt_key = no
utf8 = yes
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
C = US
ST = Cary
L = Cary
O = BigCompany
CN = api.weixin.qq.com
[v3_req]
keyUsage = critical, digitalSignature, keyAgreement, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = api.weixin.qq.com
DNS.2 = *.qq.com
根据配置文件进行证书签署:
openssl req -x509 -sha256 -nodes -days 3650 -newkey rsa:2048 -keyout app.key -out app.crt -config mock.conf
2. nginx配置ssl证书
/etc/nginx/nginx.conf
server {
listen 443 http2 ssl;
listen [::]:443 http2 ssl;
server_name 159.75.16.195;
root /usr/share/nginx/html;
ssl_certificate /root/app.crt;
ssl_certificate_key /root/app.key;
# ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
# ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
# ssl_certificate /etc/ssl/self-signed/app.crt;
# ssl_certificate_key /etc/ssl/self-signed/app.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
proxy_pass http://127.0.0.1:9999;
}
这2个参数是配置的证书和key:
- ssl_certificate /root/app.crt;
- ssl_certificate_key /root/app.key;
这个参数配置的是反向代理到mock服务:
- proxy_pass http://127.0.0.1:9999;
重启nginx:
systemctl restart nginx
3. 在请求机器上,通过curl获取合法的pem(要改域名)
echo quit | openssl s_client -showcerts -servername api.weixin.qq.com -connect api.weixin.qq.com:443 > cacert.pem
4. 将pem追加写入到ca-bundle里面
cat cacert.pem >> /etc/pki/tls/certs/ca-bundle.crt
如果网络仍然无法访问,可执行如下步骤
centos6 & centos7 安装crt信任证书
update-ca-trust force-enable
rm -rf /etc/pki/ca-trust/source/anchors/app.crt
cp /root/app.crt /etc/pki/ca-trust/source/anchors/app.crt
update-ca-trust extract
本地测试mock服务:
curl https://api.weixin.qq.com
文末探讨
japronto
这个框架到底能不能达到单机100万qps?
这里有几个条件:
1、单机配置 32核CPU 64GB内存
2、使用内网测试
3、使用http请求
基于这3个条件,本人测试了一下,使用 16核CPU 32GB内存腾讯云主机,使用内网进行请求,协议使用http,结果最大请求量可以达到35万qps
。
有条件的同学可以自行测试一下。