• ​如何部署一个高性能mock服务


    如何部署一个高性能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服务:

    重启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

    有条件的同学可以自行测试一下。

    不积跬步,无以至千里。
  • 相关阅读:
    分布式事务之可靠消息
    分布式事务之本地消息表
    分布式事务
    数据库之 事务
    WePY开发小程序(二):项目入口及注册页面、组件
    WePY开发小程序(一):入门
    vue学习笔记-事件监听
    vue学习笔记-列表渲染
    vue学习笔记-缩写
    vue学习笔记-常用指令
  • 原文地址:https://www.cnblogs.com/DeaconOne/p/15546647.html
Copyright © 2020-2023  润新知