使用Docker验证uwsgi的雷电锁定设置


构建应用服务器时存在一个小众问题。那就是雷电群问题。在这里,我想研究通过在docker环境中启用uwsgi的雷电锁定设置可以改善多少此问题。

什么是雷电群问题?

雷击牧群问题,也称为Zeeg问题,是在多进程和多线程中处理请求时发生的问题。如果一个请求到达并且所有准备好的线程都被唤醒,则资源将被浪费。因为最后通常是一个线程处理一个请求,所以其他线程才被唤醒,确保没有任务,然后再次进入睡眠状态。在当前内核中,可以使同一进程中的线程保持等待锁,以防止唤醒同一进程中的所有线程。但是,如果在进程数量很大时不锁定进程,则会再次发生争用和浪费。

我会验证

我公司的应用服务器是使用nginx uwsgi Django构建的,因此我想验证nginx uwsgi配置的问题。另外,由于我通常在本地环境中玩耍,因此这次我将使用docker构建环境。
验证计划:
答:环境构造(不要输入uwsgi的雷电锁定设置)用k6加载uwsgitop的性能测量
B:环境构造(插入uwsgi的雷锁设置)加载k6使用uwsgitop

进行性能测量

环境建设

首先,使用docker-compose构建一个docker环境,在工作目录中准备以下yml文件

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
version: '2'

services:
  web:
    image: tiangolo/uwsgi-nginx:python2.7
    ports:
      - "30301:80"
    networks:
      - "mynet"
    volumes:
      - ./workingD:/var/workingD
networks:
  mynet:

接下来,通过拉docker-compose up命令docker的映像,启动名为web的服务,该服务包含设置的nginx uwsgi。
服务启动后(您可以看到uwsgi日志已打印),使用以下命令输入docker
<br>
docker exec -it dockusginginx_web_1 /bin/bash<br>

现在,您可以转到该目录并更改nginx和uwsgi设置。

nginx环境设置(/etc/nginx/conf.d/nginx.conf)

可以使用默认设置。

1
2
3
4
5
6
root@1c30155afc5e:/etc/nginx/conf.d# cat nginx.conf
server {
    location / {
        include uwsgi_params;
        uwsgi_pass unix:///tmp/uwsgi.sock;
    }

uwsgi环境设置(/etc/uwsgi/uwsgi.ini)

我将多线程设置为4,并将进程数设置为16。我认为这个人(更多过程)将更容易看到雷鸣般的畜群问题。

1
2
3
4
5
6
[uwsgi]
socket = /tmp/uwsgi.sock
chown-socket = nginx:nginx
chmod-socket = 664
processes = 16
threads = 4

uwsgi应用程序设置

在这里,我设置了统计信息的输出目标,以便uwsgitop可以读取性能日志。
/var/workingD/stats.sock是从本地环境挂载的目录(因为即使在docker中指定了文件也无法写入)。当更改为验证计划B时,在此处输入thunder-lock = true以完成更改。

1
2
3
4
[uwsgi]
wsgi-file=/app/main.py
stats = /var/workingD/stats.sock
memory-report = true

应用逻辑

在uwsgi上运行的应用程序的内容仅返回Hello World,但是它太轻了,因此我将其留给CPU绑定的作业。

1
2
3
4
5
6
7
8
9
10
11
12
13
def application(env, start_response):
    workload()
    start_response('200 OK', [('Content-Type', 'text/html')])
    return ["NEWWWWWWW Hello World from a default Nginx uWSGI Python 2.7 app in a\
            Docker container (default)"]

def workload():
    """
    provide CPU pound workload
    """
    counter = 1000000
    while counter > 0:
        counter -= 1

关于uwsgitop

uwsgitop是用于观察uwsgi性能的工具。

,因为它是python包

1
pip install uwsgitop

您可以通过在docker中输入

进行安装。

关于k6

这次我用k6加载,来自python的蝗虫很好,但是我想尝试一个新的,所以k6吗?
要使用k6,首先准备一个脚本

loadtest.js

1
2
3
4
5
import http from "k6/http";

export default function(){
    http.get("http://localhost:30301/");
};

在这里,我们定义default函数作为k6的条目。您所要做的就是向docker打开并监听的端口发送一个get请求。
要开始负载测试时,可以在loadtest.js所在的目录中输入以下命令。
<br>
k6 run --vus 64 --duration 0 loadtest.js<br>

在此,让64个虚拟用户(伪用户)执行同时测试脚本。如果duration指定为0,则测试将继续直到停止。

检查结果

验证计划A:
当我运行k6约90秒并获取测量结果时:

uwsgitop

planA.png

k6

planAk6.png

验证计划B:
同样,运行k6约90秒的测量结果:

uwsgitop

planB.png

k6

planBk6.png

结果分析

注意:由于本地环境是4个核心,因此在docker上运行的uwsgi和k6与实际生产环境完全不同。
uwsgitop:计划A的RPS(每秒请求数)在各个进程之间差异很大,有两个RPS为0的进程。 (但这只是瞬时结果的屏幕截图)
似乎没有其他大差异。
k6指标:
http_req_blocked:tcp直到连接初始化的时间
http_req_connecting:是时候与远程主机建立连接了
方案B的上述两项的平均值和最大值较大。
http_req_duration:请求处理时间(不包括块等)。
http_req_waiting:返回请求的第一个响应的时间("到第一个字节的时间")
两项之间的平均值没有太大差异,但Plan B的最大值较小。

比较多个指标,带有雷锁的计划B不能赢得没有计划A的计划。但是,存在诸如响应时间的异常值较小以及uwsgi进程之间处理的请求数量变化较小的优点。