[译] 经过优化 Gunicorn 配置提升性能

关于如何配置 Gunicorn 的实用建议html

概要,对于 CPU 受限的应用应该提高集群数量或者核心数量。但对于 I/O 受限的应用应该使用“伪线程”。前端

Gunicorn 是一个 Python 的 WSGI HTTP 服务器。它所在的位置一般是在反向代理(如 Nginx)或者 负载均衡(如 AWS ELB)和一个 web 应用(好比 Django 或者 Flask)之间。python

Gunicorn 架构

Gunicorn 实现了一个 UNIX 的预分发 web 服务端。android

好的,那这是什么意思呢?ios

  • Gunicorn 启动了被分发到的一个主线程,而后所以产生的子线程就是对应的 worker。
  • 主进程的做用是确保 worker 数量与设置中定义的数量相同。所以若是任何一个 worker 挂掉,主线程均可以经过分发它自身而另行启动。
  • worker 的角色是处理 HTTP 请求。
  • 这个 in 预分发 就意味着主线程在处理 HTTP 请求以前就建立了 worker。
  • 操做系统的内核就负责处理 worker 进程之间的负载均衡。

为了提升使用 Gunicorn 时的性能,咱们必须牢记 3 种并发方式。nginx

第一种并发方式(workers 模式,又名 UNIX 进程模式)

每一个 worker 都是一个加载 Python 应用程序的 UNIX 进程。worker 之间没有共享内存。git

建议的 workers 数量(2*CPU)+1github

对于一个双核(两个CPU)机器,5 就是建议的 worker 数量。web

gunicorn --workers=5 main:app
复制代码

Gunicorn 使用默认的 worker 模式(同步模式)。注意看这个图片的第四行:“Using worker: sync”.

第二种并发方式(多线程)

Gunicorn 还容许每一个 worker 拥有多个线程。在这种场景下,Python 应用程序每一个 worker 都会加载一次,同一个 worker 生成的每一个线程共享相同的内存空间。编程

为了在 Gunicorn 中使用多线程。咱们使用了 threads 模式。每一次咱们使用 threads 模式,worker 的类就会是 gthread

gunicorn --workers=5 --threads=2 main:app
复制代码

Gunicorn 的多线程模式就是使用了 worker 的 gthread 类。请注意图片中的第四行 “Using worker: threads”。

上一条命令等同于:

gunicorn --workers=5 --threads=2 --worker-class=gthread main:app
复制代码

在咱们的例子里面最大的并发请求数就是 worker * 线程,也就是10。

在使用 worker 和多线程模式时建议的最大并发数量仍然是(2*CPU)+1

所以若是咱们使用四核(4 个 CPU)机器而且咱们想使用 workers 和多线程模式,咱们可使用 3 个 worker 和 3 个线程来获得最大为 9 的并发请求数量。

gunicorn --workers=3 --threads=3 main:app
复制代码

第三种并发方式(“伪线程”)

有一些 Python 库好比(geventAsyncio)能够在 Python 中启用多并发。那是基于协程实现的“伪线程”。

Gunicrn 容许经过设置对应的 worker 类来使用这些异步 Python 库。

这里的设置适用于咱们想要在单核机器上运行的gevent

gunicorn --worker-class=gevent --worker-connections=1000 --workers=3 main:app
复制代码

worker-connections 是对于 gevent worker 类的特殊设置。

(2*CPU)+1 仍然是建议的workers 数量。由于咱们仅有一核,咱们将会使用 3 个worker。

在这种状况下,最大的并发请求数量是 3000。(3 个 worker * 1000 个链接/worker)

并发 vs. 并行

  • 并发是指同时执行 2 个或更多任务,这可能意味着其中只有一个正在处理,而其余的处于暂停状态。
  • 并行是指两个或多个任务正在同时执行。

在 Python 中,线程和伪线程都是并发的一种方式,但并非并行的。可是 workers 是一系列基于并发或者并行的方式。

理论讲的很不错,但我应该怎样在程序中使用呢?

实际案例

经过调整Gunicorn设置,咱们但愿优化应用程序性能。

  1. 若是这个应用是 I/O 受限,一般能够经过使用“伪线程”(gevent 或 asyncio)来获得最佳性能。正如咱们了解到的,Gunicorn 经过设置合适的 worker 类 并将 workers数量调整到 (2*CPU)+1 来支持这种编程范式。
  2. 若是这个应用是 CPU 受限,那么应用程序处理多少并发请求就并不重要。惟一重要的是并行请求的数量。由于 Python’s GIL,线程和“伪线程”并不能以并行模式执行。实现并行性的惟一方法是增长**workers** 的数量到建议的 (2*CPU)+1,理解到最大的并行请求数量其实就是核心数。
  3. 若是不肯定应用程序的内存占用,使用 多线程 以及相应的 gthread worker 类 会产生更好的性能,由于应用程序会在每一个 worker 上都加载一次,而且在同一个 worker 上运行的每一个线程都会共享一些内存,但这须要一些额外的 CPU 消耗。
  4. 若是你不知道你本身应该选择什么就从最简单的配置开始,就只是 workers 数量设置为 (2*CPU)+1 而且不用考虑 多线程。从这个点开始,就是全部测试和错误的基准环境。若是瓶颈在内存上,就开始引入多线程。若是瓶颈在 I/O 上,就考虑使用不一样的 Python 编程范式。若是瓶颈在 CPU 上,就考虑添加更多内核而且调整 workers 数量。

构建系统

咱们软件开发人员一般认为每一个性能瓶颈均可以经过优化应用程序代码来解决,但并不是老是如此。

有时候调整 HTTP 服务器的设置,使用更多资源或经过别的编程范式从新设计应用程序都是咱们提高应用程序性能的解决方案。

在这种状况下,构建系统意味着理解咱们应该灵活应用部署高性能应用程序的计算资源类型(进程,线程和“伪线程”)。

经过使用正确的理解,架构和实施正确的技术解决方案,咱们能够避免陷入尝试经过优化应用程序代码来提升性能的陷阱。

参考

  1. Gunicorn 是从 Ruby 的 Unicorn 项目移植而来。它的设计大纲有助于澄清一些最基本的概念。Gunicorn 架构 进一步巩固了其中一些概念。
  2. 有态度的博文报道关于 Unicorn 怎么讲一些关键的特性基于 Unix 表述的很是好。
  3. Stack Overflow里有关预分发 Web 服务模型的回答。
  4. 一些更多参考来理解怎么微调 Gunicorn。

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索