python 使用Nginx和uWSGI来运行Python应用

参考:http://zmrenwu.com/post/20/

uWSGI 是一个 Web 应用服务器,它具有应用服务器,代理,进程管理及应用监控等功能。它支持 WSGI 协议,同时它也支持自有的 uWSGI 协议,该协议据说性能非常高,而且内存占用率低,为 mod_wsgi 的一半左右,我没有实测过。它还支持多应用的管理及应用的性能监控。虽然 uWSGI 本身就可以直接用来当 Web 服务器,但一般建议将其作为应用服务器配合 Nginx 一起使用,这样可以更好的发挥 Nginx 在 Web 端的强大功能。本文我们就来介绍如何搭建 uWSGI+Ngnix 环境来运行 Python 应用。

安装 uWSGI

pip install uwsgi

让我们来写个 Hello World 的 WSGI 应用,并保存在”server.py”文件中:

def application(environ, start_response):
    status = '200 OK'
    output = 'Hello World!'
response_headers </span>= [(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Content-type</span><span style="color: rgba(128, 0, 0, 1)">'</span>, <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">text/plain</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">),
                    (</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Content-Length</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">, str(len(output)))]
start_response(status, response_headers)

</span><span style="color: rgba(0, 0, 255, 1)">return</span> [output]</pre>

让我们在 uWSGI 中运行它,执行命令:

uwsgi --http :9090 --wsgi-file server.py

然后打开浏览器,访问”http://localhost:9090″,你就可以看到”Hello World!”字样了。

上面的命令中”- -http”参数指定了 HTTP 监听地址和端口,”- -wsgi-file”参数指定了 WSGI 应用程序入口,uWSGI 会自动搜寻名为”application”的应用对象并调用它。

更进一步,uWSGI 可以支持多进程和多线程的方式启动应用,也可以监控应用的运行状态。我们将启动的命令改为:

$ uwsgi --http :9090 --wsgi-file server.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191

执行它后,uWSGI 将启动 4 个应用进程,每个进程有 2 个线程,和一个 master 主进程(监控其他进程状态,如果有进程死了,则重启)。同时,你可以访问”127.0.0.1:9191″来获取 JSON 格式的应用运行信息,uWSGI 还提供了工具命令”uwsgitop”来像 top 一样监控应用运行状态,你可以用 pip 来安装它。

上面的命令参数太多了, 我们可以将参数写在配置文件里,启动 uWSGI 时指定配置文件即可。配置文件可以是键值对的格式,也可以是 XML,YAML 格式,这里我们使用键值对的格式。让我们创建一个配置文件”myapp.ini”:

[uwsgi]
http=:9090
wsgi-file=server.py
master=true
processes=4
threads=2
stats=127.0.0.1:9191

然后就可以将启动命令简化为:

$ uwsgi myapp.ini

配置 Nginx

Nginx 的安装可以参考这篇文章,文本采用的环境就是 Ubuntu Linux,对于其他系统如 Mac,基本上差不太多

首先,我们将 uWSGI 的 HTTP 端口监听改为 socket 端口监听,即将配置文件中的”http”项去掉,改为”socket”项:

[uwsgi]
socket=127.0.0.1:3031
wsgi-file=server.py
master=true
processes=4
threads=2
stats=127.0.0.1:9191

然后,打开 Nginx 的配置文件,Ubuntu 上默认是”/etc/nginx/sites-enabled/default”文件,将其中的根路径部分配置为:

location / {
    include uwsgi_params;
    uwsgi_pass 127.0.0.1:3031;
}

这段配置表明 Nginx 会将收到的所有请求都转发到”127.0.0.1:3031″端口上,即 uWSGI 服务器上。现在让我们重启 Nginx,并启动 uWSGI 服务器:

$ sudo service nginx restart
$ uwsgi myapp.ini

访问”http://localhost”,我们会再次看到”Hello World!”。

运行 Flask 应用

其实很简单,只要将上例中 server.py 的内容改为 Flask 应用即可,当然你需要先把 Flask 包安装好:

from flask import Flask
application = Flask(__name__)

@application.route('/')
def index():
return '<h1>Hello World</h1>'

很多人习惯将 Flask 应用对象取名为”app”,但是 WSGI 标准是”application”。uWSGI 提供了一个功能,可以指定应用对象,方法就是在配置文件中加上”callable”项:

[uwsgi]
...
callable=app

现在,我们的 Flask 应用就可以使用”app”作为对象名了

from flask import Flask
app = Flask(__name__)

@app.route('/')
def index():
return '<h1>Hello World</h1>'

使用 Python 虚拟环境

我们永远都是建议采用虚拟环境来避免应用间冲突,uWSGI 可以非常容易做到这点,就是在配置文件中加上”virtualenv”项:

[uwsgi]
...
virtualenv=/home/bjhee/virtualenv

部署多个应用

 一个 Nginx 中,可以同时运行多个应用,不一定是 Python 的应用。我们期望通过不同的路径来路由不同的应用,因此就不能像上例那样直接修改根目录的配置。假设我们希望通过”http://localhost/myapp”来访问我们的应用,首先要在 Nginx 的配置文件中,加入下面的内容:

location /myapp {
    include uwsgi_params;
    uwsgi_param SCRIPT_NAME /myapp;
    uwsgi_pass 127.0.0.1:3031;
}

这里我们定义了一个 uWSGI 参数”SCRIPT_NAME”,值为应用的路径”/myapp”。接下来,在 uWSGI 的启动配置中,去掉”wsgi-file”项,并加上:

[uwsgi]
...
mount=/myapp=server.py
manage-script-name=true

“mount”参数表示将”/myapp”地址路由到”server.py”中,”manage-script-name”参数表示启用之前在 Nginx 里配置的”SCRIPT_NAME”参数。再次重启 Nginx 和 uWSGI,你就可以通过”http://localhost/myapp”来访问应用了。

补充内容

上面的所有例子中,我们是通过”127.0.0.1:3031″Socket 端口来连接 Nginx 和 uWSGI 的,其实我们也可以采用 socket 文件的方式,这样可以不用写死端口。在 uWSGI 的启动配置中,我们要修改”socket”项:

[uwsgi]
socket=/tmp/uwsgi.sock
...

启动 uWSGI 服务器后,它会自动创建一个”/tmp/uwsgi.sock”文件。然后让我们修改 Nginx 配置文件,将”uwsgi_pass”配置项改为文件:

location /myapp {
    include uwsgi_params;
    uwsgi_param SCRIPT_NAME /myapp;
    uwsgi_pass unix:/tmp/uwsgi.sock;
}

重启 Nginx 服务器即可。这里我开始一直没跑通,研究了好久才发现,Nginx 的用户(默认的 www-date:adm),必须要对该文件有读写的权限才行。因为运行 uWSGI 的用户与运行 Nginx 的用户不一样,而”/tmp/uwsgi.sock”是由 uWSGI 的用户创建的,导致 Nginx 没有足够的权限。如果朋友们也遇到同样的问题,那就只能 chmod 了。

另外,还是要提一下”.egg”包的解压缩临时目录,我们在介绍 mod_wsgi的最后提到过,在 uWSGI 应用中也一样,Linux 上默认是在用户主目录下,比如”/home/bjhee/.python-eggs”。你可以通过设置系统环境变量”PYTHON_EGG_CACHE”来改变它。