相信用过 Django 的同学一定会被 “Very easy to setup” 惊艳到。只要一行命令,就可以在 admin 界面看到一个完整的登陆注册。但是到了部署的时候,你一定会被网上复杂的部署教程搞的头晕,为啥本地开发这么简单,到了服务器却需要又是 Nginx,又是 uWSGI 这种东西呢?
本文试图解释这些程序在一个 Web 服务中扮演的角色,为什么部署 Python web程序需要它们。本文不介绍如何部署 Django 应用等,这一类教程网上有很多,读者可自行搜索。其中 Nginx 和 Apache 算是一类的,可以替换。Gunicorn 和 uWSGI 的角色是类似的。同理最后端的 Web 框架是一类的,比如 Django 和 Flask。本文的内容替换以上任意一个应该也适用。
如果你读了网上任意一篇教程,你应该知道一个完整的部署应该类似这样:
1 |
HTTP Server <-----> WSGI <-----> App |
最后后面的 App 我们知道,只要 runserver
就可以访问了,但是 Django 的文档明确说明:
DO NOT USE THIS SERVER IN A PRODUCTION SETTING. It has not gone through security audits or performance tests. (And that’s how it’s gonna stay. We’re in the business of making Web frameworks, not Web servers, so improving this server to be able to handle a production environment is outside the scope of Django.)
即 Django 做的事情只是一个框架,不会去关心一些安全问题、HTTP 的性能问题等。所以我们需要一个专业的 HTTP 服务器。这就出现了 Nginx 或 Apache。那么如何将 HTTP 服务器和我们的应用连接起来呢?动态网站问世的时候,就出现了 CGI 协议。注意这是一个协议,定义了HTTP 服务器如何通过后端的应用获取动态内容。可以简单的理解成 HTTP 服务器通过 CGI 协议调用后端应用吧!WSGI 可以理解成 Python 的 CGI。uWSGI 和 Gunicorn 是这种 WSGI 的一些实现。通过像 uWSGI 和 Gunicorn 的实现,Nginx 这样的通用 web 服务器就可以和 uWSGI 来沟通了。那 Django 呢?上文说到,Django 是一个 Framework,而不是一个 Web Server,Django 只是帮你去写 Web 程序的代码,真正运行的其实是 uWSGI 进程 (而并没有一个 django 进程)。就出现了上面提到的三层的部署。
但是,为什么我们还需要 Nginx 呢?这些 WSGI 程序本身不是也提供访 HTTP 问吗?
是的,uWSGI 和 Gunicorn 本身就是一个便携的 web 服务器了,但是我们一般还是在它们前面档一个更加专业的 HTTP 服务器。Nginx 作为一个经过更长时间验证的 HTTP 服务器来说,它有很多 uWSGI 没有支持的 feature。比如:
- 处理静态资源更加优秀,E-Tag 的设置,Gzip 压缩等
- 处理网络连接,降低网络负载。例如 reuqest_buffering ,假如部署一个 uWSGI 程序,如果有慢的请求存在,uWSGI 必须等待整个 HTTP 请求发过来之后才开始处理请求。但是如果前置 Nginx,那么 Nginx 会帮你收到整个 HTTP 请求之后才交给 uWSGI 处理,也就是说,uWSGI获得的永远会是完整的 HTTP 请求,不会占用一个线程在等待。(为什么慢请求不会占用 Nginx 一个线程但是会占用 uWSGI 的一个线程,这个是因为 Nginx 是基于 epoll 的异步模型,而 uWSGI 是同步模型,这里面要详细了解的话,就要去搜索和阅读很多东西了)
- 甚至缓存动态的内容,例如将博客的首页缓存 5 分钟
- 作为一个负载均衡的前置,这样每一层的实例数量可以横向扩展
- Nginx 本身是作为一个服务存在的,几乎在任何 Linux 版本上安装之后都可以用 initd 启动,就像 MySQL 那样。uWSGI 一般是需要自己将其配置成服务的。(虽然前置了 Nginx 依然是需要配置 uWSGI)
- ……
此外,这样分开的好处还是得,到达 uWSGI 和 Gunicorn 的请求的情况变得简单了很多,Nginx 处理了一层,将过滤和处理之后的请求交给 uWSGI 或 Gunicorn。这使得这些 WSGI 程序的实现简单了一些,简化了开发的工作。专业的事情交给专业的人去做。
当然,并不是所有的项目都需要这么复杂的部署,有一个可选的是将 WSGI 程序嫁接到 HTTP 服务器上,比如 Apache httpd + mod_wsgi, Nginx + mod_uwsgi 等。
参考资料:
有一点疑问: WSGI 接收的是http请求的转发, 还是满足cgi/fastcgi协议的输入
看了下文档:uWSGI可以以 HTTP 服务器的方式启动;也可以以原生的 uWSGI 协议启动,然后通过 Nginx 设置
uwsgi_pass
转发到 uWSGI 进程。(刚刚发现 uWSGI 竟然是 uWSGI 协议,应该是兼容 Python 的 WSGI 协议的吧)。这篇文章真的解决了我查了好久资料都没有解决的疑问,很多资料只告诉你这么配置(我刚实习,公司的工程就是这么配置的,但是很不理解),博主的其他文章也值得我去好好学习一阵子,谢谢(≧▽≦)
不客气,加油。
在一个 Web 服务中扮演的角色?
Pingback: 四层负载均衡漫谈 | 卡瓦邦噶!