试图找出 uWSGI thread/workers 配置

Trying to figure out uWSGI thread/workers configuration

所以,两天前我开始为我的 python 应用程序使用 uWSGI,我试图理解我们在 .ini 文件中指定的各种参数。这是我的 app.ini 文件目前的样子:

# The following article was referenced while creating this configuration
# https://www.techatbloomberg.com/blog/configuring-uwsgi-production-deployment/
[uwsgi]
strict = true                          ; Only valid uWSGI options are tolerated
master = true                          ; The master uWSGI process is necessary to gracefully re-spawn and pre-fork workers,
                                       ; consolidate logs, and manage many other features
enable-threads = true                  ; To run uWSGI in multithreading mode
vacuum = true                          ; Delete sockets during shutdown
single-interpreter = true              ; Sets only one service per worker process
die-on-term = true                     ; Shutdown when receiving SIGTERM (default is respawn)
need-app = true

;disable-logging = true                 ; By default, uWSGI has rather verbose logging. Ensure that your
;log-4xx = true                         ; application emits concise and meaningful logs. Uncomment these lines
;log-5xx = true                         ; if you want to disable logging

cheaper-algo = busyness
processes = 128                      ; Maximum number of workers allowed
cheaper = 1                          ; Minimum number of workers allowed - default 1
cheaper-initial = 2                  ; Workers created at startup
cheaper-overload = 60                ; Will check busyness every 60 seconds.
cheaper-step = 3                     ; How many workers to spawn at a time

auto-procname = true                 ; Identify the workers
procname-prefix = "rhs-svc "         ; Note the space. uWSGI logs will be prefixed with "rhs-svc"

当我开始时 uWSGI - 这是我看到的:

[uWSGI] getting INI configuration from app.ini
*** Starting uWSGI 2.0.19.1 (64bit) on [Thu Sep 30 10:49:45 2021] ***
compiled with version: Apple LLVM 12.0.0 (clang-1200.0.32.29) on 29 September 2021 23:55:27
os: Darwin-19.6.0 Darwin Kernel Version 19.6.0: Thu Sep 16 20:58:47 PDT 2021; root:xnu-6153.141.40.1~1/RELEASE_X86_64
nodename: sth-sth-sth
machine: x86_64
clock source: unix
pcre jit disabled
detected number of CPU cores: 12
current working directory: /Users/sth.sth/My-Microservice
detected binary path: /Users/sth.sth/My-Microservice/venv/bin/uwsgi
your processes number limit is 2784
your memory page size is 4096 bytes
detected max file descriptor number: 10240
lock engine: OSX spinlocks
thunder lock: disabled (you can enable it with --thunder-lock)
uWSGI http bound on :9000 fd 4
[busyness] settings: min=25%, max=50%, overload=60, multiplier=10, respawn penalty=2
uwsgi socket 0 bound to TCP address 127.0.0.1:57164 (port auto-assigned) fd 3
Python version: 3.9.6 (default, Jun 29 2021, 06:20:32)  [Clang 12.0.0 (clang-1200.0.32.29)]
Python main interpreter initialized at 0x7fd32b905bf0
python threads support enabled
your server socket listen backlog is limited to 100 connections
your mercy for graceful operations on workers is 60 seconds
mapped 9403584 bytes (9183 KB) for 128 cores
*** Operational MODE: preforking ***
WSGI app 0 (mountpoint='') ready in 0 seconds on interpreter 0x7fd32b905bf0 pid: 78422 (default app)
spawned uWSGI master process (pid: 78422)
spawned uWSGI worker 1 (pid: 78423, cores: 1)
spawned uWSGI worker 2 (pid: 78424, cores: 1)
spawned uWSGI http 1 (pid: 78425)

我 运行 在 MacOS Catalina 上使用 6 核 i7 CPU。为什么我只有 6 却显示 detected cores: 12?它说 process number limit: 2784 - 我真的可以将 processes = 128 设置为 processes = 2784 吗? In the docs,有人提到 processes = 2 * cpucores 是一个太简单的指标,无法遵守。理想情况下,我应该考虑哪些指标?我的应用程序目前是一个 shell(即没有业务逻辑 - 现在只是在内存中获取值设置值的东西,我实际上是在构建一个模板)并且我们不期望任何 DB connections/IO 现在密集操作。我如何确定什么是好的 thread:process 比率?如果我的问题太基础,我深表歉意,但我对此很陌生

