Tornado与WSGI(通过Gunicorn)的对比

32

我读到 这篇文章 关于 Tornado:

另一方面,如果你已经有了一个 WSGI 应用程序并想要在一个非常快的 tornado.httpserver.HTTPServer 上运行它,请使用 tornado.wsgi.WSGIContainer 将其包装起来。但是你需要小心。由于你的原始应用程序没有为异步服务器做好准备,并且会执行大量的 IO/计算操作,因此在生成响应时它将阻塞其他请求(进一步的请求将被接受和缓冲,但排队等待后续处理)。

而 Gunicorn 则说:

'Gunicorn 是一个 Python WSGI HTTP 服务器,适用于 UNIX。它是从 Ruby 的 Unicorn 项目移植过来的预先 fork 工作模型。

  1. Gunicorn 将生成工作进程来处理请求吗?
  2. 每个工作进程只能同时处理一个请求吗?
  3. 而 Tornado 将使用 epoll 或 kqueue 在一个进程中执行(没有主/工作进程)?
  4. 如果我在处理函数的 get/post 函数中使用阻塞调用(例如 requests.get),这将阻塞所有的请求处理还是只阻塞当前正在处理的请求?
1. 是的,Gunicorn 会生成工作进程来处理请求。 2. 每个工作进程只能同时处理一个请求。 3. 而 Tornado 将使用 epoll 或 kqueue 在一个进程中执行,没有主/工作进程。 4. 如果您在处理函数的 get/post 函数中使用阻塞调用(例如 requests.get),那么它将阻塞当前请求处理,但不会阻塞其他请求的处理。
1个回答

53
如果您正在使用 Tornado 上层运行 WSGI 应用程序,那么在特定进程处理 WSGI 请求时,Tornado 和 gunicorn 并没有太大的区别。对于 gunicorn,由于只有一个线程处理请求,因此只能在应用程序中处理 WSGI 请求时,不会发生任何其他事情。对于 Tornado,则是因为主事件循环永远不会在此期间运行,以处理任何并发请求。
但是,在 Tornado 的情况下,实际上还存在潜在危险。对于 gunicorn,由于只有一个请求线程,所以工作进程一次只能接受一个 Web 请求。如果有并发请求击中服务器,则它们将由任何其他可用的工作进程处理,因为它们都共享同一个侦听器套接字。
然而,在 Tornado 中,Wsgi 应用程序下层的异步性质意味着同一进程可能会同时接受多个请求。它们最初会交错,因为读取请求标头和内容时,Tornado 会预先将其读入内存,然后调用 WSGI 应用程序。当整个请求内容已被读取后,控制权将移交给 WSGI 应用程序来处理该请求。在此期间,由于尚未读取请求标头和内容,因此由同一进程处理的并发请求将被阻塞,直到 WSGI 应用程序处理第一个请求所需的时间为止。
现在,如果您只有一个 Tornado 进程,则这并不是什么大问题,因为请求将被序列化。但是,如果您使用 gunicorn 的 Tornado worker 模式,以便启用多个 Tornado 工作进程共享相同的侦听器套接字,则情况可能会很糟糕。这是因为个别进程由于异步层的贪婪本质而导致请求被阻塞,而另一个工作进程可以处理它。
总而言之,对于单个 Tornado Web 服务器进程,您只能一次处理一个请求。在 gunicorn 中,您可以拥有多个工作进程以允许并发处理请求。但是,在 Tornado 中使用多进程设置会导致请求被阻塞的风险。所以Tornado适用于非常小的自定义WSGI应用程序,它们没有太多的操作,因此响应非常快。但是,在阻塞的WSGI应用程序下运行长时间请求时,会出现问题。 Gunicorn会更好,因为它具有处理并发请求的正确能力。虽然Gunicorn只有单线程,并且需要多个工作进程,但它将使用更多的内存。
因此,它们都有取舍,在某些情况下,使用支持通过多线程和多个工作进程提供并发性能的WSGI服务器可能更好。这样可以处理并发请求,而不需要许多工作进程来消耗内存。同时,您需要在每个进程中平衡线程数量和使用多个进程,以避免在CPU密集型应用程序中受到GIL影响。
具有多线程功能的WSGI服务器选择包括mod_wsgi、uWSGI和waitress。对于waitress,您仅限于一个工作进程。
总的来说,最好的WSGI服务器实际上取决于您网站应用程序的具体情况。没有一个WSGI服务器是在所有方面都最好的。

2
因为第一个工作进程已经接受了套接字连接并开始从中读取数据,所以此时另一个进程无法接管该连接。这类似于曾经为嵌入到nginx中的WSGI模块所描述的问题,如http://blog.dscpl.com.au/2009/05/blocking-requests-and-nginx-version-of.html所述。 - Graham Dumpleton
3
关于神话般的C10K问题,将nginx放置在gunicorn之前,并使用WSGI应用程序并不能很好地解决这个问题。gunicorn可以处理的并发请求数量仍然受限于内存中可以容纳多少进程,同时需要权衡的是,如果有许多工作进程超过处理器核心数,则有时会因Python的GIL在多处理器系统中的工作方式而对性能产生不利影响。 - Graham Dumpleton
5
对于大多数网站来说,C10k实际上不是一个实用的基准或目标,因为你通常不可能达到这个水平,即使你做到了,在单台机器上使用典型的Python web应用程序试图达到这个目标通常也是荒谬的。如果你的应用程序有特定的要求需要保持长时间连接,那么将其分段并编写一个专门的异步应用程序。不要仅仅因为一个部分需要异步而感染整个应用程序。然后让nginx分别路由到WSGI和异步应用程序。 - Graham Dumpleton
2
这个答案最好的地方是评论将C10K问题放在了透彻的视角中。至少现在我知道旋风和独角兽都不应该用来处理那么多的流量。 - Realistic
3
@Realistic - 只有在 Tornado 中使用WSGI时才是正确的。正如Graham Dumpleton所解释的那样:“只要特定进程中的应用程序正在处理WSGI请求,就不会发生任何其他事情”。这对于WSGI接口是正确的。但是,如果您使用本机的Tornado请求处理程序(例如没有Gunicorn),则您不仅限于单个请求。然后,您可以使用await语句或通过从请求处理程序中生成Future实例来在请求之间切换。您还可以使用Nginx将请求分发到多个Tornado进程之间(而不是Gunicorn的主进程)。 - nagylzs
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接