官方文档源地址:https://uwsgi-docs.readthedocs.io/en/latest/WSGIquickstart.html
目录
安装
部署一个简单的WSGI应用示例
为你的WSGI应用添加并发(concurrency)与监控(monitoring)
与web服务器,如nginx,协同使用(putting behind a full webserver)
自动开启uWSGI
部署到django上
从命令行选项到ini配置文件
部署Flask
部署web2py
关于https请求
关于python线程的说明
Virtualenvs
安全性和可用性(Security and availability)
常见问题之“卡住请求”
卸载
多个python版本共用相同的uWSGI服务器
uWSGI是指一个Application server ,uwsgi是指uWSGI最支持的一种规范或者说是协议。
该文档讲述了怎么部署简单的wsgi应用和web框架。这篇新手文档的版本需要uWSGI>= 1.4.并安装python支持的uWSGI。
注:官方建议新入手的小白使用官方来源的包构建,熟悉后再使用发行版中提供的那些软件包。
uWSGI是一个C应用程序,所以需要先安装好C编译器(例如gcc和clang)和python开发头文件。(python开发头文件:我猜应该是python-dev这个软件包)例如:基于Debian发行版本需要安装的环境,执行
apt-get install build-essential python-dev
对python来说,uWSGI的安装方式很多:
1. 通过pip
pip install uwsgi
2. 通过网络安装
curl http://uwsgi.it/install | bash -s default /tmp/uwsgi
这种方式会在/tmp/uwsgi路径下安装二进制版本,并且可以随意改变它。
3. 通过下载源并编译它
wget https://projects.unbit.it/downloads/uwsgi-latest.tar.gz
tar zxvf uwsgi-latest.tar.gz
cd
make
这种方式构建完成后,uwsgi二进制文件会存在于当前路径下
在使用发行版提供的软件包时,你可能需要考虑的一件事,你的发行版很可能以模块化方式构建了uWSGI(每个功能都是必须加载的不同插件)。所以,你必须先安装好--plugin python,http插件,用来保证文章中的例子能正常运行。
1. 新建文件foobar.py(文件名可自取),添加如下函数:
def application(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
return [b"Hello World"]
如你所见,就是一个名为application的函数,uWSGI Python loader会默认查找并加载此函数。
2. 部署到http的9090端口
开启uWSGI,运行HTTP server转发请求到你的WSGI应用
uwsgi --http :9090 --wsgi-file foobar.py
注:如果当前目录不再foobar.py路径中,则需要将命令中文件的路径也加上
注:当你有一个前端Webserver,或者你正在做某种形式的基准测试时,不要使用--http,使用--http-socket。
默认情况下uWSGI只会开启一个进程和一个线程。
你可以用--processes选项和--threads选项添加更多的进程和线程。
uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2
以上命令将会产生一个主进程,4个工作进程,和http路由。主进程会控制4个工作进程结束时再重新生成新的进程。每个工作进程下有2个线程。
监测。stats子系统允许你导出uWSGI的内部统计信息,格式为json。
uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
(我运行--stats选项成功后,用浏览器访问9191端口,返回的是拒绝访问。查看了原文档写的是:telnet to the port 9191,猜的可能是浏览器是http访问,而不是telnet)
你也可以使用uwsgitop工具,来监控实例。uwsgitop可用pip安装。
注:应该绑定stats socket到私有地址,否则每个人都能获取到uwsgi的状态信息。
虽然uWSGI的http router具有耐用,高性能的特点。但是你可能还是想要和功能齐全的webserver搭配使用。
uWSGI本身支持http,FastCGI,SCGI和uwsgi协议。性能最佳的是uwsgi协议,它已经可以支持nginx和Cherokee(Apache的各种模块的都可以用)
在nginx配置中使用uwsgi,打开nignx的配置文件,nginx.conf,修改如下:
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:3031;
}
此配置会转发所有请求到3031端口,3031端口为uwsgi协议。
注:nginx.conf中只有这一个location时,才会将所有请求转发到3031端口。我在nginx新手指引那篇文章中建立过两个locaton,其中第一个修改了转发到3031端口,第二个保留没动,如下:
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:3031;
}
location ~\.(gif|jpg|png)$ {
root /data/images;
}
所以当我访问 http://我的公网IP/ ,确实请求转发到了3031端口,但是当我访问http:// 我的公网IP /1.jpg,浏览器直接显示了我的静态图片。所以,nginx直接响应了请求,并返回本地文件夹中的静态资源。
注:修改完配置需要执行nginx –s reload ,使配置生效。
然后执行如下命令,启动uWSGI并选择uwsgi协议
uwsgi --socket 127.0.0.1:3031 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
注:我运行的命令中没有加 --stats选项,因为加上了访问时报错nginx error 50x(以下命令中我都没加--stats选项)
我运行的是:
uwsgi --socket 127.0.0.1:3031 --wsgi-file /data/uwtest/foobar.py --master --processes 4 --threads 2
然后,打开浏览器,访问你的公网IP,则成功弹出foobar.py中的hello world。
如果你运行ps aux命令,你将看到少了一个进程,这是因为nginx分配给uWSGI的工作进程本来就是uwsgi协议。
如果你的代理/Web服务器/路由使用的是http协议,那么你必须设置uWSGI也使用http协议,使用如下命令:
uwsgi --http-socket 127.0.0.1:3031 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
注:--http-socket和—http不同,--http是自己生成代理。
Django基本上是使用最多的web框架了。部署它相当简单。
假设我们的django项目在/home/foobar/myproject,执行
uwsgi --socket 127.0.0.1:3031 --chdir /home/foobar/myproject/ --wsgi-file myproject/wsgi.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
成功后,我们用浏览器访问http://公网IP /,则可以看到django页面的小火箭了。
上面这个命令是用--chdir选项来改变文件路径。在django中此选项为必填项,这样才能加载正确的模块。
但是上面的命令太长了,所以uWSGI支持各种风格的配置文件。这里使用.ini文件来示例吧。文件内容如下(这是最简单的版本的吧):
[uwsgi]
socket = 127.0.0.1:3031
chdir = /home/foobar/myproject/
wsgi-file = myproject/wsgi.py
processes = 4
threads = 2
stats = 127.0.0.1:9191
然后,运行这个配置文件
uwsgi yourfile.ini
如果你的django版本<1.4,那么你的django项目目录下(/home/foobar/myproject/myproject/wsgi.py)是没有wsgi.py文件的。那么你需要执行如下命令:
uwsgi --socket 127.0.0.1:3031 --chdir /home/foobar/myproject/ --pythonpath .. --env DJANGO_SETTINGS_MODULE=myproject.settings --module "django.core.handlers.wsgi:WSGIHandler()" --processes 4 --threads 2 --stats 127.0.0.1:9191
转变成ini文件为:
[uwsgi]
socket = 127.0.0.1:3031
chdir = /home/foobar/myproject/
pythonpath = ..
env = DJANGO_SETTINGS_MODULE=myproject.settings
module = django.core.handlers.wsgi:WSGIHandler()
processes = 4
threads = 2
stats = 127.0.0.1:9191
Flask是比较流行的python轻量级Web框架。
保存如下示例内容,myflaskapp.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "I am app 1"
Flask会导入它的WSGI函数(即,以application为函数名的函数)作为app。执行如下命令:
uwsgi --socket 127.0.0.1:3031 --wsgi-file myflaskapp.py --callable app --processes 4 --threads 2 --stats 127.0.0.1:9191
也是一个很流行的选择。解压web2py源文件,新建uWSGI配置文件,内容如下:
[uwsgi]
http = :9090
chdir = path_to_web2py
module = wsgihandler
master = true
processes = 8
注:当前最近的web2py版本,你可能需要复制wsgihandler.py脚本到handlers文件夹外。
该配置仍然使用的是http router。用浏览器访问9090端口,可以看到web2py的欢迎页面。
点击管理界面,你会发现不起作用,因为它需要https。因此,你需要安装OpenSSL开发头文件,然后再重新构建uWSGI,构建系统则将会自动探测到https请求了。
步骤如下:
首先,生成自己的密钥和证书
openssl genrsa -out foobar.key 2048
openssl req -new -key foobar.key -out foobar.csr
openssl x509 -req -days 365 -in foobar.csr -signkey foobar.key -out foobar.crt
然后,会有三个文件,foobar.csr, foobar.key and foobar.crt。修改uWSGI配置文件如下:
[uwsgi]
https = :9090,foobar.crt,foobar.key
chdir = path_to_web2py
module = wsgihandler
master = true
processes = 8
重新运行uWSGI,并使用https协议连接9090端口。
如果你启动uWSI服务器时不使用线程,那么python GIL也不会启动,所以你的应用线程也不会运行。你可能不太喜欢这个设置,但是uWSGI是语言独立的服务器,所以它大多数的选择都会保持”不可知”。
但是对于开发人员来说,基本上所有的uWSGI配置都可以通过选项来设置。
如果你想要在不启动应用程序的多个线程的情况下维护Python线程支持,只需添加--enable-threads选项(或ini样式的enable-threads = true)。
可以通过添加virtualenv =
为了避免用root用户身份运行uWSGI实例,你可以使用uid和gid选项来删除权限。配置如下:
[uwsgi]
https = :9090,foobar.crt,foobar.key
uid = foo
gid = bar
chdir = path_to_web2py
module = wsgihandler
master = true
processes = 8
如果需要绑定到特权端口(例如HTTPS用的443端口号),那么使用共享套接字shared-socket。它们是在删除权限之前创建的,可以使用= N语法引用,其中N是套接字号(从0开始):
[uwsgi]
shared-socket = :443
https = =0,foobar.crt,foobar.key
uid = foo
gid = bar
chdir = path_to_web2py
module = wsgihandler
master = true
processes = 8
Web应用程序部署的一个常见问题是“卡住请求”。 这时所有线程/工作线程都会被卡住(请求被阻止),应用不能接受更多请求。 为了避免这个问题,你可以设置harakiri计时器。 它是一个监视器(由主进程管理),它将摧毁超过指定秒数的进程。 示例如下,关闭超过30秒的进程:
[uwsgi]
shared-socket = :443
https = =0,foobar.crt,foobar.key
uid = foo
gid = bar
chdir = path_to_web2py
module = wsgihandler
master = true
processes = 8
harakiri = 30
除此之外,自uWSGI1.9版本之后,stats服务器会导出整个请求变量,所以你可以及时查看你的uWSGI实例在做什么。
uWSGI卸载子系统允许在某些特定模式匹配时尽快释放工作线程,并且可以委派给pure-c线程。 例如,从文件系统发送静态文件,将数据从网络传输到客户端等等。
卸载非常复杂,但它的使用对最终用户是透明的。 如果你想尝试卸载,可以添加--offload-threads
当卸载线程被允许执行时,所有可以被优化的部分都会被自动检测到。
正如我们所见,uWSGI由一个小内核和各种插件组成。插件能够在二进制中嵌入,或者动态的加载。当你为python构建uWSGI时,一系列的插件和python嵌入到二进制文件中。
那么,这会带来一个问题,如果你想很多版本python,但是又不想为每一个版本构建一个二进制文件,怎么办?
最好的方式是将语言独立(languang-independent)的各项配置构建成二进制文件,然后对每个版本的python制作成插件,当需要时再加载。
我们从同一个目录下开始构建python插件
PYTHON=python3.4 ./uwsgi --build-plugin "plugins/python python34"
PYTHON=python2.7 ./uwsgi --build-plugin "plugins/python python27"
PYTHON=python2.6 ./uwsgi --build-plugin "plugins/python python26"
你将会以python34_plugin.so, python27_plugin.so, python26_plugin.so,这三个文件结束。复制这三个文件到你的目标路径。(默认情况下,uWSGI会再当前工作目录下搜索插件)
现在,在你的配置文件中你可以在最顶上添加插件路径和插件指令了。
[uwsgi]
plugins-dir =
plugin = python26
然后它会从目录中加载python26_plugin.so插件库到你复制的插件中。