内核与处理器

首先,核心数不一定就是处理器数。在早期的计算机时代,它就像 1-1,但随着现代改进,一个处理器可以提供多个内核。 (检查这个:https://www.tomshardware.com/news/cpu-core-definition,37658.html)。因此,如果它检测到 12 个内核,您可以将其用作微积分的基础。

WSGI 进程

进程数 表示该服务器中将有多少个不同的 Web 应用程序并行实例 运行。 WSGI 首先创建一个主进程来协调事情。然后它会引导您的应用程序并创建它的 N 个克隆(fork)。这些子分支进程是隔离的,它们不共享资源。如果一个进程由于任何原因变得不健康(例如 I/O 问题),它可以终止甚至被主进程自愿杀死,而其余的克隆继续工作,所以你的应用程序仍然在运行并且 运行.当一个进程 terminated/killed 时,主进程可以创建另一个新的克隆来替换它(重新生成)。

可以将进程数设置为可用核心的比率。但是增加太多没有任何好处。所以,你绝对不应该将它设置为极限 (2784)。请记住,操作系统将在所有进程之间循环,让每个进程都有机会处理一些指令。因此,如果它提供 12 个内核并且您创建了 1000 个不同的进程,那么您只是在给系统施加压力,您最终将获得相同的吞吐量(甚至更差的吞吐量,因为有太多的混乱)。

一个进程中的线程数

然后我们继续线程数。为了简单起见,我们只说线程数是指每个子进程可以处理的并行请求数。当一个线程正在等待数据库响应来回答请求时,另一个线程可能正在做其他事情来回答另一个请求。

你可能会说:如果我已经有多个进程,为什么还需要多个线程?

一个进程是一个昂贵的东西,但线程只是并行处理一个进程可以处理的工作负载的方式。想象一个进程是一个咖啡店,而线程是你里面服务员的数量。您可以在城市周围分布 10 个不同的咖啡店单元。如果其中一家关门了,还有另外 9 家客户可以去其他地方。但是每家店都需要一定数量的服务员来尽可能地为人们服务。

如何正确设置这些数字

如果您只设置一个具有 100 个线程的进程,这意味着 100 是您的并发限制。如果在某个时候对您的应用程序有 101 个并发请求,那么最后一个请求将不得不等待前 100 个请求中的一个完成。那是您开始获得一些用户的响应时间增加的时候。请求排队的越多,情况就越糟(排队论)。

除此之外,由于您只有一个进程,如果它崩溃,所有这 100 个请求都将失败并出现服务器错误 (500)。因此,拥有更多进程更为明智,假设 4 个进程每个处理 25 个线程。您仍然有 100 个并发限制,但您的应用程序更具弹性。

您需要了解您的应用程序预期负载,以便您可以适当地调整这些数字。当您有数据库等外部集成时,您还必须考虑它的局限性。假设一个 PostgreSQL 服务器可以同时处理 100 个连接。如果您有 10 个 WSGI 进程,每个进程有 40 个线程(还有一个大小为 40 的连接池),那么您可能会用 400 个连接对数据库造成压力,然后您就会遇到大问题,但那不是您的情况!

因此,只需使用建议的进程数 (12 * 2 = 24) 并根据需要设置尽可能多的线程以提供特定的所需并发级别。

如果您不知道预期的负载,我建议您进行某种性能测试,可以模拟对您的应用程序的请求,然后您可以试验不同的负载和设置并检查它的副作用。

额外:容器

如果您 运行 您的应用程序在容器编排平台(如 Kubernetes)中,那么您可能有多个平衡容器服务于同一个应用程序。您甚至可以使其动态化,以便在内存或处理超出阈值时增加容​​器的数量。这意味着除了针对单个服务器的所有这些 WSGI 微调之外,还有其他现代配置层可以帮助您应对高峰和高负载场景。