uWSGI python高负载配置

2024-10-06 12:35:38 发布

您现在位置:Python中文网/ 问答频道 /正文

我们有一个32核的大型EC2实例,目前运行Nginx、Tornado和Redis,平均每秒服务5K请求。一切看起来都很好,但是CPU负载已经达到70%,我们必须支持更多的请求。其中一个想法是用uWSGI代替Tornado,因为我们并没有真正使用Tornado的异步特性。

我们的应用程序由一个函数组成,它接收一个JSON(~=4KB),执行一些阻塞但非常快的操作(Redis)并返回JSON。

  • 对Tornado实例之一(Nginx)的代理HTTP请求
  • 解析HTTP请求(Tornado)
  • 读取POST body string(stringified JSON)并将其转换为python字典(Tornado)
  • 从同一台机器上的Redis(阻塞)中取出数据(py Redis和hiredis)
  • 处理数据(python3.4)
  • 在同一台计算机上更新Redis(py Redis with hiredis)
  • 准备响应的字符串化JSON(python3.4)
  • 向代理发送响应(龙卷风)
  • 向客户端发送响应(Nginx)

我们认为速度的提高将来自于uwsgi协议,我们可以在单独的服务器上安装Nginx,并使用uwsgi协议将所有请求代理到uwsgi。但在尝试了所有可能的配置和更改操作系统参数之后,我们仍然无法让它在当前负载下工作。 大部分时间nginx日志包含499和502个错误。在某些配置中,它只是停止接收新的请求,就像它达到了一些操作系统限制一样。

如我所说,我们有32核,60GB的空闲内存和非常快速的网络。我们不做繁重的工作,只做很快的封锁行动。在这种情况下,什么是最好的策略?进程、线程、异步?应该设置哪些操作系统参数?

当前配置为:

[uwsgi]
master = 2
processes = 100
socket = /tmp/uwsgi.sock
wsgi-file = app.py
daemonize = /dev/null
pidfile = /tmp/uwsgi.pid
listen = 64000
stats = /tmp/stats.socket
cpu-affinity = 1
max-fd = 20000
memory-report = 1
gevent = 1000
thunder-lock = 1
threads = 100
post-buffering = 1

Nginx配置:

user www-data;
worker_processes 10;
pid /run/nginx.pid;

events {
    worker_connections 1024;
    multi_accept on;
    use epoll;
}

操作系统配置:

sysctl net.core.somaxconn
net.core.somaxconn = 64000

我知道极限太高了,开始尝试所有可能的价值。

更新

我最终得到了以下配置:

[uwsgi]
chdir = %d
master = 1
processes = %k
socket = /tmp/%c.sock
wsgi-file = app.py
lazy-apps = 1
touch-chain-reload = %dreload
virtualenv = %d.env
daemonize = /dev/null
pidfile = /tmp/%c.pid
listen = 40000
stats = /tmp/stats-%c.socket
cpu-affinity = 1
max-fd = 200000
memory-report = 1
post-buffering = 1
threads = 2

Tags: 数据实例pyredisjsonhttp代理stats
1条回答
网友
1楼 · 发布于 2024-10-06 12:35:38

我认为你的请求处理大致如下:

  • HTTP解析、请求路由、JSON解析
  • 执行一些产生redis请求的python代码
  • (阻塞)redis请求
  • 执行一些处理redis响应的python代码
  • JSON序列化,HTTP响应序列化

您可以在接近空闲的系统上对处理时间进行基准测试。我的预感是往返行程会缩短到2或3毫秒。在70%的CPU负载下,这将增加到大约4或5毫秒(不包括在nginx请求队列中花费的时间,只包括在uWSGI worker中的处理)。

在5k请求/秒时,进程中的平均请求可能在20。。。25范围。一个像样的匹配你的虚拟机。

下一步是平衡CPU核心。如果您有32个核心,那么分配1000个工作进程是没有意义的。您可能会在上下文切换开销上阻塞系统。一个好的平衡将使工作线程总数(nginx+uWSGI+redis)按可用CPU内核的数量顺序排列,可能会有一些额外的工作线程来覆盖阻塞I/O(即文件系统,但主要是对DBMS等其他主机执行的网络请求)。如果阻塞I/O成为等式的一个重要部分,请考虑重写为异步代码并集成异步堆栈。

第一个观察:你给nginx分配了10个工人。然而,nginx在请求上花费的CPU时间比uWSGI在请求上花费的时间要少得多。首先,我要将系统的10%用于nginx(3或4个工作进程)。

剩下的部分必须在uWSGI和redis之间分配。我不知道redis中索引的大小,也不知道python代码的复杂性,但我的第一次尝试是在uWSGI和redis之间进行75%/25%的分割。这将使redis和uWSGI分别有6名和20名工人和一名师傅。

至于uwsgi配置中的threads选项:线程切换比进程切换轻,但是如果python代码的很大一部分是CPU绑定的,那么它不会因为GIL而运行。如果处理时间的很大一部分被I/O阻塞,Threads选项就非常有趣。您可以禁用线程,或者尝试将workers=10、threads=2作为初始尝试。

相关问题 更多 >