The uWSGI project¶
uWSGI 项目致力于为构建一个全栈式的托管服务。
应用服务器(多种编程语言和协议),代理,进程管理器和监视器 全部都以通用 api 和通用配置风格实现了。
得益于它的可插式架构,它可以被拓展到其他更多的平台和语言。
目前你可以使用 C,C++ 和 Objective-C 来写插件。
名字中的 ”WSGI“ 部分是对 Python 标准中的同名一个东西的致敬,因为它 是这个项目的第一个开发的插件。
多功能,高性能,占用资源少和可靠性是这个项目的优势(也是唯一遵循的规则)。
包含的组件(更新到了最新的稳定发行版)¶
核心 Core (实现了配置,进程管理,创建 socket,监控,日志,共享内存,进程间通信, 集群成员和 uWSGI Subscription Server )
请求插件 Request plugins (实现了多种语言和平台的应用服务器接口: WSGI,PSGI,Rack,Lua WSAPI,CGI,PHP,Go ...)
网关 Gateways (实现了负载均衡,代理和路由器)
The Emperor (实现了对大量实例的管理和监控)
循环引擎 Loop engines (实现了事件和并发,组件可以以 preforking,threaded,asynchronous/evented 和 green thread/coroutine 模式运行。支持包括 uGreen,Greenlet,Stackless 多种技术, Gevent , Coro::AnyEvent, Tornado, Goroutines 和 Fibers)
注解
uWSGI 是一个发布周期非常快的活跃项目。所以代码和文档并不总是同步的。 我们尽最大的努力来保证文档的质量,但这很难。请原谅。 如果你遇到了麻烦,邮件列表是解决与 uWSGI 有关问题的最佳地方。 欢迎为文档(以及代码)贡献。
快速入门¶
Python/WSGI 应用快速入门¶
这个快速入门指南将会向你展示如何部署简单的 WSGI 应用和普通 web 框架。
Python 在这里特指 CPython,如果你想用 PyPy 你需要使用专门的插件: The PyPy plugin, Jython 的支持正在开发中。
注解
为了完成这个快速入门确保你的 uWSGI 的版本在 1.4 以上。任何旧的东西 都不会再维护并且使用它们是非常危险的。
安装带 Python 支持的 uWSGI¶
小技巧
当你开始学习 uWSGI 的时候,尝试从官方源代码构建:使用发行版提供的包可能会 让你非常头疼。当事情变得明朗一点的时候,你可以使用模块化构建(就像在你的发行版中提供的一样)。
uWSGI 是一个(巨大的) C 应用,所以你需要一个 C 编译器(比如 gcc 或者 clang)和 Python 开发版头文件。
在 Debian 系的发行版上一条
apt-get install build-essential python-dev
命令就够了。
你有多种方式来安装 uWSGI 的 Python 包:
使用 pip
pip install uwsgi
使用网络安装
curl http://uwsgi.it/install | bash -s default /tmp/uwsgi
(这将会把 uWSGI 二进制文件安装到
/tmp/uwsgi
下,你可以随意修改它)。通过下载源代码然后 make 安装
wget http://projects.unbit.it/downloads/uwsgi-latest.tar.gz tar zxvf uwsgi-latest.tar.gz cd <dir> make
(make 完后你会在你的当前目录下得到一个
uwsig
的二进制文件)。
通过你的发行版的包管理器安装是不能面面俱到的(不可能让所有人都开心),但是一般的规则都适用。
当你使用发行版提供的包来测试这个快速入门的时候,一件你可能想重视的事情就是很有可能
你的发行版是用模块化的方式构建的(每个特性都是一个不同的必须被加载的插件)。
为了完成这个快速入门,你必须在前面第一个例子的前面加上 --plugin python,http
选项,
以及当 HTTP 路由被移除时加上 --plugin python
选项(这可能对你没什么用,继续阅读就好)。
第一个 WSGI 应用¶
让我们从一个简单的 “Hello World” 例子开始吧(这是在 Python 2.x 中,Python 3.x 需要 返回字节字符串,看下面):
def application(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
return ["Hello World"]
(保存为 foobar.py
)。
正如你看到的,它由一个单独的 Python 函数组成。它的名字是 “application”,这是 默认的函数名,uWSGI 的 Python 加载器将会搜索这个名字(但你当然可以修改它)。
Python 3.x 版本如下:
def application(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
return [b"Hello World"]
把它部署到 HTTP 端口 9090¶
现在运行 uWSGI 来启动一个会把请求传递给你的 WSGI 应用的 HTTP 服务器/路由器。
uwsgi --http :9090 --wsgi-file foobar.py
这就是全部了。
注解
当你有前端 web 服务器时不要使用 –http 选项,使用 –http-socket。继续阅读快速入门来理解为什么要这么做。
添加并发和监控¶
你想做的第一件事可能就是增加并发(uWSGI 默认启动一个单独的进程和一个单独的线程)。
你可以通过 --processes
选项或者 --threads
(或者两个选项都使用)来增加更多的进程或者线程。
uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2
这将会产生 4 个进程(每个进程 2 个线程),一个主进程(当你的进程死掉时会重新 spawn 一个新的)以及 HTTP 路由器(见前面)。
一个重要的任何就是监控。知道发生了什么在生产环境中是极其重要的。stats 子系统允许你 用 JSON 输出 uWSGI 的内部数据:
uwsgi --http :9090 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
向你的应用发送几个请求然后 telnet 到 9191 端口,你将得到大量有趣的信息。你可能想要使用
“uwsgitop” (使用 pip install
你就能得到它),这是一个类似 top 的工具,用于监控应用实例。
注意
将 stats 套接字(socket)绑定到私有地址(除非你知道你在做什么),否则任何人都可以访问到它!
放到一个完整的 web 服务器后¶
即使 uWSGI HTTP 路由器(router)是一个可靠的高性能服务器,你可能还是想把你的应用放到一完整的 web 服务器后。
uWSGI 通常和 HTTP,FastCGI,SCGI 以及它自己特有的协议 “uwsgi” (呃,名字不应该这么取的) 通信。 性能最高的协议显然是 uwsgi,并且早已被 nginx 和 Cherokee 支持 (同时 Apache 也有许多可用的模块)。
一个普通的 nginx 配置如下:
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:3031;
}
这个意思是说 “把每个请求传递到服务器绑定的端口 3031,并且使用 uwsgi 协议通信”。
现在我们可以 spawn 一个 uWSGI 进程来天然地以 uwsgi 协议通信:
uwsgi --socket 127.0.0.1:3031 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
如果你运行 ps aux
,你将会看到少了一个进程。HTTP 路由器(router)已经从我们的 “workers” (分配给 uWSGI 的进程)
中被移除了,这些 worker 便是天然地用来以 uwsgi 协议形式通信的。
如果你的代理/web 服务器/路由器使用 HTTP 协议,你必须告诉 uWSGI 使用 HTTP 协议(这与通过 –http spawn 一个它自己的代理是不一样的):
uwsgi --http-socket 127.0.0.1:3031 --wsgi-file foobar.py --master --processes 4 --threads 2 --stats 127.0.0.1:9191
开机自启动 uWSGI¶
如果你打算打开 vi 写一个 init.d 脚本来启动 uWSGI,坐下来冷静一下然后先确保 你的系统没有提供一个更好(更现代化)的方式。
没一个发行版会选择一个启动系统 (Upstart, Systemd...),除此之外也有许多 进程管理工具(supervisord, god, monit, circus...)。
uWSGI 与上面列出的那些工具都集成得很好(我们希望如此),但是如果你想部署大量应用的话, 看看 uWSGI 的 Emperor - 它或多或少是每个开发运维工程师的梦想。
部署 Django¶
Django 可能是使用得最多的 Python web 框架了。部署它非常简单(我们仍然使用 4 个进程,2 个线程的配置)。
假定你的 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
(通过 --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
如果 /home/foobar/myproject/myproject/wsgi.py
(或者其他你的项目的名字) 这个文件不存在,你很有可能
使用的是老的版本的 Django (1.4 以下)。在这种情况下你需要配置更多一点的东西:
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
老版(1.4 以下)的 Django 发行版需要设置 evn
, module
和 pythonpath
(..
使得我们可以访问
myproject.settings
模块)。
部署 Flask¶
Flask 是一个流行的 Python web 微框架。
保存下面这个例子到 myflaskapp.py
:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "<span style='color:red'>I am app 1</span>"
Flask 把它的 WSGI 函数(就是我们在之前快速入门里称作 “application” 即应用的东西)暴露成 “app”, 所以 我们需要告诉 uWSGI 去使用它。 我们仍然使用 4 个进程/2 个线程,以及 uwsgi socket :
uwsgi --socket 127.0.0.1:3031 --wsgi-file myflaskapp.py --callable app --processes 4 --threads 2 --stats 127.0.0.1:9191
(唯一增加的选项便是 --callable
选项)。
部署 web2py¶
又是一个流行的选择。你可以选择把 web2py 的发行版源代码解压到一个目录然后写一个 uWSGI 配置文件:
[uwsgi]
http = :9090
chdir = path_to_web2py
module = wsgihandler
master = true
processes = 8
注解
On recent web2py releases you may need to copy the wsgihandler.py
script out of the handlers
directory.
我们再次使用 HTTP 路由器(router)。用你的浏览器访问 9090 端口然后你就可以看到 web2py 的欢迎页面了。
点击管理页面然后...哎呀,它需要 HTTPS。不要担心,uWSGI 路由器(router)可支持 HTTPS (确保你 有 OpenSSL 开发版的头文件:安装它们然后重新构建 uWSGI,build 系统会自动检测到它)。
First of all generate your key and certificate: 首先生成你的秘钥(key)和证书(certificate):
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
和 foobar.crt
。修改 uWSGI 配置:
[uwsgi]
https = :9090,foobar.crt,foobar.key
chdir = path_to_web2py
module = wsgihandler
master = true
processes = 8
重新运行 uWSGI 然后使用 https://
用你的浏览器连接到 9090 端口。
Python 线程小贴士¶
如果你没有使用线程启动 uWSGI,Python 的 GIL 将不会被开启,所以你的应用产生的线程 将永远不会运行。你可能不会喜欢这个选择,但是记住 uWSGI 是一个语言无关的服务器,所以它的 大部分选择都是尽可能维持它 “agnostic”。
但是不用担心,基本上不存在不能通过选项来改变的由 uWSGI 开发者决定的选项。
如果你想维持 Python 的线程支持同时应用又不启动多个线程,只需要加上
--enable-threads
选项 (或者 enable-threads = true
在 ini 风格配置文件中)。
安全和可用性¶
永远 不要使用 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 sockets)。它们在权限降低之前被创建,可以
使用 =N
语法来引用,这里的 N
指 socket 编号(从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 应用开发一个最常见的问题就是 “stuck requests”(卡住的请求)。你所有的线程/worker 都被卡住(被请求堵塞),
然后你的应用再也不能接受更多的请求。
为了避免这个问题你可以设置一个 harakiri
计时器。它是一个监视器(由主进程管理),当
进程被卡住的时间超过特定的秒数后就销毁这个进程(慎重选择 harakiri
的值)。比如,你可能
想把卡住超过 30 秒的 worker 销毁掉:
[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
另外,从 uWSGI 1.9 起,统计服务器会输出所有的请求变量,所以你可以(实时地)查看你的 实例在干什么(对于每个 worker,线程或者异步 core)。
Offloading¶
The uWSGI offloading subsystem 使得你可以在某些模式满足时释放你的 worker,并且把工作委托给一个纯 c 的线程。 这样例子比如有从文件系统传递静态文件,通过网络向客户端传输数据等等。
Offloading 非常复杂,但它的使用对用户来说是透明的。如果你想试试的话加上 --offload-threads <n>
选项,这里的 <n> 是 spawn 的线程数(以 CPU 数目的线程数启动是一个不错的值)。
当 offload threads 被启用时,所有可以被优化的部分都可以自动被检测到。
Bonus: 多版本 Python 使用同一个 uWSGI 二进制文件¶
正如我们已经看到的,uWSGI 由一个很小的核心和许多插件组成。插件可以被嵌入到二进制文件中 或者动态加载。当你为 Python 构建 uWSGI 的时候,许多插件包括 Python 在内的插件都被嵌入到了最终的二进制文件中。
当你使用多个 Python 版本但是没有为每一个版本构建一个二进制文件时这可能会造成问题。
最好的方法可能是弄一个没有内置语言特性的小二进制文件,然后每个 Python 版本有一个 插件,可以动态地加载。
在 uWSGI 的源代码目录中:
make PROFILE=nolang
这将会构建一个包含除了 Python 之外的所有默认内置插件的 uwsgi 二进制文件。
现在,在相同的目录下,我们开始构建 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 默认在当前的工作目录中搜索插件。)
现在你只需要在你的配置文件中(在文件最上面)简单加上 plugins-dir 和 plugin 选项就可以了。
[uwsgi]
plugins-dir = <path_to_your_plugin_directory>
plugin = python26
这将会从你复制插件到的那个目录中加载 python26_plugin.so
插件。
那么现在...¶
有了这些很少的概念你就已经可以进入到生产中了,但是 uWSGI 是一个拥有上百个特性和配置的生态系统。 如果你想成为一个更好的系统管理员,继续阅读完整的文档吧。
perl/PSGI 应用快速入门¶
下面的说明将会引导你安装运行一个基于 perl 的目的在于运行 PSGI apps 的 uWSGI 发行版。
安装带 Perl 支持的 uWSGI¶
为了构建 uWSGI 你需要一个 c 编译器(gcc 和 clang 都支持)以及 python 二进制文件(它只会用于运行 uwsgiconfig.py 脚本来执行一些比较复杂的步骤)。 为了构建带有 perl 支持的 uWSGI 二进制文件我们也需要 perl 开发版的头文件(在 debian 系发行版上是 libperl-dev 这个包)。
你可以手动构建 uWSGI:
python uwsgiconfig.py --build psgi
这和下面一样:
UWSGI_PROFILE=psgi make
或者使用网络安装:
curl http://uwsgi.it/install | bash -s psgi /tmp/uwsgi
这将会在 /tmp/uwsgi (你可以随便改变成你想要的路径) 目录下创建一个 uWSGI 二进制文件。
使用发行版包需要注意的地方¶
你的发行版很有可能已经包含了一个 uWSGI 的包集合。这些 uWSGI 包趋向于高度模块化, 所以除了 core 你还需要安装需要的插件。 在你的配置中插件必须被加载。在学习阶段我们强烈建议不要使用发行版的包,简单地跟着文档和教程走就可以了。
一旦你对 “uWSGI 方式” 感到习惯了,你可以为你的部署选择最好的途径。
你的第一个 PSGI 应用¶
把它以 myapp.pl 文件名保存
my $app = sub {
my $env = shift;
return [
'200',
[ 'Content-Type' => 'text/html' ],
[ "<h1>Hello World</h1>" ],
];
};
然后以 http 模式运行 uWSGI:
uwsgi --http :8080 --http-modifier1 5 --psgi myapp.pl
(如果 uwsgi 不在你当前的 $PATH 里的话记着替换它)
或者如果你使用了模块化安装(比如你的发行版里的包)
uwsgi --plugins http,psgi --http :8080 --http-modifier1 5 --psgi myapp.pl
注解
当你有一个前段 web 服务器的时候不要使用 -http 选项,使用 –http-socket。继续阅读这个快速入门你就会明白为什么要这么做
‘–http-modifier1 5’ 是什么鬼???¶
uWSGI 支持多种语言和平台。当服务器收到一个请求时它必须知道“路由”它到哪里去。
每一个 uWSGI 插件都有一个分配的数字(modifier),perl/psgi 的数字是 5。所以 –http-modifier1 5 表示“路由到 psgi 插件”。
虽然 uWSGI 有一个更“友好”的 internal routing system ,但使用 modifier 仍然是最快的方式,所以尽可能地使用他们。
使用一个完整的 web 服务器:nginx¶
提供的 http 路由器仅仅就是一个路由器(是的,难以置信)。你可以使用它作为负载均衡器或者代理, 但是如果你需要一个完整的 web 服务器(比如为了高性能地提供静态文件访问或者那些 web 服务器更适合的工作), 使用 uwsig http 路由器有风险(记住把 –plugins http,psgi 改成 –plugins psgi 如果你是模块化安装的话), 你应该把你的应用放在 nginx 后面。
为了和 nginx 通信,uWSGI 可以使用多种协议:http,uwsgi,fastcgi,scgi...
性能最高的是 uwsgi。Nginx 提供了开箱即用的 uwsgi 协议支持。
使用 uwsgi socket 运行你的 psgi 应用:
uwsgi --socket 127.0.0.1:3031 --psgi myapp.pl
然后在你的 nginx 配置中加一个 location 节:
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:3031;
uwsgi_modifier1 5;
}
重启你的 nginx 服务器,然后它就会启动请求到你的 uWSGI 实例之间的代理。
注意你不需要把你的 uWSGI 配置一个特殊的 modifier,nginx 将会使用 uwsgi_modifier1 5;
指令。
如果你的代理/web 服务器/路由器 使用 HTTP,你需要告诉 uWSGI 使用 http 协议(这与 –http 不同,后者 会自己 spawn 一个代理):
uwsgi --http-socket 127.0.0.1:3031 --http-socket-modifier1 5 --psgi myapp.pl
正如你看到的我们需要指定 modifier1,因为 http 协议不能附带这种信息。
添加并发¶
你可以通过多进程,多线程或者各种异步模式来给你的应用添加并发。
要 spawn 更多的进程,使用 –processes 选项
uwsgi --socket 127.0.0.1:3031 --psgi myapp.pl --processes 4
要使用更多的线程,使用 –threads
uwsgi --socket 127.0.0.1:3031 --psgi myapp.pl --threads 8
或者两者都用
uwsgi --socket 127.0.0.1:3031 --psgi myapp.pl --threads 8 --processes 4
在 perl 世界中一个非常常见的非堵塞/协程库就是 Coro::AnyEvent 。uWSGi 简单
包含 coroae
插件就可以使用它了。
要编译一个带有 coroae
支持的 uWSGI 二进制文件只需运行:
UWSGI_PROFILE=coroae make
或者
curl http://uwsgi.it/install | bash -s coroae /tmp/uwsgi
你将会得到一个带有 psgi
和 coroae
插件的 uWSGI 二进制文件。
现在用 Coro::AnyEvent 模式来运行你的应用:
uwsgi --socket 127.0.0.1:3031 --psgi myapp.pl --coroae 1000 --processes 4
它会运行 4 个进程,每个进程可以管理 1000 个协程(或者 Coro 微线程)。
增加鲁棒性:主进程¶
非常推荐的做法是在生成环境中的应用全部都运行主进程。
它会持续地监控你的进程/线程,并且会像 The uWSGI Stats Server 一样将会添加更多有趣的特性。
要使用主进程只需要加上 –master 选项
uwsgi --socket 127.0.0.1:3031 --psgi myapp.pl --processes 4 --master
使用配置文件¶
uWSGI 提供了好几百个选项。通过命令行去处理它们是愚蠢的,所以尽量使用配置文件。
uWSGI 支持多种标准(xml, .ini, json, yaml...)。从一个标准变成另一个非常简单。
所有你在命令行中可以使用的选项只要去掉 --
前缀就可以用在配置文件中。
[uwsgi]
socket = 127.0.0.1:3031
psgi = myapp.pl
processes = 4
master = true
或者 xml:
<uwsgi>
<socket>127.0.0.1:3031</socket>
<psgi>myapp.pl</psgi>
<processes>4</processes>
<master/>
</uwsgi>
要用配置文件来运行 uWSGI,只需要通过参数来指定它就可以了:
uwsgi yourconfig.ini
如果出于某种原因你的配置文件不能以正常的拓展名(.ini, .xml, .yml, .js)结尾, 你可以用下面这种方式来强制 uWSGI 使用指定的解析器:
uwsgi --ini yourconfig.foo
uwsgi --xml yourconfig.foo
uwsgi --yaml yourconfig.foo
等等
你甚至可以使用管道流式配置(使用 - 强制从标准输入读取):
perl myjsonconfig_generator.pl | uwsgi --json -
自动启动 uWSGI¶
如果你打算写一些 init.d 脚本来启动 uWSGI,坐下来冷静一下,然后检查你的系统是否 真的没有提供更好的(现代化)的方式。
每一个发行版会选择一个启动系统 (Upstart, Systemd...) 除此之外也许多 进程管理工具 (supervisord, god...) 。
uWSGI will integrate very well with all of them (we hope), but if you plan to deploy a big number of apps check the uWSGI Emperor uWSGI 与上面列出的那些工具都集成得很好(我们希望如此),但是如果你想部署大量应用的话,看 看 uWSGI 的 Emperor 。它是每个运维开发的梦想。
安全和可用性¶
永远 不要使用 root 来运行 uWSGI 实例。你可以用 uid 和 gid 选项来降低权限:
[uwsgi]
socket = 127.0.0.1:3031
uid = foo
gid = bar
chdir = path_toyour_app
psgi = myapp.pl
master = true
processes = 8
web 应用开发一个最常见的问题就是 “stuck requests”(卡住的请求)。你所有的线程/worker 都被卡住(被请求堵塞), 然后你的应用再也不能接受更多的请求。
为了避免这个问题你可以设置一个 harakiri 计时器。它是一个监视器(由主进程管理), 当进程被卡住的时间超过特定的秒数后就销毁这个进程。
[uwsgi]
socket = 127.0.0.1:3031
uid = foo
gid = bar
chdir = path_toyour_app
psgi = myapp.pl
master = true
processes = 8
harakiri = 30
上面的配置会将卡住超过 30 秒的 worker 销毁。慎重选择 harakiri 的值 !!!
另外,从 uWSGI 1.9 起,统计服务器会输出所有的请求变量,所以你可以(实时地)查看你的 实例在干什么(对于每个 worker,线程或者异步 core)。
打开 stats server 很简单:
[uwsgi]
socket = 127.0.0.1:3031
uid = foo
gid = bar
chdir = path_toyour_app
psgi = myapp.pl
master = true
processes = 8
harakiri = 30
stats = 127.0.0.1:5000
只需要把它绑定到一个地址(UNIX domain sockt 或者 TCP)然后(你也可以使用 telnet)连接它,然后就会 返回你的实例的一个 JSON 数据。
uwsgitop
应用(你可以在官方的 github 仓库中找到它)就是一个使用 stats
server 的例子,它和 top 这种实时监控的工具类似(彩色的!!!)
Offloading¶
The uWSGI offloading subsystem 使得你可以在某些模式满足时释放你的 worker,并且把工作委托给一个纯 c 的线程。 这样例子比如有从文件系统传递静态文件,通过网络向客户端传输数据等等。
Offloading 非常复杂,但它的使用对用户来说是透明的。如果你想试试的话加上 --offload-threads <n>
选项,
这里的 <n> 是 spawn 的线程数(以 CPU 数目的线程数启动是一个不错的值)。
当 offload threads 被启用时,所有可以被优化的部分都可以自动被检测到。
那么现在...¶
有了这些很少的概念你就已经可以进入到生产中了,但是 uWSGI 是一个拥有上百个特性和配置的生态系统。 如果你想成为一个更好的系统管理员,继续阅读完整的文档吧。
ruby/Rack 应用快速入门¶
下面这份使用说明将会引导你安装,运行一个基于 Ruby 的 uWSGI 应用。旨在运行 Rack 应用。
安装带 Ruby 支持的 uWSGI¶
为了编译 uWSGI 你需要一个 C 编译器(gcc 和 clang 都支持)和一个 Python 解释器(用于运行 uwsgiconfig.py 脚本, 这个脚本 将会执行各种各样的编译步骤)。
为了编译带 Ruby 支持的 uWSGI 二进制文件,我们还需要 Ruby 开发版头文件(在 Debian
系的发行版上即 ruby-deb
包)。
你可以手动构建 uWSGI – 所有这些方式都是等价的:
make rack
UWSGI_PROFILE=rack make
make PROFILE=rack
python uwsgiconfig.py --build rack
如果你够懒的话,你可以一次性下载编译安装一个 uWSGI + Ruby 二进制文件:
curl http://uwsgi.it/install | bash -s rack /tmp/uwsgi
或者一种更 “Ruby 友好“ 的方法:
gem install uwsgi
所有这些方法都构建了一个”大一统“的 uWSGI 二进制文件。 uWSGI 项目由许多插件组成。你可以选择构建核心的服务器,然后为每个特性构建单独的插件(需要的时候就加载)。 或者你可以构建一个单独的带有所有你需要的特性的二进制文件。后者被称为 ‘monolithic’ 。
这个快速入门假定你编译了一个大一统的二进制文件(所以你不需要加载插件)。 如果你更喜欢使用你的包管理器(从官方源构建 uWSGI),请看下面。
Note for distro packages¶
你的发行版很有可能已经包含了一个 uWSGI 的包集合。 这些 uWSGI 包更倾向于高度模块化(以及偶尔会严重过时), 所以出了核心你还需要安装需要的插件。插件必须在你的 uWSGI 配置中加载。 在学习阶段我们强烈建议不要使用包管理器提供的包,而是简单跟着文档和教程走就可以了。
一旦你适应了这种 ”uWSGI 方式“,你就可以选择最适合你开发的方法了。
比如说,这个教程会使用到 “http” 和 “rack” 插件。
如果你使用的是模块化编译确保你用 --plugins http,rack
选项加载了它们。
你的第一个 Rack 应用¶
Rack 是编写 Ruby web 应用的标准方式。
这是一个标准的 Rack Hello world 脚本(把它取名为 app.ru):
class App
def call(environ)
[200, {'Content-Type' => 'text/html'}, ['Hello']]
end
end
run App.new
.ru
后缀名表示 “rackup”, 它是 Rack 包包含的一个开发工具。
Rackup 使用了一些 DSL, 所以想在 uWSGI 中使用它的话你需要安装 rack gem:
gem install rack
现在我们准备好使用 uWSGI 了:
uwsgi --http :8080 --http-modifier1 7 --rack app.ru
(记着如果 uwsgi 不在 $PATH 里的话把 ‘uwsgi’ 替换成成相应的路径)
或者如果你使用了模块化安装的话(比如你的发行版提供的包)
uwsgi --plugins http,rack --http :8080 --http-modifier1 7 --rack app.ru
通过这个命令我们 spawn 了一个 HTTP 代理,它会每一个请求转发到一个进程(叫 ‘worker’) ,worker 会 处理它然后返回一个回复给 HTTP 路由(然后它再发送给客户端)。
如果你问为什么要 spawn 两个进程,那是因为这是在生产环境中最常见的架构(一个前端 web 服务器和一个后端应用服务器)。
如果你真的不想 spawn HTTP 代理而是直接强制 worker 回应 HTTP 请求的话改下命令行就可以了:
uwsgi --http-socket :8080 --http-socket-modifier1 7 --rack app.ru
现在你就有了一个单一的进程来处理请求(但是记住这样会把应用服务器暴露在公网中,这通常 是很危险的,而且也很少用)。
‘–http-modifier1 7’ 是什么鬼?¶
uWSGI 支持多种语言和平台。当服务器收到一个请求时它得知道把它”路由”到哪里去。
每个 uWSGI 插件都有一个给定的数字(即 modifiers), ruby/rack 是 7 。所以 --http-modifier1 7
表示 “路由到 rack 插件”。
虽然 uWSGI 也有一个更人性化的 internal routing system , 但是 使用 modifiers 是处理速度最快的方式,所有尽量使用它们。
使用完整的 web 服务器: nginx¶
uWSGI 提供的 HTTP 路由器只是一个路由器。 你可以把它当成负载均衡器或者代理来使用,但是如果你需要一个完整的 web 服务器(为了高效地提供静态文件服务或者 其他类似的 web 服务器擅长的任务), 使用 uwsgi HTTP 路由器有风险(如果你使用了模块化构建的话记着把 –plugins http,rack 改成 –plugins rack), 你应该把你的应用放在 Nginx 后面。
为了和 Nginx 通讯,uWSGI 可以使用多种协议:HTTP, uwsgi, FastCGI, SCGI 等等。
性能最好的是 uwsgi 协议。Ngxin 包含了 uwsgi 协议,开箱即用。
在 uwsgi socket 上运行你的 rack 应用:
uwsgi --socket 127.0.0.1:3031 --rack app.ru
然后在你的 nginx 配置添加 location 节:
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:3031;
uwsgi_modifier1 7;
}
重启你的 nginx 服务器,然后它应该就开始为你的 uWSGI 实例反向代理请求了。
注意你并不需要配置 uWSGI 来使用特定的 modifier, nginx 将会直接使用 uwsgi_modifier1 5;
。
添加并发¶
在前面的例子中我们构建了一个一次只能处理一个请求的栈。
为了增加并发我们需要增加更多的进程。 如果你希望有一个魔法方程来计算正确的进程数目,呃,不好意思我们没有。 你需要实验监控你的应用来找到正确的值。 考虑到每一个单独的进程都是你的应用的一份完全的复制,所以内存使用需要被考虑在内。
要添加更多的进程使用 –processes <n> 选项就可以了:
uwsgi --socket 127.0.0.1:3031 --rack app.ru --processes 8
这将会 spawn 8 个进程。
混合编译时这个插件会自动编译进去。
添加更多的线程:
uwsgi --socket 127.0.0.1:3031 --rack app.ru --rbthreads 4
或者线程 + 进程
uwsgi --socket 127.0.0.1:3031 --rack app.ru --processes --rbthreads 4
有一些其他的(通常更高级/复杂)方法来增加并发(比如 ‘fibers’),但大多数情况下你会 以原先的多进程或者多线程告终。如果你感兴趣的话可以查阅 Rack 的完整文档。
增加鲁棒性:主进程¶
强烈建议在生产环境中始终使用 uWSGI 主进程。
它会持续地监视你的进程/线程,然后它还会像 The uWSGI Stats Server 一样添加一些有趣的特性。
要使用主进程只要简单地加上 --master
就可以了
uwsgi --socket 127.0.0.1:3031 --rack app.ru --processes 4 --master
使用配置文件¶
uWSGI 提供了好几百个选项(但你通常用到的不会超过几十个)。通过命令行去处理它们是愚蠢的,所以尽量使用配置文件。
uWSGI 支持多种标准(xml, .ini, json, yaml...)。从一个标准变成另一个非常简单。 所有你在命令行中可以使用的选项只要去掉 – 前缀就可以用在配置文件中。
[uwsgi]
socket = 127.0.0.1:3031
rack = app.ru
processes = 4
master = true
或者 xml:
<uwsgi>
<socket>127.0.0.1:3031</socket>
<rack>app.ru</rack>
<processes>4</processes>
<master/>
</uwsgi>
要用配置文件来运行 uWSGI,只需要通过参数来指定它就可以了:
uwsgi yourconfig.ini
如果出于某种原因你的配置文件不能以正常的拓展名(.ini, .xml, .yml, .js)结尾, 你可以用下面这种方式来强制 uWSGI 使用指定的解析器:
uwsgi --ini yourconfig.foo
uwsgi --xml yourconfig.foo
uwsgi --yaml yourconfig.foo
等等。
你甚至可以使用管道流式配置(使用 - 强制从标准输入读取):
ruby myjsonconfig_generator.rb | uwsgi --json -
当你使用多进程时的 fork() 问题¶
uWSGI is “Perlish” in a way, there is nothing we can do to hide that. Most of its choices (starting from “There’s more than one way to do it”) came from the Perl world (and more generally from classical UNIX sysadmin approaches).
有时候其他语言/平台上使用这些方法会导致不在意料中的行为发生。
当你开始学习 uWSGI 的时候一个你可能会面对的”问题”之一就是它的 fork()
使用。
默认情况下 uWSGI 在第一个 spawned 的进程里加载你的应用,然后在这个进程里面调用 fork()
多次。
这意味这你的应用被单独加载一次然后被复制。
虽然这个方法加速了服务器的启动,但有些应用可能会因为这个技术造成一些问题(特别是这些在启动的 时候初始化数据库连接的,因为连接的文件描述符会在字进程中继承)。
如果你确定应不应该使用 uWSGI 野蛮的预-fork方式,那就使用 --lazy-apps
选项禁用掉它。
它将会强制你的应用在每个 worker 里都会完整加载一次。
部署 Sinatra¶
让我们忘掉 fork(), 然后回到有趣的事情上来。这次我们要部署一个 Sinatra 应用。
require 'sinatra'
get '/hi' do
"Hello World"
end
run Sinatra::Application
保存为 config.ru
然后像前面那样运行:
[uwsgi]
socket = 127.0.0.1:3031
rack = config.ru
master = true
processes = 4
lazy-apps = true
uwsgi yourconf.ini
呃,你或许早就发现和前面例子中 app.ru 基本没有发生什么改变。
这是因为基本上所有的现代的 Rack 应用都把它自己暴露成一个 .ru 文件(通常叫 config.ru), 所以 加载应用不需要多种选项(就像 Python/WSGI 世界里的例子一样)。
部署 RubyOnRails >= 3¶
从 3.0 开始,Rails 完全兼容 Rack,并且提供了一个你可以直接加载的 cofnig.ru 文件(就像我们在 Sinatra 中做的那样)。
与 Sinatra 唯一的不同就是你的项目的布局/约定你的当前目录包含了项目,所以让我们添加一个 chdir 的选项:
[uwsgi]
socket = 127.0.0.1:3031
rack = config.ru
master = true
processes = 4
lazy-apps = true
chdir = <path_to_your_rails_app>
env = RAILS_ENV=production
uwsgi yourconf.ini
除了 chdir 之外我们还加上了 ‘env’ 选项,设置了 RAILS_ENV
环境变量。
从 4.0 起,Rails 支持多线程(仅在 ruby 2.0 中):
[uwsgi]
socket = 127.0.0.1:3031
rack = config.ru
master = true
processes = 4
rbthreads = 2
lazy-apps = true
chdir = <path_to_your_rails_app>
env = RAILS_ENV=production
部署旧版的 RubyOnRails¶
旧版的 Rails 不是完全兼容 Rack。基于这个原因所以 uWSGI 有一个专门的选项来加载旧版 Rails 应用(你 也需要 ‘thin’ gem)。
[uwsgi]
socket = 127.0.0.1:3031
master = true
processes = 4
lazy-apps = true
rails = <path_to_your_rails_app>
env = RAILS_ENV=production
所以,简单来说就是,指定 rails
选项,然后把 rails 应用目录传给它,而不是传一个 Rackup 文件。
Bundler 和 RVM¶
Bundler 是事实上的标准 Ruby 依赖管理工具。你主要在 Gemfile 文本文件中申明 你的应用需要的 gems,然后用 bundler 来安装它们。
要让 uWSGI 帮你使用 bundler 安装你只需要添加:
rbrequire = rubygems
rbrequire = bundler/setup
env = BUNDLE_GEMFILE=<path_to_your_Gemfile>
(前一个 require 在 rubty 1.9/2.x 中不需要。)
这些行主要强制 uWSGI 加载 bundler 引擎然后使用由 BUNDLE_GEMFILE
环境变量
指定的 Gemfile 文件。
当使用 Bundler 的时候(就像现代的框架一样)你通常的开发配置会这样:
[uwsgi]
socket = 127.0.0.1:3031
rack = config.ru
master = true
processes = 4
lazy-apps = true
rbrequire = rubygems
rbrequire = bundler/setup
env = BUNDLE_GEMFILE=<path_to_your_Gemfile>
除了 Bundler,RVM 是另外一个常用的工具。
它允许你有多个版本(独立的)的 Ruby 安装(以及它们的 gem 集合)在一个单独的系统中。
要让 uWSGI 使用某特定 RVM 版本的 gem 集合只需要使用 -gemset 选项:
[uwsgi]
socket = 127.0.0.1:3031
rack = config.ru
master = true
processes = 4
lazy-apps = true
rbrequire = rubygems
rbrequire = bundler/setup
env = BUNDLE_GEMFILE=<path_to_your_Gemfile>
gemset = ruby-2.0@foobar
请注意对于每一个 Ruby 版本(是 Ruby 的版本,不是 gemset 的)你需要一个 uWSGI 二进制文件(或者一个插件,如果你使用了模块化构建的话)。
如果你感兴趣,这是用来在 rvm 构建多版本的 Ruby 并各自带有 uWSGI 核心以及一个插件的命令列表:
# build the core
make nolang
# build plugin for 1.8.7
rvm use 1.8.7
./uwsgi --build-plugin "plugins/rack rack187"
# build for 1.9.2
rvm use 1.9.2
./uwsgi --build-plugin "plugins/rack rack192"
# and so on...
然后如果你想使用 ruby 1.9.2 并使用 @oops gemset:
[uwsgi]
plugins = ruby192
socket = 127.0.0.1:3031
rack = config.ru
master = true
processes = 4
lazy-apps = true
rbrequire = rubygems
rbrequire = bundler/setup
env = BUNDLE_GEMFILE=<path_to_your_Gemfile>
gemset = ruby-1.9.2@oops
自动启动¶
如果你打算打开 vi 写一个 init.d 脚本来启动 uWSGI, 坐下来冷静一下然后先确保你的系统没有提供一个更好(更现代化)的方式。
没一个发行版会选择一个启动系统 (Upstart, Systemd...) , 除此之外也有许多 进程管理工具(supervisord, god, monit, circus...)。
uWSGI 与上面列出的那些工具都集成得很好(我们希望如此),但是如果你想部署大量应用的话, 看看 uWSGI 的 Emperor - 它或多或少是每个开发运维工程师的梦想。
安全和可用性¶
永远 不要使用 root 来运行 uWSGI 实例。你可以用 uid 和 gid 选项来降低权限:
[uwsgi]
socket = 127.0.0.1:3031
uid = foo
gid = bar
chdir = path_toyour_app
rack = app.ru
master = true
processes = 8
web 应用开发一个最常见的问题就是 “stuck requests”(卡住的请求)。你所有的线程/worker 都被卡住(被请求堵塞), 然后你的应用再也不能接受更多的请求。
为了避免这个问题你可以设置一个 harakiri
计时器。它是一个监视器(由主进程管理),当进程被卡住的时间超过特定的秒数后就销毁这个进程。
[uwsgi]
socket = 127.0.0.1:3031
uid = foo
gid = bar
chdir = path_toyour_app
rack = app.ru
master = true
processes = 8
harakiri = 30
上面的配置会将卡住超过 30 秒的 worker 销毁。慎重选择 harakiri 的值!
另外,从 uWSGI 1.9 起,统计服务器会输出所有的请求变量,所以你可以(实时地)查看你的实例在干什么(对于每个 worker, 线程或者异步 core)。
打开 stats server 很简单:
[uwsgi]
socket = 127.0.0.1:3031
uid = foo
gid = bar
chdir = path_to_your_app
rack = app.ru
master = true
processes = 8
harakiri = 30
stats = 127.0.0.1:5000
只需要把它绑定到一个地址(UNIX domain sockt 或者 TCP)然后(你也可以使用 telnet)连接它, 然后就会返回你的实例的一个 JSON 数据。
uwsgitop
应用(你可以在官方的 github 仓库中找到它)就是一个使用 stats server 的例子,
它和 top 这种实时监控的工具类似(彩色的!!!)
内存使用¶
低内存消耗是真个 uWSGI 项目的一个买点之一。
不幸的是默认的苛刻内存使用可能(注意:是可能)会导致性能问题。
uWSGI Rack 插件默认在每个请求完成后调用 Ruby GC(垃圾回收器)。
如果你想减少 gc 的频率只需要添加上 --rb-gc-freq <n>
选项,
n 是多少个请求完成后才调用 GC。
如果你计划对 uWSGI 做基准测试(或者与其他的解决方案比较)请注意它的 GC 使用。
Ruby 有时可能会真的是一头内存怪兽,所以我们更倾向于默认的苛刻内存使用, 而不是为了得到 hello-world 类的基准测试(benchmarkers)高分。
Offloading¶
The uWSGI The uWSGI offloading subsystem 使得你可以在某些模式满足时立即释放你的 worker, 并且把工作委托给一个纯 c 的线程。 这样例子包括从文件系统传递静态文件,通过网络向客户端传输数据等等。
Offloading 非常复杂,但它的使用对终端用户来说是透明的。
如果你想试试的话加上 --offload-threads <n>
选项,
这里的 <n> 是 spawn 的线程数(以 CPU 数目的线程数启动是一个不错的值)。
当 offload threads 被启用时,所有可以被优化的部分都可以自动被检测到。
代码片段¶
这是一些最”有趣”的 uWSGI 特性使用的合集。
X-Sendfile emulation¶
甚至你的前端代理/web 服务器不支持 X-Sendfile (或者不能访问到你的静态资源)你可以使用 uWSGI 内部的 offloading(你的进程/线程把服务静态文件的实际工作交给 offload 线程) 来模拟它。
[uwsgi]
...
; load router_static plugin (compiled in by default in monolithic profiles)
plugins = router_static
; spawn 2 offload threads
offload-threads = 2
; files under /private can be safely served
static-safe = /private
; collect the X-Sendfile response header as X_SENDFILE var
collect-header = X-Sendfile X_SENDFILE
; if X_SENDFILE is not empty, pass its value to the "static" routing action (it will automatically use offloading if available)
response-route-if-not = empty:${X_SENDFILE} static:${X_SENDFILE}
强制 HTTPS¶
这会强制在你的整个网站上使用 HTTPS。
[uwsgi]
...
; load router_redirect plugin (compiled in by default in monolithic profiles)
plugins = router_redirect
route-if-not = equal:${HTTPS};on redirect-permanent:https://${HTTP_HOST}${REQUEST_URI}
只是对 /admin
强制 https:
[uwsgi]
...
; load router_redirect plugin (compiled in by default in monolithic profiles)
plugins = router_redirect
route = ^/admin goto:https
; stop the chain
route-run = last:
route-label = https
route-if-not = equal:${HTTPS};on redirect-permanent:https://${HTTP_HOST}${REQUEST_URI}
最后你可能还想要发送 HSTS(HTTP Strict Transport Security) http 头。
[uwsgi]
...
; load router_redirect plugin (compiled in by default in monolithic profiles)
plugins = router_redirect
route-if-not = equal:${HTTPS};on redirect-permanent:https://${HTTP_HOST}${REQUEST_URI}
route-if = equal:${HTTPS};on addheader:Strict-Transport-Security: max-age=31536000
Python 自动重新加载(Python auto-reloading)(仅限于在开发中使用!)¶
在生产环境中你可以检测文件/目录的改动,然后自动重新加载(touch-reload, fs-reload...)。
在开发的时候有一个检测所有加载的/使用的 python 模块改动会非常方便。但是请仅仅在开发过程 中使用它。
检测是通过一个线程以设定的频率扫描模块列表实现的:
[uwsgi]
...
py-autoreload = 2
这将会以每隔两秒的频率检测 python 模块的改动,然后有改动的话就重新启动实例。
再次说明:
警告
只能在开发中使用它,不要在线上环境使用。
Full-Stack CGI setup¶
This example spawned from a uWSGI mainling-list thread. 这个例子产生自一个 uWSGI 邮件列表。
我的静态文件在 /var/www 目录下,cgi 在 /var/cgi 下,Cgi 通过 /cgi-bin 路径可以访问到。 所以 /var/cig/foo.lua 会在访问 /cgi-bin/foo.lua 时运行。
[uwsgi]
workdir = /var
ipaddress = 0.0.0.0
; start an http router on port 8080
http = %(ipaddress):8080
; enable the stats server on port 9191
stats = 127.0.0.1:9191
; spawn 2 threads in 4 processes (concurrency level: 8)
processes = 4
threads = 2
; drop privileges
uid = nobody
gid = nogroup
; serve static files in /var/www
static-index = index.html
static-index = index.htm
check-static = %(workdir)/www
; skip serving static files ending with .lua
static-skip-ext = .lua
; route requests to the CGI plugin
http-modifier1 = 9
; map /cgi-bin requests to /var/cgi
cgi = /cgi-bin=%(workdir)/cgi
; only .lua script can be executed
cgi-allowed-ext = .lua
; .lua files are executed with the 'lua' command (it avoids the need of giving execute permission to files)
cgi-helper = .lua=lua
; search for index.lua if a directory is requested
cgi-index = index.lua
在不同的 url 路径下使用多个 flask 应用¶
让我们写三个 flask 应用:
#app1.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World! i am app1"
#app2.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World! i am app2"
#app3.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World! i am app3"
每个会被相应地挂载到 /app1, /app2, /app3
在 uWSGI 中要把一个应用挂载到一个特定的”key”,需要使用 –mount 选项:
`
--mount <mountpoint>=<app>
`
在我们的例子中我们想要挂载三个 python 应用,每一个以相应的 WSGI 脚本名字作为 key:
[uwsgi]
plugin = python
mount = /app1=app1.py
mount = /app2=app2.py
mount = /app3=app3.py
; generally flask apps expose the 'app' callable instead of 'application'
callable = app
; tell uWSGI to rewrite PATH_INFO and SCRIPT_NAME according to mount-points
manage-script-name = true
; bind to a socket
socket = /var/run/uwsgi.sock
现在直接把你的 webserver.proxy 指向你的实例 socket (不需要任何其他的配置)
Note: 每个应用默认会启动一个新的 python 解释器(这意味着每个应用的名字空间是相互隔离的)。 如果你希望所有的应用都运行同一个 python 虚拟机上的话,使用 –single-interpreter 选项。
Another note: 你可能已经看到 “modifier1 30” 这个明显的陷阱了。它已经被弃用了,而且它相当丑陋。uWSGI 有许多的方式来重写请求的变量。
Final note: 第一个加载的应用默认为是缺省挂载应用。当没有挂载点匹配时那个应用便会起作用。
在 OSX 上使用 rbenv (也应该能在其他的平台上工作)¶
安装 rbenv
brew update
brew install rbenv ruby-build
(不要在 .bash_profile 中设置 magic line,因为我们不想污染系统环境并且导致 uWSGI 异常)
获取一个 uWSGI 源码包,然后编译成 ‘nolang’ 版本(即一个没有编译任何语言插件进去的版本)
wget http://projects.unbit.it/downloads/uwsgi-latest.tar.gz
tar zxvf uwsgi-latest.tar.gz
cd uwsgi-xxx
make nolang
现在开始安装你需要的 ruby 版本
rbenv install 1.9.3-p551
rbenv install 2.1.5
然后安装你需要的 gems(即 sinatra):
# set the current ruby env
rbenv local 1.9.3-p551
# get the path of the gem binary
rbenv which gem
# /Users/roberta/.rbenv/versions/1.9.3-p551/bin/gem
/Users/roberta/.rbenv/versions/1.9.3-p551/bin/gem install sinatra
# from the uwsgi sources directory, build the rack plugin for 1.9.3-p551, naming it rack_193_plugin.so
# the trick here is changing PATH to find the right ruby binary during the build procedure
PATH=/Users/roberta/.rbenv/versions/1.9.3-p551/bin:$PATH ./uwsgi --build-plugin "plugins/rack rack_193"
# set ruby 2.1.5
rbenv local 2.1.5
rbenv which gem
# /Users/roberta/.rbenv/versions/2.1.5/bin/gem
/Users/roberta/.rbenv/versions/2.1.5/bin/gem install sinatra
PATH=/Users/roberta/.rbenv/versions/2.1.5/bin:$PATH ./uwsgi --build-plugin "plugins/rack rack_215"
现在切换到另外一个 ruby,只需要改变插件就可以了:
[uwsgi]
plugin = rack_193
rack = config.ru
http-socket = :9090
或者
[uwsgi]
plugin = rack_215
rack = config.ru
http-socket = :9090
请确保插件存储在当前的工作目录中,或者直接设置插件目录,或者指定绝对路径,就像这样:
[uwsgi]
plugin = /foobar/rack_215_plugin.so
rack = config.ru
http-socket = :9090
目录表¶
The Master FIFO¶
Available from uWSGI 1.9.17.
Generally you use UNIX signals to manage the master, but we are running out of signal numbers and (more importantly) not needing to mess with PIDs greatly simplifies the implementation of external management scripts.
So, instead of signals, you can tell the master to create a UNIX named pipe (FIFO) that you may use to issue commands to the master.
To create a FIFO just add --master-fifo <filename>
then start issuing commands to it.
echo r > /tmp/yourfifo
You can send multiple commands in one shot.
# add 3 workers and print stats
echo +++s > /tmp/yourfifo
Available commands¶
- ‘0’ to ‘9’ - set the fifo slot (see below)
- ‘+’ - increase the number of workers when in cheaper mode (add
--cheaper-algo manual
for full control) - ‘-‘ - decrease the number of workers when in cheaper mode (add
--cheaper-algo manual
for full control) - ‘B’ - ask Emperor for reinforcement (broodlord mode, requires uWSGI >= 2.0.7)
- ‘C’ - set cheap mode
- ‘c’ - trigger chain reload
- ‘E’ - trigger an Emperor rescan
- ‘f’ - re-fork the master (dangerous, but very powerful)
- ‘l’ - reopen log file (need –log-master and –logto/–logto2)
- ‘L’ - trigger log rotation (need –log-master and –logto/–logto2)
- ‘p’ - pause/resume the instance
- ‘P’ - update pidfiles (can be useful after master re-fork)
- ‘Q’ - brutally shutdown the instance
- ‘q’ - gracefully shutdown the instance
- ‘R’ - send brutal reload
- ‘r’ - send graceful reload
- ‘S’ - block/unblock subscriptions
- ‘s’ - print stats in the logs
- ‘W’ - brutally reload workers
- ‘w’ - gracefully reload workers
FIFO slots¶
uWSGI supports up to 10 different FIFO files. By default the first specified is bound (mapped as ‘0’).
During the instance’s lifetime you can change from one FIFO to another by simply sending the number of the FIFO slot to use.
[uwsgi]
master-fifo = /tmp/fifo0
master-fifo = /tmp/fifo1
master-fifo = /var/run/foofifo
processes = 2
...
By default /tmp/fifo0
will be allocated, but after sending:
echo 1 > /tmp/fifo0
the /tmp/fifo1
file will be bound.
This is very useful to map FIFO files to specific instance when you (ab)use the ‘fork the master’ command (the ‘f’ one).
echo 1fp > /tmp/fifo0
After sending this command, a new uWSGI instance (inheriting all of the bound sockets) will be spawned, the old one will be put in “paused” mode (the ‘p’ command).
As we have sent the ‘1’ command before ‘f’ and ‘p’ the old instance will now accept commands on /tmp/fifo1 (the slot 1), and the new one will use the default one (‘0’).
There are lot of tricks you can accomplish, and lots of ways to abuse the forking of the master.
Just take into account that corner-case problems can occur all over the place, especially if you use the most complex features of uWSGI.
Notes¶
- The FIFO is created in non-blocking modes and recreated by the master every time a client disconnects.
- You can override (or add) commands using the global array
uwsgi_fifo_table
via plugins or C hooks. - Only the uid running the master has write access to the fifo.
Systemd¶
uWSGI is a new-style daemon for systemd.
It can notify status change and readyness.
When uWSGI detects it is running under systemd, the notification system is enabled.
Adding the Emperor to systemd¶
The best approach to integrate uWSGI apps with your init system is using the Emperor.
Your init system will talk only with the Emperor that will rule all of the apps itself.
Create a systemd service file (you can save it as /etc/systemd/system/emperor.uwsgi.service)
[Unit]
Description=uWSGI Emperor
After=syslog.target
[Service]
ExecStart=/root/uwsgi/uwsgi --ini /etc/uwsgi/emperor.ini
Restart=always
KillSignal=SIGQUIT
Type=notify
StandardError=syslog
NotifyAccess=all
[Install]
WantedBy=multi-user.target
Then run it
systemctl start emperor.uwsgi.service
And check its status.
systemctl status emperor.uwsgi.service
You will see the Emperor reporting the number of governed vassals to systemd (and to you).
emperor.uwsgi.service - uWSGI Emperor
Loaded: loaded (/etc/systemd/system/emperor.uwsgi.service)
Active: active (running) since Tue, 17 May 2011 08:51:31 +0200; 5s ago
Main PID: 30567 (uwsgi)
Status: "The Emperor is governing 1 vassals"
CGroup: name=systemd:/system/emperor.uwsgi.service
├ 30567 /root/uwsgi/uwsgi --ini /etc/uwsgi/emperor.ini
├ 30568 /root/uwsgi/uwsgi --ini werkzeug.ini
└ 30569 /root/uwsgi/uwsgi --ini werkzeug.ini
You can stop the Emperor (and all the apps it governs) with
systemctl stop emperor.uwsgi.service
A simple emperor.ini
could look like this (www-data is just an anonymous user)
NOTE: DO NOT daemonize the Emperor (or the master) unless you know what you are doing!!!
[uwsgi]
emperor = /etc/uwsgi/vassals
uid = www-data
gid = www-data
If you want to allow each vassal to run under different privileges, remove the uid
and gid
options from the emperor configuration (and please read the Emperor docs!)
Logging¶
Using the previous service file all of the Emperor messages go to the syslog. You can avoid it by removing the StandardError=syslog
directive.
If you do that, be sure to set a --logto
option in your Emperor configuration, otherwise all of your logs will be lost!
Putting sockets in /run/¶
On a modern system, /run/ is mounted as a tmpfs and is the right place to put sockets and pidfiles into. You can have systemd create a uwsgi directory to put them into by creating a systemd-tmpfiles configuration file (you can save it as /etc/tmpfiles.d/emperor.uwsgi.conf):
d /run/uwsgi 0755 www-data www-data -
Socket activation¶
Starting from uWSGI 0.9.8.3 socket activation is available. You can setup systemd to spawn uWSGI instances only after the first socket connection.
Create the required emperor.uwsgi.socket (in /etc/systemd/system/emperor.uwsgi.socket
). Note that the *.socket file name must match the *.service file name.
[Unit]
Description=Socket for uWSGI Emperor
[Socket]
# Change this to your uwsgi application port or unix socket location
ListenStream=/tmp/uwsgid.sock
[Install]
WantedBy=sockets.target
Then disable the service and enable the socket unit.
# systemctl disable emperor.uwsgi.service
# systemctl enable emperor.uwsgi.socket
Running uWSGI instances with Circus¶
Circus (http://circus.readthedocs.org/en/0.7/) is a process manager written in Python. It is very similar to projects like Supervisor, but with several additional features. Although most, if not all, of it’s functionalities have a counterpart in uWSGI, Circus can be used as a library allowing you to build dynamic configurations (and extend uWSGI patterns). This aspect is very important and may be the real selling point of Circus.
Socket activation¶
Based on the venerable inetd pattern, Circus can bind to sockets and pass them to children.
Start with a simple Circus config (call it circus.ini):
[circus]
endpoint = tcp://127.0.0.1:5555
pubsub_endpoint = tcp://127.0.0.1:5556
stats_endpoint = tcp://127.0.0.1:5557
[watcher:dummy]
cmd = uwsgi --http-socket fd://$(circus.sockets.foo) --wsgi-file yourapp.wsgi
use_sockets = True
send_hup = True
stop_signal = QUIT
[socket:foo]
host = 0.0.0.0
port = 8888
run it with
circusd circus.ini
(Better) Socket activation¶
If you want to spawn instances on demand, you will likely want to shut them down when they are no longer used. To accomplish that use the –idle uWSGI option.
[circus]
check_delay = 5
endpoint = tcp://127.0.0.1:5555
pubsub_endpoint = tcp://127.0.0.1:5556
stats_endpoint = tcp://127.0.0.1:5557
[watcher:dummy]
cmd = uwsgi --master --idle 60 --http-socket fd://$(circus.sockets.foo) --wsgi-file yourapp.wsgi
use_sockets = True
warmup_delay = 0
send_hup = True
stop_signal = QUIT
[socket:foo]
host = 0.0.0.0
port = 8888
This time we have enabled the master process. It will manage the –idle option, shutting down the instance if it is inactive for more than 60 seconds.
教程¶
The uWSGI Caching Cookbook¶
This is a cookbook of various caching techniques using uWSGI internal routing, The uWSGI caching framework and uWSGI Transformations
The examples assume a modular uWSGI build. You can ignore the ‘plugins’ option, if you are using a monolithic build.
Recipes are tested over uWSGI 1.9.7. Older versions may not work.
Let’s start¶
This is a simple perl/PSGI Dancer app we deploy on an http-socket with 4 processes.
use Dancer;
get '/' => sub {
"Hello World!"
};
dance;
This is the uWSGI config. Pay attention to the log-micros directive. The objective of uWSGI in-memory caching is generating a response in less than 1 millisecond (yes, this is true), so we want to get the response time logging in microseconds (thousandths of a millisecond).
[uwsgi]
; load the PSGI plugin as the default one
plugins = 0:psgi
; load the Dancer app
psgi = myapp.pl
; enable the master process
master = true
; spawn 4 processes
processes = 4
; bind an http socket to port 9090
http-socket = :9090
; log response time with microseconds resolution
log-micros = true
Run the uWSGI instance in your terminal and just make a bunch of requests to it.
curl -D /dev/stdout http://localhost:9090/
If all goes well you should see something similar in your uWSGI logs:
[pid: 26586|app: 0|req: 1/1] 192.168.173.14 () {24 vars in 327 bytes} [Wed Apr 17 09:06:58 2013] GET / => generated 12 bytes in 3497 micros (HTTP/1.1 200) 4 headers in 126 bytes (0 switches on core 0)
[pid: 26586|app: 0|req: 2/2] 192.168.173.14 () {24 vars in 327 bytes} [Wed Apr 17 09:07:14 2013] GET / => generated 12 bytes in 1134 micros (HTTP/1.1 200) 4 headers in 126 bytes (0 switches on core 0)
[pid: 26586|app: 0|req: 3/3] 192.168.173.14 () {24 vars in 327 bytes} [Wed Apr 17 09:07:16 2013] GET / => generated 12 bytes in 1249 micros (HTTP/1.1 200) 4 headers in 126 bytes (0 switches on core 0)
[pid: 26586|app: 0|req: 4/4] 192.168.173.14 () {24 vars in 327 bytes} [Wed Apr 17 09:07:17 2013] GET / => generated 12 bytes in 953 micros (HTTP/1.1 200) 4 headers in 126 bytes (0 switches on core 0)
[pid: 26586|app: 0|req: 5/5] 192.168.173.14 () {24 vars in 327 bytes} [Wed Apr 17 09:07:18 2013] GET / => generated 12 bytes in 1016 micros (HTTP/1.1 200) 4 headers in 126 bytes (0 switches on core 0)
while cURL will return:
HTTP/1.1 200 OK
Server: Perl Dancer 1.3112
Content-Length: 12
Content-Type: text/html
X-Powered-By: Perl Dancer 1.3112
Hello World!
The first request on a process took about 3 milliseconds (this is normal as lot of code is executed for the first request), but the following run in about 1 millisecond.
Now we want to store the response in the uWSGI cache.
The first recipe¶
We first create a uWSGI cache named ‘mycache’ with 100 slots of 64 KiB each (new options are at the end of the config) and for each request for ‘/’ we search in it for a specific item named ‘myhome’.
This time we load the router_cache
plugin too (though it is built-in by default in monolithic servers).
[uwsgi]
; load the PSGI plugin as the default one
plugins = 0:psgi,router_cache
; load the Dancer app
psgi = myapp.pl
; enable the master process
master = true
; spawn 4 processes
processes = 4
; bind an http socket to port 9090
http-socket = :9090
; log response time with microseconds resolution
log-micros = true
; create a cache with 100 items (default size per-item is 64k)
cache2 = name=mycache,items=100
; at each request for / check for a 'myhome' item in the 'mycache' cache
; 'route' apply a regexp to the PATH_INFO request var
route = ^/$ cache:key=myhome,name=mycache
Restart uWSGI and re-run the previous test with cURL. Sadly nothing will change. Why?
Because you did not instruct uWSGI to store the plugin response in the cache. You need to use the cachestore
routing action...
[uwsgi]
; load the PSGI plugin as the default one
plugins = 0:psgi,router_cache
; load the Dancer app
psgi = myapp.pl
; enable the master process
master = true
; spawn 4 processes
processes = 4
; bind an http socket to port 9090
http-socket = :9090
; log response time with microseconds resolution
log-micros = true
; create a cache with 100 items (default size per-item is 64k)
cache2 = name=mycache,items=100
; at each request for / check for a 'myhome' item in the 'mycache' cache
; 'route' apply a regexp to the PATH_INFO request var
route = ^/$ cache:key=myhome,name=mycache
; store each successfull request (200 http status code) for '/' in the 'myhome' item
route = ^/$ cachestore:key=myhome,name=mycache
Now re-run the test, and you should see requests going down to a range of 100-300 microseconds. The gain depends on various factors, but you should gain at least 60% in response time.
The log line reports -1 as the app id:
[pid: 26703|app: -1|req: -1/2] 192.168.173.14 () {24 vars in 327 bytes} [Wed Apr 17 09:24:52 2013] GET / => generated 12 bytes in 122 micros (HTTP/1.1 200) 2 headers in 64 bytes (0 switches on core 0)
This is because when a response is served from the cache your app/plugin is not touched (in this case, no perl call is involved).
You will note less headers too:
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 12
Hello World!
This is because only the body of a response is cached. By default the generated response is set as text/html but you can change it or let the MIME type engine do the work for you (see later).
Cache them all !!!¶
We want to cache all of our requests. Some of them returns images and css, while the others are always text/html
[uwsgi]
; load the PSGI plugin as the default one
plugins = 0:psgi,router_cache
; load the Dancer app
psgi = myapp.pl
; enable the master process
master = true
; spawn 4 processes
processes = 4
; bind an http socket to port 9090
http-socket = :9090
; log response time with microseconds resolution
log-micros = true
; create a cache with 100 items (default size per-item is 64k)
cache2 = name=mycache,items=100
; load the mime types engine
mime-file = /etc/mime.types
; at each request starting with /img check it in the cache (use mime types engine for the content type)
route = ^/img/(.+) cache:key=/img/$1,name=mycache,mime=1
; at each request ending with .css check it in the cache
route = \.css$ cache:key=${REQUEST_URI},name=mycache,content_type=text/css
; fallback to text/html all of the others request
route = .* cache:key=${REQUEST_URI},name=mycache
; store each successfull request (200 http status code) in the 'mycache' cache using the REQUEST_URI as key
route = .* cachestore:key=${REQUEST_URI},name=mycache
Multiple caches¶
You may want/need to store items in different caches. We can chnage the previous recipe to use three different caches for images, css and html responses.
[uwsgi]
; load the PSGI plugin as the default one
plugins = 0:psgi,router_cache
; load the Dancer app
psgi = myapp.pl
; enable the master process
master = true
; spawn 4 processes
processes = 4
; bind an http socket to port 9090
http-socket = :9090
; log response time with microseconds resolution
log-micros = true
; create a cache with 100 items (default size per-item is 64k)
cache2 = name=mycache,items=100
; create a cache for images with dynamic size (images can be big, so do not waste memory)
cache2 = name=images,items=20,bitmap=1,blocks=100
; a cache for css (20k per-item is more than enough)
cache2 = name=stylesheets,items=30,blocksize=20000
; load the mime types engine
mime-file = /etc/mime.types
; at each request starting with /img check it in the 'images' cache (use mime types engine for the content type)
route = ^/img/(.+) cache:key=/img/$1,name=images,mime=1
; at each request ending with .css check it in the 'stylesheets' cache
route = \.css$ cache:key=${REQUEST_URI},name=stylesheets,content_type=text/css
; fallback to text/html all of the others request
route = .* cache:key=${REQUEST_URI},name=mycache
; store each successfull request (200 http status code) in the 'mycache' cache using the REQUEST_URI as key
route = .* cachestore:key=${REQUEST_URI},name=mycache
; store images and stylesheets in the corresponding caches
route = ^/img/ cachestore:key=${REQUEST_URI},name=images
route = ^/css/ cachestore:key=${REQUEST_URI},name=stylesheets
Important, every matched ‘cachestore’ will overwrite the previous one. So we are adding .* as the first rule.
Being more aggressive, the Expires HTTP header¶
You can set an expiration for each cache item. If an item has an expire, it will be translated to HTTP Expires headers. This means that once you have sent a cache item to the browser, it will not request it until it expires!
We use the previous recipe simply adding different expires to the items.
[uwsgi]
; load the PSGI plugin as the default one
plugins = 0:psgi,router_cache
; load the Dancer app
psgi = myapp.pl
; enable the master process
master = true
; spawn 4 processes
processes = 4
; bind an http socket to port 9090
http-socket = :9090
; log response time with microseconds resolution
log-micros = true
; create a cache with 100 items (default size per-item is 64k)
cache2 = name=mycache,items=100
; create a cache for images with dynamic size (images can be big, so do not waste memory)
cache2 = name=images,items=20,bitmap=1,blocks=100
; a cache for css (20k per-item is more than enough)
cache2 = name=stylesheets,items=30,blocksize=20000
; load the mime types engine
mime-file = /etc/mime.types
; at each request starting with /img check it in the 'images' cache (use mime types engine for the content type)
route = ^/img/(.+) cache:key=/img/$1,name=images,mime=1
; at each request ending with .css check it in the 'stylesheets' cache
route = \.css$ cache:key=${REQUEST_URI},name=stylesheets,content_type=text/css
; fallback to text/html all of the others request
route = .* cache:key=${REQUEST_URI},name=mycache
; store each successfull request (200 http status code) in the 'mycache' cache using the REQUEST_URI as key
route = .* cachestore:key=${REQUEST_URI},name=mycache,expires=60
; store images and stylesheets in the corresponding caches
route = ^/img/ cachestore:key=${REQUEST_URI},name=images,expires=3600
route = ^/css/ cachestore:key=${REQUEST_URI},name=stylesheets,expires=3600
images and stylesheets are cached for 1 hour, while html response are cached for 1 minute
Monitoring Caches¶
The stats server exposes cache information.
There is an ncurses-based tool (https://pypi.python.org/pypi/uwsgicachetop) using that information.
Storing GZIP variant of an object¶
Back to the first recipe. We may want to store two copies of a response. The “clean” one and a gzipped one for clients supporting gzip encoding.
To enable the gzip copy you only need to choose a name for the item and pass it as the ‘gzip’ option of the cachestore action.
Then check for HTTP_ACCEPT_ENCODING request header. If it contains the ‘gzip’ word you can send it the gzip variant.
[uwsgi]
; load the PSGI plugin as the default one
plugins = 0:psgi,router_cache
; load the Dancer app
psgi = myapp.pl
; enable the master process
master = true
; spawn 4 processes
processes = 4
; bind an http socket to port 9090
http-socket = :9090
; log response time with microseconds resolution
log-micros = true
; create a cache with 100 items (default size per-item is 64k)
cache2 = name=mycache,items=100
; if the client support GZIP give it the gzip body
route-if = contains:${HTTP_ACCEPT_ENCODING};gzip cache:key=gzipped_myhome,name=mycache,content_encoding=gzip
; else give it the clear version
route = ^/$ cache:key=myhome,name=mycache
; store each successfull request (200 http status code) for '/' in the 'myhome' item in gzip too
route = ^/$ cachestore:key=myhome,gzip=gzipped_myhome,name=mycache
Storing static files in the cache for fast serving¶
You can populate a uWSGI cache on server startup with static files for fast serving them. The option –load-file-in-cache is the right tool for the job
[uwsgi]
plugins = 0:notfound,router_cache
http-socket = :9090
cache2 = name=files,bitmap=1,items=1000,blocksize=10000,blocks=2000
load-file-in-cache = files /usr/share/doc/socat/index.html
route-run = cache:key=${REQUEST_URI},name=files
You can specify all of the –load-file-in-cache directive you need but a better approach would be
[uwsgi]
plugins = router_cache
http-socket = :9090
cache2 = name=files,bitmap=1,items=1000,blocksize=10000,blocks=2000
for-glob = /usr/share/doc/socat/*.html
load-file-in-cache = files %(_)
endfor =
route-run = cache:key=${REQUEST_URI},name=files
this will store all of the html files in /usr/share/doc/socat.
Items are stored with the path as the key.
When a non-existent item is requested the connection is closed and you should get an ugly
-- unavailable modifier requested: 0 --
This is because the internal routing system failed to manage the request, and no request plugin is available to manage the request.
You can build a better infrastructure using the simple ‘notfound’ plugin (it will always return a 404)
[uwsgi]
plugins = 0:notfound,router_cache
http-socket = :9090
cache2 = name=files,bitmap=1,items=1000,blocksize=10000,blocks=2000
for-glob = /usr/share/doc/socat/*.html
load-file-in-cache = files %(_)
endfor =
route-run = cache:key=${REQUEST_URI},name=files
You can store file in the cache as gzip too using –load-file-in-cache-gzip
This option does not allow to set the name of the cache item, so to support client iwith and without gzip support we can use 2 different caches
[uwsgi]
plugins = 0:notfound,router_cache
http-socket = :9090
cache2 = name=files,bitmap=1,items=1000,blocksize=10000,blocks=2000
cache2 = name=compressedfiles,bitmap=1,items=1000,blocksize=10000,blocks=2000
for-glob = /usr/share/doc/socat/*.html
load-file-in-cache = files %(_)
load-file-in-cache-gzip = compressedfiles %(_)
endfor =
; take the item from the compressed cache
route-if = contains:${HTTP_ACCEPT_ENCODING};gzip cache:key=${REQUEST_URI},name=compressedfiles,content_encoding=gzip
; fallback to the uncompressed one
route-run = cache:key=${REQUEST_URI},name=files
Caching for authenticated users¶
If you authenticate users with http basic auth, you can differentiate caching for each one using the ${REMOTE_USER} request variable:
[uwsgi]
; load the PSGI plugin as the default one
plugins = 0:psgi,router_cache
; load the Dancer app
psgi = myapp.pl
; enable the master process
master = true
; spawn 4 processes
processes = 4
; bind an http socket to port 9090
http-socket = :9090
; log response time with microseconds resolution
log-micros = true
; create a cache with 100 items (default size per-item is 64k)
cache2 = name=mycache,items=100
; check if the user is authenticated
route-if-not = empty:${REMOTE_USER} goto:cacheme
route-run = break:
; the following rules are executed only if REMOTE_USER is defined
route-label = cacheme
route = ^/$ cache:key=myhome_for_${REMOTE_USER},name=mycache
; store each successfull request (200 http status code) for '/'
route = ^/$ cachestore:key=myhome_for_${REMOTE_USER},name=mycache
Cookie-based authentication is generally more complex, but the vast majority of time a session id is passed as a cookie.
You may want to use this session_id as the key
[uwsgi]
; load the PHP plugin as the default one
plugins = 0:php,router_cache
; enable the master process
master = true
; spawn 4 processes
processes = 4
; bind an http socket to port 9090
http-socket = :9090
; log response time with microseconds resolution
log-micros = true
; create a cache with 100 items (default size per-item is 64k)
cache2 = name=mycache,items=100
; check if the user is authenticated
route-if-not = empty:${cookie[PHPSESSID]} goto:cacheme
route-run = break:
; the following rules are executed only if the PHPSESSID cookie is defined
route-label = cacheme
route = ^/$ cache:key=myhome_for_${cookie[PHPSESSID]},name=mycache
; store each successfull request (200 http status code) for '/'
route = ^/$ cachestore:key=myhome_for_${cookie[PHPSESSID]},name=mycache
Obviously a malicious user could build a fake session id and could potentially fill your cache. You should always check the session id. There is no single solution, but a good example for file-based php session is the following one:
[uwsgi]
; load the PHP plugin as the default one
plugins = 0:php,router_cache
; enable the master process
master = true
; spawn 4 processes
processes = 4
; bind an http socket to port 9090
http-socket = :9090
; log response time with microseconds resolution
log-micros = true
; create a cache with 100 items (default size per-item is 64k)
cache2 = name=mycache,items=100
; check if the user is authenticated
route-if-not = empty:${cookie[PHPSESSID]} goto:cacheme
route-run = break:
; the following rules are executed only if the PHPSESSID cookie is defined
route-label = cacheme
; stop if the session file does not exist
route-if-not = isfile:/var/lib/php5/sessions/sess_${cookie[PHPSESSID]} break:
route = ^/$ cache:key=myhome_for_${cookie[PHPSESSID]},name=mycache
; store each successfull request (200 http status code) for '/'
route = ^/$ cachestore:key=myhome_for_${cookie[PHPSESSID]},name=mycache
Caching to files¶
Sometimes, instead of caching in memory you want to store static files.
The transformation_tofile plugin allows you to store responses in files:
[uwsgi]
; load the PHP plugin as the default one
plugins = 0:psgi,transformation_tofile,router_static
; load the Dancer app
psgi = myapp.pl
; enable the master process
master = true
; spawn 4 processes
processes = 4
; bind an http socket to port 9090
http-socket = :9090
; log response time with microseconds resolution
log-micros = true
; check if a file exists
route-if = isfile:/var/www/cache/${hex[PATH_INFO]}.html static:/var/www/cache/${hex[PATH_INFO]}.html
; otherwise store the response in it
route-run = tofile:/var/www/cache/${hex[PATH_INFO]}.html
the hex[] routing var take a request variable content and encode it in hexadecimal. As PATH_INFO tend to contains / it is a better approach than storing full path names (or using other encoding scheme like base64 that can include slashes too)
Setting up Django and your web server with uWSGI and nginx¶
This tutorial is aimed at the Django user who wants to set up a production web server. It takes you through the steps required to set up Django so that it works nicely with uWSGI and nginx. It covers all three components, providing a complete stack of web application and server software.
Django Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design.
nginx (pronounced engine-x) is a free, open-source, high-performance HTTP server and reverse proxy, as well as an IMAP/POP3 proxy server.
Some notes about this tutorial¶
Note
This is a tutorial. It is not intended to provide a reference guide, never mind an exhaustive reference, to the subject of deployment.
nginx and uWSGI are good choices for Django deployment, but they are not the only ones, or the ‘official’ ones. There are excellent alternatives to both, and you are encouraged to investigate them.
The way we deploy Django here is a good way, but it is not the only way; for some purposes it is probably not even the best way.
It is however a reliable and easy way, and the material covered here will introduce you to concepts and procedures you will need to be familiar with whatever software you use for deploying Django. By providing you with a working setup, and rehearsing the steps you must take to get there, it will offer you a basis for exploring other ways to achieve this.
Note
This tutorial makes some assumptions about the system you are using.
It is assumed that you are using a Unix-like system, and that it features an aptitude-like package manager. However if you need to ask questions like “What’s the equivalent of aptitude on Mac OS X?”, you’ll be able to find that kind of help fairly easily.
While this tutorial assumes Django 1.4 or later, which will automatically create a wsgi module in your new project, the instructions will work with earlier versions. You will though need to obtain that Django wsgi module yourself, and you may find that the Django project directory structure is slightly different.
Concept¶
A web server faces the outside world. It can serve files (HTML, images, CSS, etc) directly from the file system. However, it can’t talk directly to Django applications; it needs something that will run the application, feed it requests from web clients (such as browsers) and return responses.
A Web Server Gateway Interface - WSGI - does this job. WSGI is a Python standard.
uWSGI is a WSGI implementation. In this tutorial we will set up uWSGI so that it creates a Unix socket, and serves responses to the web server via the WSGI protocol. At the end, our complete stack of components will look like this:
the web client <-> the web server <-> the socket <-> uwsgi <-> Django
Before you start setting up uWSGI¶
virtualenv¶
Make sure you are in a virtualenv for the software we need to install (we will describe how to install a system-wide uwsgi later):
virtualenv uwsgi-tutorial
cd uwsgi-tutorial
source bin/activate
Django¶
Install Django into your virtualenv, create a new project, and cd
into the
project:
pip install Django
django-admin.py startproject mysite
cd mysite
About the domain and port¶
In this tutorial we will call your domain example.com
. Substitute your own
FQDN or IP address.
Throughout, we’ll be using port 8000 for the web server to publish on, just like the Django runserver does by default. You can use whatever port you want of course, but I have chosen this one so it doesn’t conflict with anything a web server might be doing already.
Basic uWSGI installation and configuration¶
Install uWSGI into your virtualenv¶
pip install uwsgi
Of course there are other ways to install uWSGI, but this one is as good as
any. Remember that you will need to have Python development packages installed.
In the case of Debian, or Debian-derived systems such as Ubuntu, what you need
to have installed is pythonX.Y-dev
, where X.Y is your version of Python.
Basic test¶
Create a file called test.py
:
# test.py
def application(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
return [b"Hello World"] # python3
#return ["Hello World"] # python2
注解
Take into account that Python 3 requires bytes()
.
Run uWSGI:
uwsgi --http :8000 --wsgi-file test.py
The options mean:
http :8000
: use protocol http, port 8000wsgi-file test.py
: load the specified file, test.py
This should serve a ‘hello world’ message directly to the browser on port 8000. Visit:
http://example.com:8000
to check. If so, it means the following stack of components works:
the web client <-> uWSGI <-> Python
Test your Django project¶
Now we want uWSGI to do the same thing, but to run a Django site instead of the
test.py
module.
If you haven’t already done so, make sure that your mysite
project actually works:
python manage.py runserver 0.0.0.0:8000
And if it that works, run it using uWSGI:
uwsgi --http :8000 --module mysite.wsgi
module mysite.wsgi
: load the specified wsgi module
Point your browser at the server; if the site appears, it means uWSGI is able to serve your Django application from your virtualenv, and this stack operates correctly:
the web client <-> uWSGI <-> Django
Now normally we won’t have the browser speaking directly to uWSGI. That’s a job for the webserver, which will act as a go-between.
Basic nginx¶
Install nginx¶
sudo apt-get install nginx
sudo /etc/init.d/nginx start # start nginx
And now check that the nginx is serving by visiting it in a web browser on port 80 - you should get a message from nginx: “Welcome to nginx!”. That means these components of the full stack are working together:
the web client <-> the web server
If something else is already serving on port 80 and you want to use nginx there, you’ll have to reconfigure nginx to serve on a different port. For this tutorial though, we’re going to be using port 8000.
Configure nginx for your site¶
You will need the uwsgi_params
file, which is available in the nginx
directory of the uWSGI distribution, or from
https://github.com/nginx/nginx/blob/master/conf/uwsgi_params
Copy it into your project directory. In a moment we will tell nginx to refer to it.
Now create a file called mysite_nginx.conf, and put this in it:
# mysite_nginx.conf
# the upstream component nginx needs to connect to
upstream django {
# server unix:///path/to/your/mysite/mysite.sock; # for a file socket
server 127.0.0.1:8001; # for a web port socket (we'll use this first)
}
# configuration of the server
server {
# the port your site will be served on
listen 8000;
# the domain name it will serve for
server_name .example.com; # substitute your machine's IP address or FQDN
charset utf-8;
# max upload size
client_max_body_size 75M; # adjust to taste
# Django media
location /media {
alias /path/to/your/mysite/media; # your Django project's media files - amend as required
}
location /static {
alias /path/to/your/mysite/static; # your Django project's static files - amend as required
}
# Finally, send all non-media requests to the Django server.
location / {
uwsgi_pass django;
include /path/to/your/mysite/uwsgi_params; # the uwsgi_params file you installed
}
}
This conf file tells nginx to serve up media and static files from the filesystem, as well as handle requests that require Django’s intervention. For a large deployment it is considered good practice to let one server handle static/media files, and another handle Django applications, but for now, this will do just fine.
Symlink to this file from /etc/nginx/sites-enabled so nginx can see it:
sudo ln -s ~/path/to/your/mysite/mysite_nginx.conf /etc/nginx/sites-enabled/
Deploying static files¶
Before running nginx, you have to collect all Django static files in the static folder. First of all you have to edit mysite/settings.py adding:
STATIC_ROOT = os.path.join(BASE_DIR, "static/")
and then run
python manage.py collectstatic
Basic nginx test¶
Restart nginx:
sudo /etc/init.d/nginx restart
To check that media files are being served correctly, add an image called
media.png
to the /path/to/your/project/project/media directory
, then
visit http://example.com:8000/media/media.png - if this works, you’ll know at
least that nginx is serving files correctly.
It is worth not just restarting nginx, but actually stopping and then starting it again, which will inform you if there is a problem, and where it is.
nginx and uWSGI and test.py¶
Let’s get nginx to speak to the “hello world” test.py
application.
uwsgi --socket :8001 --wsgi-file test.py
This is nearly the same as before, except this time one of the options is different:
socket :8001
: use protocol uwsgi, port 8001
nginx meanwhile has been configured to communicate with uWSGI on that port, and with the outside world on port 8000. Visit:
to check. And this is our stack:
the web client <-> the web server <-> the socket <-> uWSGI <-> Python
Meanwhile, you can try to have a look at the uswgi output at http://example.com:8001 - but quite probably, it won’t work because your browser speaks http, not uWSGI, though you should see output from uWSGI in your terminal.
Using Unix sockets instead of ports¶
So far we have used a TCP port socket, because it’s simpler, but in fact it’s better to use Unix sockets than ports - there’s less overhead.
Edit mysite_nginx.conf
, changing it to match:
server unix:///path/to/your/mysite/mysite.sock; # for a file socket
# server 127.0.0.1:8001; # for a web port socket (we'll use this first)
and restart nginx.
Run uWSGI again:
uwsgi --socket mysite.sock --wsgi-file test.py
This time the socket
option tells uWSGI which file to use.
Try http://example.com:8000/ in the browser.
If that doesn’t work¶
Check your nginx error log(/var/log/nginx/error.log). If you see something like:
connect() to unix:///path/to/your/mysite/mysite.sock failed (13: Permission
denied)
then probably you need to manage the permissions on the socket so that nginx is allowed to use it.
Try:
uwsgi --socket mysite.sock --wsgi-file test.py --chmod-socket=666 # (very permissive)
or:
uwsgi --socket mysite.sock --wsgi-file test.py --chmod-socket=664 # (more sensible)
You may also have to add your user to nginx’s group (which is probably www-data), or vice-versa, so that nginx can read and write to your socket properly.
It’s worth keeping the output of the nginx log running in a terminal window so you can easily refer to it while troubleshooting.
Running the Django application with uwsgi and nginx¶
Let’s run our Django application:
uwsgi --socket mysite.sock --module mysite.wsgi --chmod-socket=664
Now uWSGI and nginx should be serving up not just a “Hello World” module, but your Django project.
Configuring uWSGI to run with a .ini file¶
We can put the same options that we used with uWSGI into a file, and then ask uWSGI to run with that file. It makes it easier to manage configurations.
Create a file called `mysite_uwsgi.ini`
:
# mysite_uwsgi.ini file
[uwsgi]
# Django-related settings
# the base directory (full path)
chdir = /path/to/your/project
# Django's wsgi file
module = project.wsgi
# the virtualenv (full path)
home = /path/to/virtualenv
# process-related settings
# master
master = true
# maximum number of worker processes
processes = 10
# the socket (use the full path to be safe
socket = /path/to/your/project/mysite.sock
# ... with appropriate permissions - may be needed
# chmod-socket = 664
# clear environment on exit
vacuum = true
And run uswgi using this file:
uwsgi --ini mysite_uwsgi.ini # the --ini option is used to specify a file
Once again, test that the Django site works as expected.
Install uWSGI system-wide¶
So far, uWSGI is only installed in our virtualenv; we’ll need it installed system-wide for deployment purposes.
Deactivate your virtualenv:
deactivate
and install uWSGI system-wide:
sudo pip install uwsgi
# Or install LTS (long term support).
pip install http://projects.unbit.it/downloads/uwsgi-lts.tar.gz
The uWSGI wiki describes several installation procedures. Before installing uWSGI system-wide, it’s worth considering which version to choose and the most apppropriate way of installing it.
Check again that you can still run uWSGI just like you did before:
uwsgi --ini mysite_uwsgi.ini # the --ini option is used to specify a file
Emperor mode¶
uWSGI can run in ‘emperor’ mode. In this mode it keeps an eye on a directory of uWSGI config files, and will spawn instances (‘vassals’) for each one it finds.
Whenever a config file is amended, the emperor will automatically restart the vassal.
# create a directory for the vassals
sudo mkdir /etc/uwsgi
sudo mkdir /etc/uwsgi/vassals
# symlink from the default config directory to your config file
sudo ln -s /path/to/your/mysite/mysite_uwsgi.ini /etc/uwsgi/vassals/
# run the emperor
uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data
You may need to run uWSGI with sudo:
sudo uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data
The options mean:
emperor
: where to look for vassals (config files)uid
: the user id of the process once it’s startedgid
: the group id of the process once it’s started
Check the site; it should be running.
Make uWSGI startup when the system boots¶
The last step is to make it all happen automatically at system startup time.
Edit /etc/rc.local
and add:
/usr/local/bin/uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data
before the line “exit 0”.
And that should be it!
Further configuration¶
It is important to understand that this has been a tutorial, to get you started. You do need to read the nginx and uWSGI documentation, and study the options available before deployment in a production environment.
Both nginx and uWSGI benefit from friendly communities, who are able to offer invaluable advice about configuration and usage.
nginx¶
General configuration of nginx is not within the scope of this tutorial though you’ll probably want it to listen on port 80, not 8000, for a production website.
You also ought to consider at having a separate server for non-Django serving, of static files for example.
uWSGI¶
uWSGI supports multiple ways to configure it. See uWSGI’s documentation and examples.
Some uWSGI options have been mentioned in this tutorial; others you ought to look at for a deployment in production include (listed here with example settings):
env = DJANGO_SETTINGS_MODULE=mysite.settings # set an environment variable
pidfile = /tmp/project-master.pid # create a pidfile
harakiri = 20 # respawn processes taking more than 20 seconds
limit-as = 128 # limit the project to 128 MB
max-requests = 5000 # respawn processes after serving 5000 requests
daemonize = /var/log/uwsgi/yourproject.log # background the process & log
Running python webapps on Heroku with uWSGI¶
Prerequisites: a Heroku account (on the cedar platform), git (on the local system) and the heroku toolbelt.
Note: you need a uWSGI version >= 1.4.6 to correctly run python apps. Older versions may work, but are not supported.
Preparing the environment¶
On your local system prepare a directory for your project:
mkdir uwsgi-heroku
cd uwsgi-heroku
git init .
heroku create
the last command will create a new heroku application (you can check it on the web dashboard).
For our example we will run the Werkzeug WSGI testapp, so we need to install the werkzeug package in addition to uWSGI.
First step is creating a requirements.txt file and tracking it with git.
The content of the file will be simply
uwsgi
werkzeug
Let’s track it with git
git add requirements.txt
Creating the uWSGI config file¶
Now we can create our uWSGI configuration file. Basically all of the features can be used on heroku
[uwsgi]
http-socket = :$(PORT)
master = true
processes = 4
die-on-term = true
module = werkzeug.testapp:test_app
memory-report = true
as you can see this is a pretty standard configuration. The only heroku-required options are –http-socket and –die-on-term.
The first is required to bind the uWSGI socket to the port requested by the Heroku system (exported via the environment variable PORT we can access with $(PORT))
The second one (–die-on-term) is required to change the default behaviour of uWSGI when it receive a SIGTERM (brutal realod, while Heroku expect a shutdown)
The memory-report option (as we are in a memory contrained environment) is a good thing.
Remember to track the file
git add uwsgi.ini
Preparing for the first commit/push¶
We now need the last step: creating the Procfile.
The Procfile is a file describing which commands to start. Generally (with other deployment systems) you will use it for every additional process required by your app (like memcached, redis, celery...), but under uWSGI you can continue using its advanced facilities to manage them.
So, the Procfile, only need to start your uWSGI instance:
web: uwsgi uwsgi.ini
Track it
git add Procfile
And finally let’s commit all:
git commit -a -m "first commit"
and push it (read: deploy) to Heroku:
git push heroku master
The first time it will requires a couple of minutes as it need to prepare your virtualenv and compile uWSGI.
Following push will be much faster.
Checking your app¶
Running heroku logs
you will be able to access uWSGI logs. You should get all of your familiar information, and eventually
some hint in case of problems.
Using another version of python¶
Heroku supports different python versions. By default (currently, february 2013), Python 2.7.3 is enabled.
If you need another version just create a runtime.txt in your repository with a string like that:
python-2.7.2
to use python 2.7.2
Remember to add/commit that in the repository.
Every time you change the python version, a new uWSGI binary is built.
Multiprocess or Multithread ?¶
It obviosuly depend on your app. But as we are on a memory-limited environment you can expect better memory usage with threads.
In addition to this, if you plan to put production-apps on Heroku be sure to understand how Dynos and their proxy works (it is very important. really)
Async/Greethreads/Coroutine ?¶
As always, do not trust people suggesting you to ALWAYS use some kind of async mode (like gevent). If your app is async-friendly you can obviously use gevent (it is built by default in recent uWSGI releases), but if you do not know that, remain with multiprocess (or multithread).
Harakiri¶
As said previously, if you plan to put production-apps on heroku, be sure to understand how dynos and their proxy works. Based on that, try to always set the harakiri parameters to a good value for your app. (do not ask for a default value, IT IS APP-DEPENDENT)
Static files¶
Generally, serving static files on Heroku is not a good idea (mainly from a design point of view). You could obviously have that need. In such a case remember to use uWSGI facilities for that, in particular offloading is the best way to leave your workers free while you serve big files (in addition to this remember that your static files must be tracked with git)
Adaptive process spawning¶
None of the supported algorithm are good for the Heroku approach and, very probably, it makes little sense to use a dynamic process number on such a platform.
Logging¶
If you plan to use heroku on production, remember to send your logs (via udp for example) on an external server (with persistent storage).
Check the uWSGI available loggers. Surely one will fit your need. (pay attention to security, as logs will fly in clear).
UPDATE: a udp logger with crypto features is on work.
Alarms¶
All of the alarms plugin should work without problems
The Spooler¶
As your app runs on a non-persistent filesystem, using the Spooler is a bad idea (you will easily lose tasks).
Mules¶
They can be used without problems
Signals (timers, filemonitors, crons...)¶
They all works, but do not rely on cron facilities, as heroku can kill/destroy/restarts your instances in every moment.
External daemons¶
The –attach-daemon option and its –smart variants work without problems. Just remember you are on a volatile filesystem and you are not free to bind on port/addresses as you may wish
Monitoring your app (advanced/hacky)¶
Albeit Heroku works really well with newrelic services, you always need to monitor the internals of your uWSGI instance.
Generally you enable the stats subsystem with a tool like uwsgitop as the client.
You can simply add uwsgitop to you requirements.txt
uwsgi
uwsgitop
werkzeug
and enable the stats server on a TCP port (unix sockets will not work as the instance running uwsgitop is not on the same server !!!):
[uwsgi]
http-socket = :$(PORT)
master = true
processes = 4
die-on-term = true
module = werkzeug.testapp:test_app
memory-report = true
stats = :22222
Now we have a problem: how to reach our instance ?
We need to know the LAN address of the machine where our instance is phisically running. To accomplish that, a raw trick is running ifconfig on uWSGI startup:
[uwsgi]
http-socket = :$(PORT)
master = true
processes = 4
die-on-term = true
module = werkzeug.testapp:test_app
memory-report = true
stats = :22222
exec-pre-app = /sbin/ifconfig eth0
Now thanks to the heroku logs
command you can know where your stats server is
heroku run uwsgitop 10.x.x.x:22222
change x.x.x with the discovered address and remember that you could not be able to bind on port 22222, so change it accordingly.
Is it worthy to make such a mess to get monitoring ? If you are testing your app before going to production, it could be a good idea, but if you plan to buy more dynos, all became so complex that you’d better to use some heroku-blessed technique (if any)
Running Ruby/Rack webapps on Heroku with uWSGI¶
Prerequisites: a Heroku account (on the cedar platform), git (on the local system) and the heroku toolbelt (or the old/deprecated heroku gem)
Note: you need a uWSGI version >= 1.4.8 to correctly run ruby/rack apps. Older versions may work, but are not supported.
Preparing the environment (a Sinatra application)¶
On your local system prepare the structure for your sinatra application
mkdir uwsgi-heroku
cd uwsgi-heroku
git init .
heroku create --stack cedar
the last command will create a new heroku application (you can check it on the web dashboard).
Next step is creating our Gemfile (this file containes the gem required by the application)
source 'https://rubygems.org'
gem "uwsgi"
gem "sinatra"
we now need to run bundle install
to create the Gemfile.lock file
let’s track the two with git:
git add Gemfile
git add Gemfile.lock
Finally create a config.ru file containing the Sinatra sample app
require 'sinatra'
get '/hi' do
return "ciao"
end
run Sinatra::Application
and track it
git add config.ru
Creating the uWSGI config file¶
We are now ready to create the uWSGI configuration (we will use the .ini format in a file called uwsgi.ini).
The minimal setup for heroku is the following (check the comments in the file for an explanation)
[uwsgi]
; bind to the heroku required port
http-socket = :$(PORT)
; force the usage of the ruby/rack plugin for every request (7 is the official numbero for ruby/rack)
http-socket-modifier1 = 7
; load the bundler subsystem
rbrequire = bundler/setup
; load the application
rack = config.ru
; when the app receives the TERM signal let's destroy it (instead of brutal reloading)
die-on-term = true
but a better setup will be
[uwsgi]
; bind to the heroku required port
http-socket = :$(PORT)
; force the usage of the ruby/rack plugin for every request (7 is the official numbero for ruby/rack)
http-socket-modifier1 = 7
; load the bundler subsystem
rbrequire = bundler/setup
; load the application
rack = config.ru
; when the app receives the TERM signal let's destroy it (instead of brutal reloading)
die-on-term = true
; enable the master process
master = true
; spawn 4 processes to increase concurrency
processes = 4
; report memory usage after each request
memory-report = true
; reload if the rss memory is higher than 100M
reload-on-rss = 100
Let’s track it
git add uwsgi.ini
Deploying to heroku¶
We need to create the last file (required by Heroku). It is the Procfile, that instruct the Heroku system on which process to start for a web application.
We want to spawn uwsgi (installed as a gem via bundler) using the uwsgi.ini config file
web: bundle exec uwsgi uwsgi.ini
track it
git add Procfile
And let’s commit all:
git commit -a -m "first attempt"
And push to heroku:
git push heroku master
If all goes well, you will see your page under your app url on the /hi path
Remember to run heroku logs
to check if all is ok.
fork() for dummies¶
uWSGI allows you to choose how to abuse the fork() syscall in your app.
By default the approach is loading the application in the master process and then fork() to the workers that will inherit a copy of the master process.
This approach speedup startup and can potentially consume less memory. The truth is that often (for the way ruby garbage collection works) you will get few memory gain. The real advantage in in performance as the vast majority of time during application startup is spent in (slowly) searching for files. With the fork() early approach you can avoid repeating that slow procedure one time for worker.
Obviously the uWSGI mantra is “do whatever you need, if you can’t, it is a uWSGI bug” so if your app is not fork()-friendly
you can add the lazy-apps = true
option that will load your app one time per-worker.
The ruby GC¶
By default uWSGI, calls the ruby Garbage collector after each request. This ensure an optimal use of memory (remember on Heroku, your memory is limited) you should not touch
the default approach, but if you experience a drop in performance you may want to tune it using the ruby-gc-freq = n
option
where n is the number of requests after the GC is called.
Concurrency¶
Albeit uWSGI supports lot of different paradigms for concurrency, the multiprocess one is suggested for the vast majority of ruby/rack apps.
Basically all popular ruby-frameworks rely on that. Remember that your app is limited so spawn a number of processes that can fit in your Heroku dyno.
Starting from uWSGI 1.9.14, native ruby 1.9/2.x threads support has been added. Rails4 (only in production mode !!!) supports them:
[uwsgi]
...
; spawn 8 threads per-process
threads = 8
; maps them as ruby threads
rbthreads = true
; do not forget to set production mode for rails4 apps !!!
env = RAILS_ENV=production
...
Harakiri¶
If you plan to put production-apps on heroku, be sure to understand how dynos and their proxy works. Based on that, try to always set the harakiri parameters to a good value for your app. (do not ask for a default value, IT IS APP-DEPENDENT)
Harakiri, is the maximum time a single request can run, before being destroyed by the master
Static files¶
Generally, serving static files on Heroku is not a good idea (mainly from a design point of view). You could obviously have that need. In such a case remember to use uWSGI facilities for that, in particular offloading is the best way to leave your workers free while you serve big files (in addition to this remember that your static files must be tracked with git)
Try to avoid serving static files from your ruby/rack code. It will be extremely slow (compared to the uWSGI facilities) and can hold your worker busy for the whole transfer of the file
Adaptive process spawning¶
None of the supported algorithms are good for the Heroku approach and, very probably, it makes little sense to use a dynamic process number on such a platform.
Logging¶
If you plan to use heroku on production, remember to send your logs (via udp for example) on an external server (with persistent storage).
Check the uWSGI available loggers. Surely one will fit your need. (pay attention to security, as logs will fly in clear).
UPDATE: a udp logger with crypto features is on work.
Alarms¶
All of the alarms plugin should work without problems
The Spooler¶
As your app runs on a non-persistent filesystem, using the Spooler is a bad idea (you will easily lose tasks).
Mules¶
They can be used without problems
Signals (timers, filemonitors, crons...)¶
They all works, but do not rely on cron facilities, as heroku can kill/destroy/restarts your instances in every moment.
External daemons¶
The –attach-daemon option and its –smart variants work without problems. Just remember you are on a volatile filesystem and you are not free to bind on port/addresses as you may wish
Reliably use FUSE filesystems for uWSGI vassals (with Linux)¶
Requirements: uWSGI 1.9.18, Linux kernel with FUSE and namespaces support.
FUSE is a technology allowing the implementation of filesystems in user space (hence the name: Filesystem in Userspace). There are hundreds of high-quality FUSE filesystems, so having your application relying on them is a common situation.
FUSE filesystems are normal system processes, so as any process in the system, they can crash (or you may involuntarily kill them). In addition to this, if you host multiple applications, each one requiring a FUSE mount point, you may want to avoid polluting the main mount points namespace and, more important, avoid having unused mount points in your system (i.e. an instance is completely removed and you do not want its FUSE mount point to be still available in the system).
The purpose of this tutorial is to configure an Emperor and a series of vassals, each one mounting a FUSE filesystem.
A Zip filesystem¶
fuse-zip is a FUSE process exposing a zip file as a filesystem.
Our objective is to store whole app in a zip archive and instruct uWSGI to mount it as a filesystem (via FUSE) under /app
.
The Emperor¶
[uwsgi]
emperor = /etc/uwsgi/vassals
emperor-use-clone = fs,pid
The trick here is to use Linux namespaces to create vassals in a new pid and filesystem namespace.
The first one (fs
) allows mount point created by the vassal to be available only to the vassal (without messing with the main system), while the pid
allows the uWSGI master to be the “init” process (pid 1) of the vassal. Being “pid 1” means that when you die all your children die too. In our scenario (where our vassal launches a FUSE process on startup) it means that when
the vassal is destroyed, the FUSE process is destroyed too, as well as its mount point.
A Vassal¶
[uwsgi]
uid = user001
gid = user001
; mount FUSE filesystem under /app (but only if it is not a reload)
if-not-reload =
exec-as-user = fuse-zip -r /var/www/app001.zip /app
endif =
http-socket = :9090
psgi = /app/myapp.pl
Here we use the -r
option of the fuse-zip
command for a read-only mount.
Monitoring mount points¶
The problem with the current setup is that if the fuse-zip
process dies, the instance will no more be able to access /app
until it is respawned.
uWSGI 1.9.18 added the --mountpoint-check
option. It forces the master to constantly verify the specified filesystem. If it fails, the whole instance will be brutally destroyed.
As we are under The Emperor, soon after the vassal is destroyed it will be restarted in a clean state (allowing the FUSE mount point to be started again).
[uwsgi]
uid = user001
gid = user001
; mount FUSE filesystem under /app (but only if it is not a reload)
if-not-reload =
exec-as-user = fuse-zip -r /var/www/app001.zip /app
endif =
http-socket = :9090
psgi = /app/myapp.pl
mountpoint-check = /app
Going Heavy Metal: A CoW rootfs (unionfs-fuse)¶
unionfs-fuse is a user-space implementation of a union filesystem. A union filesystem is a stack of multiple filesystems, so directories with same name are merged into a single view.
Union filesystems are more than this and one of the most useful features is copy-on-write (COW or CoW). Enabling CoWs means you will have an immutable/read-only mount point base and all of the modifications to it will go to another mount point.
Our objective is to have a read-only rootfs shared by all of our customers, and a writable mount point (configured as CoW) for each customer, in which every modification will be stored.
The Emperor¶
Previous Emperor configuration can be used, but we need to prepare our filesystems.
The layout will be:
/ufs (where we initially mount our unionfs for each vassal)
/ns
/ns/precise (the shared rootfs, based on Ubuntu Precise Pangolin)
/ns/lucid (an alternative rootfs for old-fashioned customers, based on Ubuntu Lucid Lynx)
/ns/saucy (another shared rootfs, based on Ubuntu Saucy Salamander)
/ns/cow (the customers' writable areas)
/ns/cow/user001
/ns/cow/user002
/ns/cow/userXXX
...
We create our rootfs:
debootstrap precise /ns/precise
debootstrap lucid /ns/lucid
debootstrap saucy /ns/saucy
And we create the .old_root
directory in each one (it is required for pivot_root
, see below):
mkdir /ns/precise/.old_root
mkdir /ns/lucid/.old_root
mkdir /ns/saucy/.old_root
Be sure to install the required libraries into each of them (especially the libraries required for your language).
The uwsgi
binary must be executable in this rootfs, so you have to invest a bit of time in it (a good approach is having a language plugin
compiled for each distribution and placed into a common directory, for example, each rootfs could have an /opt/uwsgi/plugins/psgi_plugin.so
file and so on).
A Vassal¶
Here things get a bit more complicated. We need to launch the unionfs process (as root, as it must be our new rootfs) and then call pivot_root
(a more advanced chroot
available on Linux).
Hooks are the best way to run custom commands (or functions) at various uWSGI startup phases.
In our example we will run FUSE processes at the “pre-jail” phase, and deal with mount points at the “as-root” phase (that happens after pivot_root
).
[uwsgi]
; choose the approach that suits you best (plugins loading)
; this will be used for the first run ...
plugins-dir = /ns/precise/opt/uwsgi/plugins
; and this after a reload (where our rootfs is already /ns/precise)
plugins-dir = /opt/uwsgi/plugins
plugin = psgi
; drop privileges
uid = user001
gid = user001
; chdir to / to avoid problems after pivot_root
hook-pre-jail = callret:chdir /
; run unionfs-fuse using chroot (it is required to avoid deadlocks) and cow (we mount it under /ufs)
hook-pre-jail = exec:unionfs-fuse -ocow,chroot=/ns,default_permissions,allow_other /precise=RO:/cow/%(uid)=RW /ufs
; change the rootfs to the unionfs one
; the .old_root directory is where the old rootfs is still available
pivot_root = /ufs /ufs/.old_root
; now we are in the new rootfs and in 'as-root' phase
; remount the /proc filesystem
hook-as-root = mount:proc none /proc
; bind mount the original /dev in the new rootfs (simplifies things a lot)
hook-as-root = mount:none /.old_root/dev /dev bind
; recursively un-mount the old rootfs
hook-as-root = umount:/.old_root rec,detach
; common bind
http-socket = :9090
; load the app (fix it according to your requirements)
psgi = /var/www/myapp.pl
; constantly check for the rootfs (seems odd but is is very useful)
mountpoint-check = /
If your app will try to write to its filesystem, you will see that all
of the created/updated files are available in its /cow
directory.
Notes¶
Some FUSE filesystems do not commit writes until they are unmounted. In such a case unmounting on vassal shutdown is a good trick:
[uwsgi]
; vassal options ...
...
; umount on exit
exec-as-user-atexit = fusermount -u /app
Build a dynamic proxy using RPC and internal routing¶
Work in progress (requires uWSGI 1.9.14, we use PyPy as the engine)
step 1: build your mapping function¶
we use the hostname as the mapping (you can use whatever you need)
import uwsgi
def my_mapper(hostname):
return "127.0.0.1:3031"
uwsgi.register_rpc('the_mapper', my_mapper)
save it as myfuncs.py
step 2: building a routing table¶
[uwsgi]
; enable the pypy engine
pypy-home = /opt/pypy
; execute the myfuncs.py file (the 'the_mapper' rpc function will be registered)
pypy-exec = myfuncs.py
; bind to a port
http-socket = :9090
; let's define our routing table
; at every request (route-run execute the action without making check, use it instead of --route .*) run the_mapper passing HTTP_HOST as argument
; and place the result in the MYNODE variable
route-run = rpcvar:MYNODE the_mapper ${HTTP_HOST}
; print the MYNODE variable (just for fun)
route-run = log:${MYNODE}
; proxy the request to the choosen backend node
route-run = http:${MYNODE}
; enable offloading for automagic non-blocking behaviour
; a good value for offloading is the number of cpu cores
offload-threads = 2
Setting up Graphite on Ubuntu using the Metrics subsystem¶
This tutorial will guide you in installing a multi-app server, with each application sending metrics to a central graphite/carbon server.
Graphite is available here: http://graphite.wikidot.com/
The uWSGI Metrics subsystem is documented here The Metrics subsystem
The tutorial assumes an Ubuntu Saucy (13.10) release on amd64
While for Graphite we will use Ubuntu official packages, uWSGI core and plugins will be downloaded and installed from official sources
Installing Graphite and the others needed packages¶
sudo apt-get install python-dev ruby-dev bundler build-essential libpcre3-dev graphite-carbon graphite-web
python-dev and ruby-dev are required as we want to support both WSGI and Rack apps.
pcre development headers allow you to build uWSGI with internal routing support (something you always want)
Initializing Graphite¶
The first step will be enabling th Carbon server.
The Graphite project is composed by three subsystems: whisper, carbon and the web frontend
Whisper is a data storage format (similar to rrdtool)
Carbon is the server gathering metrics and storing them in whisper files (well it does more, but this is its main purpose)
The web frontend visualize the charts/graphs built from the data gathered by the carbon server.
To enable the carbon server edit /etc/default/graphite-carbon
and set CARBON_CACHE_ENABLED to true
Before starting the carbon server we need to build its search index.
Just run:
sudo /usr/bin/graphite-build-search-index
Then start the carbon server (at the next reboot it will be automatically started)
sudo /etc/init.d/carbon-cache start
Building and Installing uWSGI¶
Download latest stable uWSGI tarball
wget http://projects.unbit.it/downloads/uwsgi-latest.tar.gz
explode it, and from the created directory run:
python uwsgiconfig.py --build core
this will build the uWSGI “core” binary.
We now want to build the python, rack and carbon plugins:
python uwsgiconfig.py --plugin plugins/python core
python uwsgiconfig.py --plugin plugins/rack core
python uwsgiconfig.py --plugin plugins/carbon core
now we have uwsgi
, python_plugin.so
, rack_plugin.so
and carbon_plugin.so
let’s copy it to system directories:
sudo mkdir /etc/uwsgi
sudo mkdir /usr/lib/uwsgi
sudo cp uwsgi /usr/bin/uwsgi
sudo cp python_plugin.so /usr/lib/uwsgi
sudo cp rack_plugin.so /usr/lib/uwsgi
sudo cp carbon_plugin.so /usr/lib/uwsgi
Setting up the uWSGI Emperor¶
Create an upstart config file for starting The uWSGI Emperor – multi-app deployment
# Emperor uWSGI script
description "uWSGI Emperor"
start on runlevel [2345]
stop on runlevel [06]
exec /usr/bin/uwsgi --emperor /etc/uwsgi
save it as /etc/init/emperor.conf
and start the Emperor:
start emperor
From now on, to start uWSGI instances just drop their config files into /etc/uwsgi
Spawning the Graphite web interface¶
Before starting the graphite web interface (that is a Django app) we need to initialize its database.
Just run:
sudo graphite-manage syncdb
this is the standard django syncdb command for manage.py. Just answer the questions to create an admin user.
Now we are ready to create a uWSGI vassal:
[uwsgi]
plugins-dir = /usr/lib/uwsgi
plugins = python
uid = _graphite
gid = _graphite
wsgi-file = /usr/share/graphite-web/graphite.wsgi
http-socket = :8080
Save it as /etc/uwsgi/graphite.ini
the _graphite user (and group) is created by the graphite ubuntu package. Our uWSGI vassal will run under this privileges.
The web interface will be available on the port 8080 of your server natively speaking HTTP. If you prefer to proxy it,
just change http-socket
to http
or place it behind a full webserver like nginx (this step is not covered in this tutorial)
Spawning vassals sending metrics to Graphite¶
We are now ready to send applications metrics to the carbon/graphite server.
For every vassal file in /etc/uwsgi just be sure to add the following options:
[uwsgi]
...
plugins = carbon
enable-metrics = true
carbon-use-metrics = true
carbon-id = %n
carbon = 127.0.0.1:2003
...
The carbon-id
set a meaningful prefix to each metric (%n automatically translates to the name without extension of the vassal file).
The carbon
option set the address of the carbon server to send metrics to (by default the carbon server binds on port 2003, but you can change it editing
/etc/carbon/carbon.conf
and restarting the carbon server)
Using Graphiti (Ruby/Sinatra based) as alternative frontend¶
Graphiti is an alternative dashboard/frontend from Graphite writte in Sinatra (a Ruby/Rack framework).
Graphiti requires redis, so be sure a redis server is running in your system.
Running:
sudo apt-get install redis-server
will be enough
First step is cloning the graphiti app (place it where you want/need):
git clone https://github.com/paperlesspost/graphiti.git
then run the bundler tool (if you are not confident with the ruby world it is a tool for managing dependencies)
bundle install
注解
if the eventmachine gem installation fails, add “gem ‘eventmachine’” in the Gemfile as the first gem and run bundle update. This will ensure latest eventmachine version will be installed
After bundle has installed all of the gems, you have to copy the graphiti example configuration:
cp config/settings.yml.example config/settings.yml
edit it and set graphite_base_url to the url where the graphite web interface (the django one) is running.
Now we can deploy it on uWSGI
[uwsgi]
plugins-dir = /usr/lib/uwsgi
plugins = rack
chdir = <path_to_graphiti>
rack = config.ru
rbrequire = bundler/setup
http-socket = :9191
uid = _graphite
gid = _graphite
save it as /etc/uwsgi/graphiti.ini
to let the Emperor deploy it
You can now connect to port 9191 to manage your gathered metrics.
As always you are free to place the instance under a proxy.
Notes¶
By default the carbon server listens on a public address. Unless you know what you are doing you should point it to a local one (like 127.0.0.1)
uWSGI exports a gazillion of metrics (and more are planned), do not be afraid to use them
There is no security between apps and the carbon server, any apps can write metrics to it. If you are hosting untrusted apps you’d better to use other approcahes (like giving a graphite instance to every user in the system)
The same is true for redis, if you run untrusted apps a shared redis instance is absolutely not a good choice from a secuity point of view
Articles¶
Serializing accept(), AKA Thundering Herd, AKA the Zeeg Problem¶
One of the historical problems in the UNIX world is the “thundering herd”.
What is it?
Take a process binding to a networking address (it could be AF_INET
,
AF_UNIX
or whatever you want) and then forking itself:
int s = socket(...)
bind(s, ...)
listen(s, ...)
fork()
After having forked itself a bunch of times, each process will generally start
blocking on accept()
for(;;) {
int client = accept(...);
if (client < 0) continue;
...
}
The funny problem is that on older/classic UNIX, accept()
is woken up in
each process blocked on it whenever a connection is attempted on the socket.
Only one of those processes will be able to truly accept the connection, the
others will get a boring EAGAIN
.
This results in a vast number of wasted cpu cycles (the kernel scheduler has to give control to all of the sleeping processes waiting on that socket).
This behaviour (for various reasons) is amplified when instead of processes you
use threads (so, you have multiple threads blocked on accept()
).
The de facto solution was placing a lock before the accept()
call to serialize
its usage:
for(;;) {
lock();
int client = accept(...);
unlock();
if (client < 0) continue;
...
}
For threads, dealing with locks is generally easier but for processes you have to fight with system-specific solutions or fall back to the venerable SysV ipc subsystem (more on this later).
In modern times, the vast majority of UNIX systems have evolved, and now the kernel ensures (more or less) only one process/thread is woken up on a connection event.
Ok, problem solved, what we are talking about?
select()/poll()/kqueue()/epoll()/...¶
In the pre-1.0 era, uWSGI was a lot simpler (and less interesting) than the
current form. It did not have the signal framework and it was not able to
listen to multiple addresses; for this reason its loop engine was only calling
accept()
in each process/thread, and thundering herd (thanks to modern
kernels) was not a problem.
Evolution has a price, so after a while the standard loop engine of a uWSGI process/thread moved from:
for(;;) {
int client = accept(s, ...);
if (client < 0) continue;
...
}
to a more complex:
for(;;) {
int interesting_fd = wait_for_fds();
if (fd_need_accept(interesting_fd)) {
int client = accept(interesting_fd, ...);
if (client < 0) continue;
}
else if (fd_is_a_signal(interesting_fd)) {
manage_uwsgi_signal(interesting_fd);
}
...
}
The problem is now the wait_for_fds()
example function: it will call
something like select()
, poll()
or the more modern epoll()
and
kqueue()
.
These kinds of system calls are “monitors” for file descriptors, and they are woken up in all of the processes/threads waiting for the same file descriptor.
Before you start blaming your kernel developers, this is the right approach, as
the kernel cannot know if you are waiting for those file descriptors to call
accept()
or to make something funnier.
So, welcome again to the thundering herd.
Application Servers VS WebServers¶
The popular, battle tested, solid, multiprocess reference webserver is Apache HTTPD.
It survived decades of IT evolutions and it’s still one of the most important technologies powering the whole Internet.
Born as multiprocess-only, Apache had to always deal with the thundering herd problem and they solved it using SysV ipc semaphores.
(Note: Apache is really smart about that, when it only needs to wait on a
single file descriptor, it only calls accept()
taking advantage of modern
kernels anti-thundering herd policies)
(Update: Apache 2.x even allows you to choose which lock technique to use, included flock/fcntl for very ancient systems, but on the vast majority of the system, when in multiprocess mode it will use the sysv semaphores)
Even on modern Apache releases, stracing one of its process (bound to multiple interfaces) you will see something like that (it is a Linux system):
semop(...); // lock
epoll_wait(...);
accept(...);
semop(...); // unlock
... // manage the request
the SysV semaphore protect your epoll_wait from thundering herd.
So, another problem solved, the world is a such a beatiful place... but ....
SysV IPC is not good for application servers :(*
The definition of “application server” is pretty generic, in this case we refer to one or more process/processes generated by an unprivileged (non-root) user binding on one or more network address and running custom, highly non-deterministic code.
Even if you had a minimal/basic knowledge on how SysV IPC works, you will know each of its components is a limited resource in the system (and in modern BSDs these limits are set to ridiculously low values, PostgreSQL FreeBSD users know this problem very well).
Just run ‘ipcs’ in your terminal to get a list of the allocated objects in your kernel. Yes, in your kernel. SysV ipc objects are persistent resources, they need to be removed manually by the user. The same user that could allocate hundreds of those objects and fill your limited SysV IPC memory.
One of the most common problems in the Apache world caused by the SysV ipc usage is the leakage when you brutally kills Apache instances (yes, you should never do it, but you don’t have a choice if you are so brave/fool to host unreliable PHP apps in your webserver process).
To better understand it, spawn Apache and killall -9 apache2
. Respawn it
and run ‘ipcs’ you will get a new semaphore object every time. Do you see the
problem? (to Apache gurus: yes I know there are hacky tricks to avoid that,
but this is the default behaviour)
Apache is generally a system service, managed by a conscious sysadmin, so except few cases you can continue trusting it for more decades, even if it decides to use more SysV ipc objects :)
Your application server, sadly, is managed by different kind of users, from the most skilled one to the one who should change job as soon as possible to the one with the site cracked by a moron wanting to take control of your server.
Application servers are not dangerous, users are. And application servers are run by users. The world is an ugly place.
How application server developers solved it¶
Fast answer: they generally do not solve/care it
Note: we are talking about multiprocessing, we have already seen multithreading is easy to solve.
Serving static files or proxying (the main activities of a webserver) is generally a fast, non-blocking (very deterministic under various points of view) activity. Instead, a web application is way slower and heavier, so, even on moderately loaded sites, the amount of sleeping processes is generally low.
On highly loaded sites you will pray for a free process, and in non-loaded sites the thundering herd problem is completely irrelevant (unless you are running your site on a 386).
Given the relatively low number of processes you generally allocate for an application server, we can say thundering herd is a no-problem.
Another approach is dynamic process spawning. If you ensure your application server has always the minimum required number of processes running you will highly reduce the thundering herd problem. (check the family of –cheaper uWSGI options)
No-problem ??? So, again, what we are talking about ?¶
We are talking about “common cases”, and for common cases there are a plethora of valid choices (instead of uWSGI, obviously) and the vast majority of problems we are talking about are non-existent.
Since the beginning of the uWSGI project, being developed by a hosting company where “common cases” do not exist, we cared a lot about corner-case problems, bizarre setups and those problems the vast majority of users never need to care about.
In addition to this, uWSGI supports operational modes only common/available in general-purpose webservers like Apache (I have to say Apache is probably the only general purpose webserver as it allows basically anything in its process space in a relatively safe and solid way), so lot of new problems combined with user bad-behaviour arise.
One of the most challenging development phase of uWSGI was adding multithreading. Threads are powerful, but are really hard to manage in the right way.
Threads are way cheaper than processes, so you generally allocate dozens of them for your app (remember, not used memory is wasted memory).
Dozens (or hundreds) of threads waiting for the same set of file descriptors bring us back to a thundering herd problem (unless all of your threads are constantly used).
For such a reason when you enable multiple threads in uWSGI a pthread mutex is allocated, serializing epoll()/kqueue()/poll()/select()... usage in each thread.
Another problem solved (and strange for uWSGI, without the need of an option ;)
But...
The Zeeg problem: Multiple processes with multiple threads¶
On June 27, 2013, David Cramer wrote an interesting blog post (you may not agree with its conclusions, but it does not matter now, you can continue hating uWSGI safely or making funny jokes about its naming choices or the number of options).
http://justcramer.com/2013/06/27/serving-python-web-applications/
The problem David faced was such a strong thundering herd that its response time was damaged by it (non constant performance was the main result of its tests).
Why did it happen? Wasn’t the mutex allocated by uWSGI solving it?
David is (was) running uWSGI with 10 process and each of them with 10 threads:
uwsgi --processes 10 --threads 10 ...
While the mutex protects each thread in a single process to call accept()
on the same request, there is no such mechanism (or better, it is not enabled
by default, see below) to protect multiple processes from doing it, so given
the number of threads (100) available for managing requests, it is unlikely
that a single process is completely blocked (read: with all of its 10 threads
blocked in a request) so welcome back to the thundering herd.
How David solved it ?¶
uWSGI is a controversial piece of software, no shame in that. There are users fiercely hating it and others morbidly loving it, but all agree that docs could be way better ([OT] it is good when all the people agree on something, but pull requests on uwsgi-docs are embarrassingly low and all from the same people.... come on, help us !!!)
David used an empirical approach, spotted its problem and decided to solve it running independent uwsgi processes bound on different sockets and configured nginx to round robin between them.
It is a very elegant approach, but it has a problem: nginx cannot know if the process on which is sending the request has all of its thread busy. It is a working but suboptimal solution.
The best way would be having an inter-process locking (like Apache),
serializing all of the accept()
in both threads and processes
uWSGI docs sucks: –thunder-lock¶
Michael Hood (you will find his name in the comments of David’s post, too)
signalled the problem in the uWSGI mailing-list/issue tracker some time ago, he
even came out with an initial patch that ended with the --thunder-lock
option (this is why open-source is better ;)
--thunder-lock
is available since uWSGI 1.4.6 but never got documentation (of
any kind)
Only the people following the mailing-list (or facing the specific problem) know about it.
SysV IPC semaphores are bad how you solved it ?¶
Interprocess locking has been an issue since uWSGI 0.0.0.0.0.1, but we solved it in the first public release of the project (in 2009).
We basically checked each operating system capabilities and chose the best/fastest ipc locking they could offer, filling our code with dozens of #ifdef.
When you start uWSGI you should see in its logs which “lock engine” has been chosen.
There is support for a lot of them:
- pthread mutexes with _PROCESS_SHARED and _ROBUST attributes (modern Linux and Solaris)
- pthread mutexes with _PROCESS_SHARED (older Linux)
- OSX Spinlocks (MacOSX, Darwin)
- Posix semaphores (FreeBSD >= 9)
- Windows mutexes (Windows/Cygwin)
- SysV IPC semaphores (fallback for all the other systems)
Their usage is required for uWSGI-specific features like caching, rpc and all of those features requiring changing shared memory structures (allocated with mmap() + _SHARED)
Each of these engines is different from the others and dealing with them has been a pain and (more important) some of them are not “ROBUST”.
The “ROBUST” term is pthread-borrowed. If a lock is “robust”, it means if the process locking it dies, the lock is released.
You would expect it from all of the lock engines, but sadly only few of them works reliably.
For this reason the uWSGI master process has to allocate an additional thread (the ‘deadlock’ detector) constantly checking for non-robust unreleased locks mapped to dead processes.
It is a pain, however, anyone will tell you IPC locking is easy should be accepted in a JEDI school...
uWSGI developers are fu*!ing cowards¶
Both David Cramer and Graham Dumpleton (yes, he is the mod_wsgi author but
heavily contributed to uWSGI development as well to the other WSGI servers,
this is another reason why open source is better) asked why --thunder-lock
is not the default when multiprocess + multithread is requested.
This is a good question with a simple answer: we are cowards who only care about money.
uWSGI is completely open source, but its development is sponsored (in various way) by the companies using it and by Unbit.it customers.
Enabling “risky” features by default for a “common” usage (like multiprocess+multithread) is too much for us, and in addition to this, the situation (especially on linux) of library/kernel incompatibilities is a real pain.
As an example for having ROBUST pthread mutexes you need a modern kernel with a modern glibc, but commonly used distros (like the centos family) have a mix of older kernels with newer glibc and the opposite too. This leads to the inability to correctly detect which is the best locking engine for a platform, and so, when the uwsgiconfig.py script is in doubt it falls back to the safest approach (like non-robust pthread mutexes on linux).
The deadlock-detector should save you from most of the problem, but the “should” word is the key. Making a test suite (or even a single unit test) on this kind of code is basically impossible (well, at least for me), so we cannot be sure all is in the right place (and reporting threading bugs is hard for users as well as skilled developer, unless you work on pypy ;)
Linux pthread robust mutexes are solid, we are “pretty” sure about that, so you
should be able to enable --thunder-lock
on modern Linux systems with a
99.999999% success rates, but we prefer (for now) users consciously enable it
When SysV IPC semaphores are a better choice¶
Yes, there are cases on which SysV IPC semaphores gives you better results than system-specific features.
Marcin Deranek of Booking.com has been battle-testing uWSGI for months and helped us with fixing corner-case situations even in the locking area.
He noted system-specific lock-engines tend to favour the kernel scheduler (when choosing which process wins the next lock after an unlock) instead of a round-robin distribution.
As for their specific need for an equal distribution of requests among processes is better (they use uWSGI with perl, so no threading is in place, but they spawn lot of processes) they (currently) choose to use the “ipcsem” lock engine with:
uwsgi --lock-engine ipcsem --thunder-lock --processes 100 --psgi ....
The funny thing (this time) is that you can easily test if the lock is working well. Just start blasting the server and you will see in the request logs how the reported pid is different each time, while with system-specific locking the pids are pretty random with a pretty heavy tendency of favouring the last used process.
Funny enough, the first problem they faced was the ipcsem leakage (when you are in emergency, graceful reload/stop is your enemy and kill -9 will be your silver bullet)
To fix it, the –ftok option is available allowing you to give a unique id to the semaphore object and to reuse it if it is available from a previous run:
uwsgi --lock-engine ipcsem --thunder-lock --processes 100 --ftok /tmp/foobar --psgi ....
–ftok takes a file as an argument, it will use it to build the unique id. A common pattern is using the pidfile for it
What about other portable lock engines ?¶
In addition to “ipcsem”, uWSGI (where available) adds “posixsem” too.
They are used by default only on FreeBSD >= 9, but are available on Linux too.
They are not “ROBUST”, but they do not need shared kernel resources, so if you trust our deadlock detector they are a pretty-good approach. (Note: Graham Dumpleton pointed me to the fact they can be enabled on Apache 2.x too)
Conclusions¶
You can have the best (or the worst) software of the whole universe, but without docs it does not exist.
The Apache team still slam the face of the vast majority of us trying to touch their market share :)
Bonus chapter: using the Zeeg approach in a uWSGI friendly way¶
I have to admit, I am not a big fan of supervisord. It is a good software without doubts, but I consider the Emperor and the –attach-daemon facilities a better approach to the deployment problems. In addition to this, if you want to have a “scriptable”/”extendable” process supervisor I think Circus (http://circus.readthedocs.org/) is a lot more fun and capable (the first thing I have done after implementing socket activation in the uWSGI Emperor was making a pull request [merged, if you care] for the same feature in Circus).
Obviously supervisord works and is used by lot of people, but as a heavy uWSGI user I tend to abuse its features to accomplish a result.
The first approach I would use is binding to 10 different ports and mapping each of them to a specific process:
[uwsgi]
processes = 5
threads = 5
; create 5 sockets
socket = :9091
socket = :9092
socket = :9093
socket = :9094
socket = :9095
; map each socket (zero-indexed) to the specific worker
map-socket = 0:1
map-socket = 1:2
map-socket = 2:3
map-socket = 3:4
map-socket = 4:5
Now you have a master monitoring 5 processes, each one bound to a different
address (no --thunder-lock
needed)
For the Emperor fanboys you can make such a template (call it foo.template):
[uwsgi]
processes = 1
threads = 10
socket = :%n
Now make a symbolic link for each instance+port you want to spawn:
ln -s foo.template 9091.ini
ln -s foo.template 9092.ini
ln -s foo.template 9093.ini
ln -s foo.template 9094.ini
ln -s foo.template 9095.ini
ln -s foo.template 9096.ini
Bonus chapter 2: securing SysV IPC semaphores¶
My company hosting platform in heavily based on Linux cgroups and namespaces.
The first (cgroups) are used to limit/account resource usage, while the second (namespaces) are used to give an “isolated” system view to users (like seeing a dedicated hostname or root filesystem).
As we allow users to spawn PostgreSQL instances in their accounts we need to limit SysV objects.
Luckily, modern Linux kernels have a namespace for IPC, so calling unshare(CLONE_NEWIPC) will create a whole new set (detached from the others) of IPC objects.
Calling --unshare ipc
in customer-dedicated Emperors is a common approach.
When combined with memory cgroup you will end with a pretty secure setup.
The Art of Graceful Reloading¶
Author: Roberto De Ioris
The following article is language-agnostic, and albeit uWSGI-specific, some of its initial considerations apply to other application servers and platforms too.
All of the described techniques assume a modern (>= 1.4) uWSGI release with the master process enabled.
What is a “graceful reload”?¶
During the life-cycle of your webapp you will reload it hundreds of time.
You need reloading for code updates, you need reloading for changes in the uWSGI configuration, you need reloading to reset the state of your app.
Basically, reloading is one of the most simple, frequent and dangerous operation you do every time.
So, why “graceful”?
Take a traditional (and highly suggested) architecture: a proxy/load balancer (like nginx) forwards requests to one or more uWSGI daemons listening on various addresses.
If you manage your reloads as “stop the instance, start the instance”, the time slice between two phases will result in a brutal disservice for your customers.
The main trick for avoiding it is: not closing the file descriptors mapped to
the uWSGI daemon addresses and abusing the Unix fork()
behaviour (read:
file descriptors are inherited by default) to exec()
the uwsgi
binary
again.
The result is your proxy enqueuing requests to the socket until the latter
will be able to accept()
them again, with the user/customer only seeing
a little slowdown in the first response (the time required for the app to be
fully loaded again).
Another important step of graceful reload is to avoid destroying workers/threads that are still managing requests. Obviously requests could be stuck, so you should have a timeout for running workers (in uWSGI it is called the “worker’s mercy” and it has a default value of 60 seconds).
These kind of tricks are pretty easy to accomplish and basically all of the modern servers/application servers do it (more or less).
But, as always, the world is an ugly place and lot of problems arise, and the “inherited sockets” approach is often not enough.
Things go wrong¶
We have seen that holding the uWSGI sockets alive allows the proxy webserver to enqueue requests without spitting out errors to the clients. This is true only if your app restarts fast, and, sadly, this may not always happen.
Frameworks like Ruby on Rails or Zope start up really slow by default, your
app could start up slowly by itself, or your machine could be so overloaded that
every process spawn (fork()
) takes ages.
In addition to this, your site could be so famous that even if your app restarts in a couple of seconds, the queue of your sockets could be filled up forcing the proxy server to raise an error.
Do not forget, your workers/threads that are still running requests could block the reload (for various reasons) for more seconds than your proxy server could tolerate.
Finally, you could have made an application error in your just-committed code, so uWSGI will not start, or will start sending wrong things or errors...
Reloads (brutal or graceful) can easily fail.
The listen queue¶
Let’s start with the dream of every webapp developer: success.
Your app is visited by thousands of clients and you obviously make money with it. Unfortunately, it is a very complex app and requires 10 seconds to warm up.
During graceful reloads, you expect new clients to wait 10 seconds (best case) to start seeing contents, but, unfortunately, you have hundreds of concurrent requests, so first 100 customers will wait during the server warm-up, while the others will get an error from the proxy.
This happens because the default size of uWSGI’s listen queue is 100 slots. Before you ask, it is an average value choosen by the maximum value allowed by default by your kernel.
Each operating system has a default limit (Linux has 128, for example), so before increasing it you need to increase your kernel limit too.
So, once your kernel is ready, you can increase the listen queue to the maximum number of users you expect to enqueue during a reload.
To increase the listen queue you use the --listen <n>
option where
<n>
is the maximum number of slots.
To raise kernel limits, you should check your OS docs. Some examples:
- sysctl
kern.ipc.somaxconn
on FreeBSD /proc/sys/net/core/somaxconn
on Linux.
注解
This is only one of the reasons to tune the listen queue, but do not blindly set it to huge values as a way to increase availability.
Proxy timeouts¶
This is another thing you need to check if your reloads take a lot of time.
Generally, proxies allow you to set two timeouts:
- connect
- Maximum amount of time the proxy will wait for a successful connection.
- read
- Maximum amount of time the server will be able to wait for data before giving up.
When tuning the reloads, only the “connection” timeout matters. This timeout
enters the game in the time slice between uWSGI’s bind to an interface (or
inheritance of it) and the call to accept()
.
Waiting instead of errors is good, no errors and no waiting is even better¶
This is the focus of this article. We have seen how to increase the tolerance of your proxy during application server reloading. The customers will wait instead of getting scary errors, but we all want to make money, so why force them to wait?
We want zero-downtime and zero-wait.
Preforking VS lazy-apps VS lazy¶
This is one of the controversial choices of the uWSGI project.
By default uWSGI loads the whole application in the first process and after
the app is loaded it does fork()
itself multiple times.
This is the common Unix pattern, it may highly reduce the memory usage of your
app, allows lot of funny tricks and on some languages may bring you a lot of
headaches.
Albeit its name, uWSGI was born as a Perl application server (it was not called uWSGI and it was not open source), and in the Perl world preforking is generally the blessed way.
This is not true for a lot of other languages, platforms and frameworks, so
before starting dealing with uWSGI you should choose how to manage fork()
in your stack.
Seeing it from the “graceful reloading” point of view, preforking extremely speeds up things: your app is loaded only one time, and spawning additional workers will be really fast. Avoiding disk access for each worker of your stack will decrease startup times, expecially for frameworks or languages doing a lot of disk access to find modules.
Unfortunately, the preforking approach forces you to reload the whole stack whenever you make code changes instead of reloading only the workers.
In addition to this, your app could need preforking, or could completely crash due to it because of the way it has been developed.
lazy-apps mode instead loads your application one time per worker. It will require about O(n) time to load it (where n is the number of workers), will very probably consume more memory, but will run in a more consistent and clean environment.
Remember: lazy-apps is different from lazy, the first one only instructs uWSGI to load the application one time per worker, while the second is more invasive (and generally discouraged) as it changes a lot of internal defaults.
The following approaches will show you how to accomplish zero-downtime/wait reloads in both preforking and lazy modes.
注解
Each approach has pros and cons, choose carefully.
Standard (default/boring) graceful reload (aka SIGHUP
)¶
To trigger it, you can:
- send
SIGHUP
to the master - write
r
to The Master FIFO - use
--touch-reload
option - call
uwsgi.reload()
API.
In preforking and lazy-apps mode, it will:
- Wait for running workers.
- Close all of the file descriptors except the ones mapped to sockets.
- Call
exec()
on itself.
In lazy mode, it will:
- Wait for running workers.
- Restart all of them (this means you cannot change uWSGI options during this kind of reload).
警告
lazy is discouraged!
Pros:
- easy to manage
- no corner-case problems
- no inconsistent states
- basically full reset of the instance.
Cons:
- the ones we seen before
- listen queue filling up
- stuck workers
- potentially long waiting times.
Workers reloading in lazy-apps mode¶
Requires --lazy-apps
option.
To trigger it:
- write
w
to The Master FIFO - use
--touch-workers-reload
option.
It will wait for running workers and then restart each of them.
Pros:
- avoids restarting the whole instance.
Cons:
- no user-experience improvements over standard graceful reload, it is only a shortcut for situation when code updates do not imply instance reconfiguration.
Chain reloading (lazy apps)¶
Requires --lazy-apps
option.
To trigger it:
- write
c
to The Master FIFO - use
--touch-chain-reload
option.
This is the first approach that improves user experience. When triggered, it will restart one worker at time, and the following worker is not reloaded until the previous one is ready to accept new requests.
Pros:
- potentially highly reduces waiting time for clients
- reduces the load of the machine during reloads (no multiple processes loading the same code).
Cons:
- only useful for code updates
- you need a good amount of workers to get a better user experience.
Zerg mode¶
Requires a zerg server or a zerg pool.
To trigger it, run the instance in zerg mode.
This is the first approach that uses multiple instances of the same application to increase user experience.
Zerg mode works by making use of the venerable “fd passing over Unix sockets” technique.
Basically, an external process (the zerg server/pool) binds to the various sockets required by your app. Your uWSGI instance, instead of binding by itself, asks the zerg server/pool to pass it the file descriptor. This means multiple unrelated instances can ask for the same file descriptors and work together.
Zerg mode was born to improve auto-scalability, but soon became one of the most loved approaches for zero-downtime reloading.
Now, examples.
Spawn a zerg pool exposing 127.0.0.1:3031
to the Unix socket
/var/run/pool1
:
[uwsgi]
master = true
zerg-pool = /var/run/pool1:127.0.0.1:3031
Now spawn one or more instances attached to the zerg pool:
[uwsgi]
; this will give access to 127.0.0.1:3031 to the instance
zerg = /var/run/pool1
When you want to make update of code or options, just spawn a new instance attached to the zerg, and shut down the old one when the new one is ready to accept requests.
The so-called “zerg dance” is a trick for automation of this kind of reload. There are various ways to accomplish it, the objective is to automatically “pause” or “destroy” the old instance when the new one is fully ready and able to accept requests. More on this below.
Pros:
- potentially the silver bullet
- allows instances with different options to cooperate for the same app.
Cons:
- requires an additional process
- can be hard to master
- reload requires copy of the whole uWSGI stack.
The Zerg Dance: Pausing instances¶
We all make mistakes, sysadmins must improve their skill of fast disaster recovery. Focusing on avoiding them is a waste of time. Unfortunately, we are all humans.
Rolling back deployments could be your life-safer.
We have seen how zerg mode allows us to have multiple instances asking on the same socket. In the previous section we used it to spawn a new instance working together with the old one. Now, instead of shutting down the old instance, why not “pause” it? A paused instance is like the standby mode of your TV. It consumes very few resources, but you can bring it back very quickly.
“Zerg Dance” is the battle-name for the procedure of continuos swapping of instances during reloads. Every reload results in a “sleeping” instance and a running one. Following reloads destroy the old sleeping instance and transform the old running to the sleeping one and so on.
There are literally dozens of ways to accomplish the “Zerg Dance”, the fact that you can easily use scripts in your reloading procedures makes this approach extremely powerful and customizable.
Here we will see the one that requires zero scripting, it could be the less versatile (and requires at least uWSGI 1.9.21), but should be a good starting point for the improvements.
The Master FIFO is the best way to manage instances instead of relying on Unix signals. Basically, you write single-char commands to govern the instance.
The funny thing about the Master FIFOs is that you can have many of them configured for your instance and swap one with another very easily.
An example will clarify things.
We spawn an instance with 3 Master FIFOs: new (the default one), running and sleeping:
[uwsgi]
; fifo '0'
master-fifo = /var/run/new.fifo
; fifo '1'
master-fifo = /var/run/running.fifo
; fifo '2'
master-fifo = /var/run/sleeping.fifo
; attach to zerg
zerg = /var/run/pool1
; other options ...
By default the “new” one will be active (read: will be able to process commands).
Now we want to spawn a new instance, that once is ready to accept requests will put the old one in sleeping mode. To do it, we will use uWSGI’s advanced hooks. Hooks allow you to “make things” at various phases of uWSGI’s life cycle. When the new instance is ready, we want to force the old instance to start working on the sleeping FIFO and be in “pause” mode:
[uwsgi]
; fifo '0'
master-fifo = /var/run/new.fifo
; fifo '1'
master-fifo = /var/run/running.fifo
; fifo '2'
master-fifo = /var/run/sleeping.fifo
; attach to zerg
zerg = /var/run/pool1
; hooks
; destroy the currently sleeping instance
if-exists = /var/run/sleeping.fifo
hook-accepting1-once = writefifo:/var/run/sleeping.fifo Q
endif =
; force the currently running instance to became sleeping (slot 2) and place it in pause mode
if-exists = /var/run/running.fifo
hook-accepting1-once = writefifo:/var/run/running.fifo 2p
endif =
; force this instance to became the running one (slot 1)
hook-accepting1-once = writefifo:/var/run/new.fifo 1
The hook-accepting1-once
phase is run one time per instance soon after the
first worker is ready to accept requests.
The writefifo
command allows writing to FIFOs without failing if the
other peers are not connected (this is different from a simple write
command that would fail or completely block when dealing with bad FIFOs).
注解
Both features have been added only in uWSGI 1.9.21, with older releases you can
use the --hook-post-app
option instead of --hook-accepting1-once
, but
you will lose the “once” feature, so it will work reliably only in preforking
mode.
Instead of writefifo
you can use the shell variant:
exec:echo <string> > <fifo>
.
Now start running instances with the same config files over and over again. If all goes well, you should always end with two instances, one sleeping and one running.
Finally, if you want to bring back a sleeping instance, just do:
# destroy the running instance
echo Q > /var/run/running.fifo
# unpause the sleeping instance and set it as the running one
echo p1 > /var/run/sleeping.fifo
Pros:
- truly zero-downtime reload.
Cons:
- requires high-level uWSGI and Unix skills.
SO_REUSEPORT
(Linux >= 3.9 and BSDs)¶
On recent Linux kernels and modern BSDs you may try --reuse-port
option.
This option allows multiple unrelated instances to bind on the same network
address. You may see it as a kernel-level zerg mode. Basically, all of the Zerg
approaches can be followed.
Once you add --reuse-port
to you instance, all of the sockets will have
the SO_REUSEPORT
flag set.
Pros:
- similar to zerg mode, could be even easier to manage.
Cons:
- requires kernel support
- could lead to inconsistent states
- you lose ability to use TCP addresses as a way to avoid incidental multiple instances running.
The Black Art (for rich and brave people): master forking¶
To trigger it, write f
to The Master FIFO.
This is the most dangerous of the ways to reload, but once mastered, it could lead to pretty cool results.
The approach is: call fork()
in the master, close all of the file
descriptors except the socket-related ones, and exec()
a new uWSGI
instance.
You will end with two specular uWSGI instances working on the same set of sockets.
The scary thing about it is how easy (just write a single char to the master FIFO) is to trigger it...
With a bit of mastery you can implement the zerg dance on top of it.
Pros:
- does not require kernel support nor an additional process
- pretty fast.
Cons:
- a whole copy for each reload
- inconstent states all over the place (pidfiles, logging, etc.: the master FIFO commands could help fix them).
Subscription system¶
This is probably the best approach when you can count on multiple servers. You add the “fastrouter” between your proxy server (e.g., nginx) and your instances.
Instances will “subscribe” to the fastrouter that will pass requests from proxy server (nginx) to them while load balancing and constantly monitoring all of them.
Subscriptions are simple UDP packets that instruct the fastrouter which domain maps to which instance or instances.
As you can subscribe, you can unsubscribe too, and this is where the magic happens:
[uwsgi]
subscribe-to = 192.168.0.1:4040:unbit.it
unsubscribe-on-graceful-reload = true
; all of the required options ...
Adding unsubscribe-on-graceful-reload
will force the instance to send an
“unsubscribe” packet to the fastrouter, so until it will not be back no request
will be sent to it.
Pros:
- low-cost zero-downtime
- a KISS approach (finally).
Cons:
- requires a subscription server (like the fastrouter) that introduces overhead (even if we are talking about microseconds).
Inconsistent states¶
Sadly, most of the approaches involving copies of the whole instance (like Zerg Dance or master forking) lead to inconsistent states.
Take, for example, an instance writing pidfiles: when starting a copy of it, that pidfile will be overwritten.
If you carefully plan your configurations, you can avoid inconsistent states, but thanks to The Master FIFO you can manage some of them (read: the most common ones):
l
command will reopen logfilesP
command will update all of the instance pidfiles.
Fighting inconsistent states with the Emperor¶
If you manage your instances with the Emperor, you can use its features to avoid (or reduce number of) inconsistent states.
Giving each instance a different symbolic link name will allow you to map files (like pidfiles or logs) to different paths:
[uwsgi]
logto = /var/log/%n.log
pidfile = /var/run/%n.pid
; and so on ...
Dealing with ultra-lazy apps (like Django)¶
Some applications or frameworks (like Django) may load the vast majority of their code only at the first request. This means that customer will continue to experience slowdowns during reload even when using things like zerg mode or similar.
This problem is hard to solve (impossible?) in the application server itself, so you should find a way to force your app to load itself ASAP. A good trick (read: works with Django) is to call the entry-point function (like the WSGI callable) in the app itself:
def application(environ, sr):
sr('200 OK', [('Content-Type', 'text/plain')])
yield "Hello"
application({}, lambda x, y: None) # call the entry-point function
You may need to pass CGI vars to the environ to make a true request: it depends on the WSGI app.
Finally: Do not blindly copy & paste!¶
Please, turn on your brain and try to adapt shown configs to your needs, or invent new ones.
Each app and system is different from the others.
Experiment before making a choice.
Fun with Perl, Eyetoy and RaspberryPi¶
Author: Roberto De Ioris
Date: 2013-12-07

Intro¶
This article is the result of various experiments aimed at improving uWSGI performance and usability in various areas before the 2.0 release.
To follow the article you need:
- a Raspberry Pi (any model) with a Linux distribution installed (I used standard Raspbian)
- a PS3 Eyetoy webcam
- a websocket-enabled browser (basically any serious browser)
- a bit of Perl knowledge (really only a bit, there’s less than 10 lines of Perl ;)
- Patience (building uWSGI + PSGI + coroae on the RPI requires 13 minutes)
uWSGI subsystems and plugins¶
The project makes use of the following uWSGI subsystems and plugins:
- WebSocket support
- SharedArea – share memory pages between uWSGI components (for storing frames)
- uWSGI Mules (for gathering frames)
- The Symcall plugin
- uWSGI Perl support (PSGI)
- uWSGI asynchronous/non-blocking modes (updated to uWSGI 1.9) (optional, we use
Coro::Anyevent
but you can rely on standard processes, though you’ll need way more memory)
What we want to accomplish¶
We want our RPI to gather frames from the Eyetoy and stream them to various connected clients using websockets, using a HTML5 canvas element to show them.
The whole system must use as little memory as possible, as few CPU cycles as possible, and it should support a large number of clients (... though well, even 10 clients will be a success for the Raspberry Pi hardware ;)
Technical background¶
The Eyetoy captures frames in YUYV format (known as YUV 4:2:2). This means we need 4 bytes for 2 pixels.
By default the resolution is set to 640x480, so each frame will need 614,400 bytes.
Once we have a frame we need to decode it to RGBA to allow the HTML5 canvas to show it.
The translation between YUYV and RGBA is pretty heavy for the RPI (especially if you need to do it for every connected client) so we will do it in the browser using Javascript. (There are other approaches we could follow, just check the end of the article for them.)
The uWSGI stack is composed by a mule gathering frames from the Eyetoy and writing them to the uWSGI SharedArea.
Workers constantly read from that SharedArea and send frames as binary websocket messages.
Let’s start: the uwsgi-capture plugin¶
uWSGI 1.9.21 introduced a simplified (and safe) procedure to build uWSGI plugins. (Expect more third party plugins soon!)
The project at: https://github.com/unbit/uwsgi-capture shows a very simple plugin using the Video4Linux 2 API to gather frames.
Each frame is written in a shared area initialized by the plugin itself.
The first step is getting uWSGI and building it with the ‘coroae’ profile:
sudo apt-get install git build-essential libperl-dev libcoro-perl
git clone https://github.com/unbit/uwsgi
cd uwsgi
make coroae
The procedure requires about 13 minutes. If all goes well you can clone the uwsgi-capture plugin and build it.
git clone https://github.com/unbit/uwsgi-capture
./uwsgi --build-plugin uwsgi-capture
You now have the capture_plugin.so file in your uwsgi directory.
Plug your Eyetoy into an USB port on your RPI and check if it works:
./uwsgi --plugin capture --v4l-capture /dev/video0
(the --v4l-capture
option is exposed by the capture plugin)
If all goes well you should see the following lines in uWSGI startup logs:
/dev/video0 detected width = 640
/dev/video0 detected height = 480
/dev/video0 detected format = YUYV
sharedarea 0 created at 0xb6935000 (150 pages, area at 0xb6936000)
/dev/video0 started streaming frames to sharedarea 0
(the sharedarea memory pointers will obviously probably be different)
The uWSGI process will exit soon after this as we did not tell it what to do. :)
The uwsgi-capture
plugin exposes 2 functions:
captureinit()
, mapped as the init() hook of the plugin, will be called automatically by uWSGI. If the –v4l-capture option is specified, this function will initialize the specified device and will map it to a uWSGI sharedarea.captureloop()
is the function gathering frames and writing them to the sharedarea. This function should constantly run (even if there are no clients reading frames)
We want a mule to run the captureloop()
function.
./uwsgi --plugin capture --v4l-capture /dev/video0 --mule="captureloop()" --http-socket :9090
This time we have bound uWSGI to HTTP port 9090 with a mule mapped to the “captureloop()” function. This mule syntax is exposed by the symcall plugin that takes control of every mule argument ending with “()” (the quoting is required to avoid the shell making a mess of the parentheses).
If all goes well you should see your uWSGI server spawning a master, a mule and a worker.
Step 2: the PSGI app¶
Time to write our websocket server sending Eyetoy frames (you can find sources for the example here: https://github.com/unbit/uwsgi-capture/tree/master/rpi-examples).
The PSGI app will be very simple:
use IO::File;
use File::Basename;
my $app = sub {
my $env = shift;
# websockets connection happens on /eyetoy
if ($env->{PATH_INFO} eq '/eyetoy') {
# complete the handshake
uwsgi::websocket_handshake($env->{HTTP_SEC_WEBSOCKET_KEY}, $env->{HTTP_ORIGIN});
while(1) {
# wait for updates in the sharedarea
uwsgi::sharedarea_wait(0, 50);
# send a binary websocket message directly from the sharedarea
uwsgi::websocket_send_binary_from_sharedarea(0, 0)
}
}
# other requests generate the html
else {
return [200, ['Content-Type' => 'text/html'], new IO::File(dirname(__FILE__).'/eyetoy.html')];
}
}
The only interesting parts are:
uwsgi::sharedarea_wait(0, 50);
This function suspends the current request until the specified shared area (the ‘zero’ one) gets an update. As this function is basically a busy-loop poll, the second argument specifies the polling frequency in milliseconds. 50 milliseconds gave us good results (feel free to try with other values).
uwsgi::websocket_send_binary_from_sharedarea(0, 0)
This is a special utility function sending a websocket binary message directly from the sharedarea (yep, zero-copy). The first argument is the sharedarea id (the ‘zero’ one) and the second is the position in the sharedarea to start reading from (zero again, as we want a full frame).
Step 3: HTML5¶
The HTML part (well it would be better to say Javascript part) is very easy, aside from the YUYV to RGB(A) transform voodoo.
<html>
<body>
<canvas id="mystream" width="640" height="480" style="border:solid 1px red"></canvas>
<script>
var canvas = document.getElementById('mystream');
var width = canvas.width;
var height = canvas.height;
var ctx = canvas.getContext("2d");
var rgba = ctx.getImageData(0, 0, width, height);
// fill alpha (optimization)
for(y = 0; y< height; y++) {
for(x = 0; x < width; x++) {
pos = (y * width * 4) + (x * 4) ;
rgba.data[pos+3] = 255;
}
}
// connect to the PSGI websocket server
var ws = new WebSocket('ws://' + window.location.host + '/eyetoy');
ws.binaryType = 'arraybuffer';
ws.onopen = function(e) {
console.log('ready');
};
ws.onmessage = function(e) {
var x, y;
var ycbcr = new Uint8ClampedArray(e.data);
// convert YUYV to RGBA
for(y = 0; y< height; y++) {
for(x = 0; x < width; x++) {
pos = (y * width * 4) + (x * 4) ;
var vy, cb, cr;
if (x % 2 == 0) {
ycbcr_pos = (y * width * 2) + (x * 2);
vy = ycbcr[ycbcr_pos];
cb = ycbcr[ycbcr_pos+1];
cr = ycbcr[ycbcr_pos+3];
}
else {
ycbcr_pos = (y * width * 2) + ((x-1) * 2);
vy = ycbcr[ycbcr_pos+2];
cb = ycbcr[ycbcr_pos+1];
cr = ycbcr[ycbcr_pos+3];
}
var r = (cr + ((cr * 103) >> 8)) - 179;
var g = ((cb * 88) >> 8) - 44 + ((cr * 183) >> 8) - 91;
var b = (cb + ((cb * 198) >> 8)) - 227;
rgba.data[pos] = vy + r;
rgba.data[pos+1] = vy + g;
rgba.data[pos+2] = vy + b;
}
}
// draw pixels
ctx.putImageData(rgba, 0, 0);
};
ws.onclose = function(e) { alert('goodbye');}
ws.onerror = function(e) { alert('oops');}
</script>
</body>
</html>
Nothing special here. The vast majority of the code is related to YUYV->RGBA conversion. Pay attention to set the websocket communication in ‘binary’ mode (binaryType = ‘arraybuffer’ is enough) and be sure to use an Uint8ClampedArray (otherwise performance will be terribly bad)
Ready to watch¶
./uwsgi --plugin capture --v4l-capture /dev/video0 --http-socket :9090 --psgi uwsgi-capture/rpi-examples/eyetoy.pl --mule="captureloop()"
Connect with your browser to TCP port 9090 of your Raspberry Pi and start watching.
Concurrency¶
While you watch your websocket stream, you may want to start another browser window to see a second copy of your video. Unfortunately you spawned uWSGI with a single worker, so only a single client can get the stream.
You can add multiple workers easily:
./uwsgi --plugin capture --v4l-capture /dev/video0 --http-socket :9090 --psgi uwsgi-capture/rpi-examples/eyetoy.pl --mule="captureloop()" --processes 10
Like this up to 10 people will be able to watch the stream.
But coroutines are way better (and cheaper) for I/O bound applications such as this:
./uwsgi --plugin capture --v4l-capture /dev/video0 --http-socket :9090 --psgi uwsgi-capture/rpi-examples/eyetoy.pl --mule="captureloop()" --coroae 10
Now, magically, we are able to manage 10 clients with but a single process! The memory on the RPI will be grateful to you.
Zero-copy all the things¶
Why are we using the SharedArea?
The SharedArea is one of the most advanced uWSGI features. If you give a look at the uwsgi-capture plugin you will see how it easily creates a sharedarea pointing to a mmap()’ed region. Basically each worker, thread (but please do not use threads with Perl) or coroutine will have access to that memory in a concurrently safe way.
In addition to this, thanks to the websocket/sharedarea cooperation API you can directly send websocket packets from a sharedarea without copying memory (except for the resulting websocket packet).
This is way faster than something like:
my $chunk = uwsgi::sharedarea_read(0, 0)
uwsgi::websocket_send_binary($chunk)
We would need to allocate the memory for $chunk at every iteration, copying the sharedarea content into it and finally encapsulating it in a websocket message.
With the sharedarea you remove the need to allocate (and free) memory constantly and to copy it from sharedarea to the Perl VM.
Alternative approaches¶
There are obviously other approaches you can follow.
You could hack uwsgi-capture to allocate a second sharedarea into which it will directly write RGBA frames.
JPEG encoding is relatively fast, you can try encoding frames in the RPI and sending them as MJPEG frames (instead of using websockets):
my $writer = $responder->( [200, ['Content-Type' => 'multipart/x-mixed-replace; boundary=uwsgi_mjpeg_frame']]);
$writer->write("--uwsgi_mjpeg_frame\r\n");
while(1) {
uwsgi::sharedarea_wait(0);
my $chunk = uwsgi::sharedarea_read(0, 0);
$writer->write("Content-Type: image/jpeg\r\n");
$writer->write("Content-Length: ".length($chunk)."\r\n\r\n");
$writer->write($chunk);
$writer->write("\r\n--uwsgi_mjpeg_frame\r\n");
}
Other languages¶
At the time of writing, the uWSGI PSGI plugin is the only one exposing the additional API for websockets+sharedarea. The other language plugins will be updated soon.
More hacking¶
The RPI board is really fun to tinker with and uWSGI is a great companion for it (especially its lower-level API functions).
注解
As an exercise left to the reader: remember you can mmap() the address 0x20200000 to access the Raspberry PI GPIO controller... ready to write a uwsgi-gpio plugin?
Offloading Websockets and Server-Sent Events AKA “Combine them with Django safely”¶
Author: Roberto De Ioris
Date: 20140315
Disclaimer¶
This article shows a pretty advanced way for combining websockets (or sse) apps with Django in a “safe way”. It will not show you how cool are websockets and sse, or how to write better apps with them, it is an attempt to try to avoid bad practices with them.
In my opinion the Python web-oriented world is facing a communication/marketing problem: There is a huge number of people running heavily blocking apps (like Django) on non-blocking technologies (like gevent) only because someone told them it is cool and will solve all of their scaling issues.
This is completely WRONG, DANGEROUS and EVIL, you cannot mix blocking apps with non-blocking engines, even a single, ultra-tiny blocking part can potentially destroy your whole stack. As i have already said dozens of time, if your app is 99.9999999% non-blocking, it is still blocking.
And no, monkey patching on your Django app is not magic. Unless you are using pretty-customized database adapters, tuned for working in a non-blocking way, you are doing wrong.
At the cost of looking a huber-asshole, i strongly suggest you to completely ignore people suggesting you to move your Django app to gevent, eventlet, tornado or whatever, without warning you about the hundreds of problems you may encounter.
Having said that, i love gevent, it is probably the best (with perl’s Coro::AnyEvent) supported loop engine in the uWSGI project. So in this article i will use gevent for managing websocket/sse traffic and plain multiprocessing for the Django part.
If this last sentence looks a nonsense to you, you probably do not know what uWSGI offloading is...
uWSGI offloading¶
The concept is not a new thing, or a uWSGI specific one. Projects like nodejs or twisted use it by ages.
注解
an example of a webapp serving a static file is not very interesting, nor the best thing to show, but will be useful later, when presenting a real-world scenario with X-Sendfile
Immagine this simple WSGI app:
def application(env, start_response):
start_response('200 OK',[('Content-Type','text/plain')])
f = open('/etc/services')
# do not do it, if the file is 4GB it will allocate 4GB of memory !!!
yield f.read()
it will simply returns the content of /etc/services. It is a pretty tiny file, so in few milliseconds your process will be ready to process another request.
What if /etc/services is 4 gigabytes ? Your process (or thread) will be blocked for various seconds (even minutes), and will not be able to manage another request until the file is completely transferred.
Would not be cool if you can tell to another thread to send the file for you, so you will be able to manage another request ?
Offloading is exactly this: it will give you one ore more threads for doing simple and slow task for you. Which kind of tasks ? All of those that can be managed in a non-blocking way, so a single thread can manage thousand of transfer for you.
You can see it as the DMA engine in your computer, your CPU will program the DMA to tranfer memory from a controller to the RAM, and then will be freed to accomplish another task while the DMA works in background.
To enable offloading in uWSGI you only need to add the --offload-threads <n>
option, where <n> is the number of threads per-process to spawn. (generally a single thread will be more than enough, but if you want to use/abuse your multiple cpu cores feel free to increase it)
Once offloading is enabled, uWSGI will automatically use it whenever it detects that an operation can be offloaded safely.
In the python/WSGI case the use of wsgi.file_wrapper will be offloaded automatically, as well as when you use the uWSGI proxy features for passing requests to other server speaking the uwsgi or HTTP protocol.
A cool example (showed even in the Snippets page of uWSGI docs) is implementing a offload-powered X-Sendfile feature:
[uwsgi]
; load router_static plugin (compiled in by default in monolithic profiles)
plugins = router_static
; spawn 2 offload threads
offload-threads = 2
; files under /etc can be safely served (DANGEROUS !!!)
static-safe = /etc
; collect the X-Sendfile response header as X_SENDFILE var
collect-header = X-Sendfile X_SENDFILE
; if X_SENDFILE is not empty, pass its value to the "static" routing action (it will automatically use offloading if available)
response-route-if-not = empty:${X_SENDFILE} static:${X_SENDFILE}
; now the classic options
plugins = python
; bind to HTTP port 8080
http-socket = :8080
; load a simple wsgi-app
wsgi-file = myapp.py
now in our app we can X-Sendfile to send static files without blocking:
def application(env, start_response):
start_response('200 OK',[('X-Sendfile','/etc/services')])
return []
A very similar concept will be used in this article: We will use a normal Django to setup our session, to authorize the user and whatever (that is fast) you want, then we will return a special header that will instruct uWSGI to offload the connection to another uWSGI instance (listening on a private socket) that will manage the websocket/sse transaction using gevent in a non-blocking way.
Our SSE app¶
The SSE part will be very simple, a gevent-based WSGI app will send the current time every second:
from sse import Sse
import time
def application(e, start_response):
print e
# create the SSE session
session = Sse()
# prepare HTTP headers
headers = []
headers.append(('Content-Type','text/event-stream'))
headers.append(('Cache-Control','no-cache'))
start_response('200 OK', headers)
# enter the loop
while True:
# monkey patching will prevent sleep() to block
time.sleep(1)
# add the message
session.add_message('message', str(time.time()))
# send to the client
yield str(session)
Let’s run it on /tmp/foo UNIX socket (save the app as sseapp.py)
uwsgi --wsgi-file sseapp.py --socket /tmp/foo --gevent 1000 --gevent-monkey-patch
(monkey patching is required for time.sleep(), feel free to use gevent primitives for sleeping if you want/prefer)
The (boring) HTML/Javascript¶
<html>
<head>
</head>
<body>
<h1>Server sent events</h1>
<div id="event"></div>
<script type="text/javascript">
var eventOutputContainer = document.getElementById("event");
var evtSrc = new EventSource("/subscribe");
evtSrc.onmessage = function(e) {
console.log(e.data);
eventOutputContainer.innerHTML = e.data;
};
</script>
</body>
</html>
it is very simple, it will connect to /subscribe and will start waiting for events
The Django view¶
Our django view, will be very simple, it will simply generate a special response header (we will call it X-Offload-to-SSE) with the username of the logged user as its value:
def subscribe(request):
response = HttpResponse()
response['X-Offload-to-SSE'] = request.user
return response
Now we are ready for the “advanced” part
Let’s offload the SSE transaction¶
The configuration could look a bit complex but it is the same concept of the X-Sendfile seen before
[uwsgi]
; the boring part
http-socket = :9090
offload-threads = 2
wsgi-file = sseproject/wsgi.py
; collect X-Offload-to-SSE header and store in var X_OFFLOAD
collect-header = X-Offload-to-SSE X_OFFLOAD
; if X_OFFLOAD is defined, do not send the headers generated by Django
response-route-if-not = empty:${X_OFFLOAD} disableheaders:
; if X_OFFLOAD is defined, offload the request to the app running on /tmp/foo
response-route-if-not = empty:${X_OFFLOAD} uwsgi:/tmp/foo,0,0
The only “new’ part is the use of `disableheaders
routing action. It is required otherwise the headers generated by Django
will be sent along the ones generated by the gevent-based app.
You could avoid it (remember that disableheaders
has been added only in 2.0.3) removing the call to start_response() in the gevent app (at the risk of being cursed by some WSGI-god) and changing the Django view
to set the right headers:
def subscribe(request):
response = HttpResponse()
response['Content-Type'] = 'text/event-stream'
response['X-Offload-to-SSE'] = request.user
return response
Eventually you may want to be more “streamlined” and simply detect for ‘text/event-stream’ content_type presence:
[uwsgi]
; the boring part
http-socket = :9090
offload-threads = 2
wsgi-file = sseproject/wsgi.py
; collect Content-Type header and store in var CONTENT_TYPE
collect-header = Content-Type CONTENT_TYPE
; if CONTENT_TYPE is 'text/event-stream', forward the request
response-route-if = equal:${CONTENT_TYPE};text/event-stream uwsgi:/tmp/foo,0,0
Now, how to access the username of the Django-logged user in the gevent app ?
You should have noted that the gevent-app prints the content of the WSGI environ on each request. Such environment is the same of the Django app + the collected headers. So accessing environ[‘X_OFFLOAD’] will return the logged username. (obviously in the second example, where the content type is used, the variable with the username is no more collected, so you should fix it)
You can pass all of the infos you need using the same approach, you can collect all of the vars you need and so on.
You can even add variables at runtime
[uwsgi]
; the boring part
http-socket = :9090
offload-threads = 2
wsgi-file = sseproject/wsgi.py
; collect Content-Type header and store in var CONTENT_TYPE
collect-header = Content-Type CONTENT_TYPE
response-route-if = equal:${CONTENT_TYPE};text/event-stream addvar:FOO=BAR
response-route-if = equal:${CONTENT_TYPE};text/event-stream addvar:TEST1=TEST2
; if CONTENT_TYPE is 'text/event-stream', forward the request
response-route-if = equal:${CONTENT_TYPE};text/event-stream uwsgi:/tmp/foo,0,0
or (using goto for better readability)
[uwsgi]
; the boring part
http-socket = :9090
offload-threads = 2
wsgi-file = sseproject/wsgi.py
; collect Content-Type header and store in var CONTENT_TYPE
collect-header = Content-Type CONTENT_TYPE
response-route-if = equal:${CONTENT_TYPE};text/event-stream goto:offload
response-route-run = last:
response-route-label = offload
response-route-run = addvar:FOO=BAR
response-route-run = addvar:TEST1=TEST2
response-route-run = uwsgi:/tmp/foo,0,0
Simplifying things using the uwsgi api (>= uWSGI 2.0.3)¶
While dealing with headers is pretty HTTP friendly, uWSGI 2.0.3 added the possibility to define per-request variables directly in your code.
This allows a more “elegant” approach (even if highly non-portable)
import uwsgi
def subscribe(request):
uwsgi.add_var("LOGGED_IN_USER", request.user)
uwsgi.add_var("USER_IS_UGLY", "probably")
uwsgi.add_var("OFFLOAD_TO_SSE", "y")
uwsgi.add_var("OFFLOAD_SERVER", "/tmp/foo")
return HttpResponse()
Now the config can change to a more gentle:
; the boring part
http-socket = :9090
offload-threads = 2
wsgi-file = sseproject/wsgi.py
; if OFFLOAD_TO_SSE is 'y', do not send the headers generated by Django
response-route-if = equal:${OFFLOAD_TO_SSE};y disableheaders:
; if OFFLOAD_TO_SSE is defined, offload the request to the app running on 'OFFLOAD_SERVER'
response-route-if = equal:${OFFLOAD_TO_SSE};y uwsgi:${OFFLOAD_SERVER},0,0
Have you noted how we allowed the Django app to set the backend server to use using a request variable ?
Now we can go even further. We will not use the routing framework (except for disabling headers generation)
import uwsgi
def subscribe(request):
uwsgi.add_var("LOGGED_IN_USER", request.user)
uwsgi.add_var("USER_IS_UGLY", "probably")
uwsgi.route("uwsgi", "/tmp/foo,0,0")
return HttpResponse()
and a simple:
; the boring part
http-socket = :9090
offload-threads = 2
wsgi-file = sseproject/wsgi.py
response-route = ^/subscribe disableheaders:
What about Websockets ?¶
We have seen how to offload SSE (that are mono-directional), we can offload websockets too (that are bidirectional).
The concept is the same, you only need to ensure (as before) that no headers are sent by django, (otherwise the websocket handshake will fail) and then you can change your gevent app:
import time
import uwsgi
def application(e, start_response):
print e
uwsgi.websocket_handshake()
# enter the loop
while True:
# monkey patching will prevent sleep() to block
time.sleep(1)
# send to the client
uwsgi.websocket_send(str(time.time()))
Using redis or uWSGI caching framework¶
Request vars are handy (and funny), but they are limited (see below). If you need to pass a big amount of data between Django and the sse/websocket app, Redis is a great way (and works perfectly with gevent). Basically you store infos from django to redis and than you pass only the hash key (via request vars) to the sse/websocket app.
The same can be accomplished with the uWSGI caching framework, but take in account redis has a lot of data primitives, while uWSGI only supports key->value items.
Common pitfalls¶
- The amount of variables you can add per-request is limited by the uwsgi packet buffer (default 4k). You can increase it up to 64k with the –buffer-size option
- This is the whole point of this article: do not use the Django ORM in your gevent apps unless you know what you are doing !!! (read, you have a django database adapter that supports gevent and does not sucks compared to the standard ones...)
- Forget about finding a way to disable headers generation in django. This is a “limit/feature” of its WSGI adapter, use the uWSGI facilities (if available) or do not generate headers in your gevent app. Eventually you can modify wsgi.py in this way:
"""
WSGI config for sseproject project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
"""
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "sseproject.settings")
from django.core.wsgi import get_wsgi_application
django_application = get_wsgi_application()
def fake_start_response(status, headers, exc_info=None):
pass
def application(environ, start_response):
if environ['PATH_INFO'] == '/subscribe':
return django_application(environ, fake_start_response)
return django_application(environ, start_response)
uWSGI 子系统¶
The uWSGI Legion subsystem¶
As of uWSGI 1.9-dev a new subsystem for clustering has been added: The Legion subsystem. A Legion is a group of uWSGI nodes constantly fighting for domination. Each node has a valor value (different from the others, if possible). The node with the highest valor is the Lord of the Legion (or if you like a less gaming nerd, more engineer-friendly term: the master). This constant fight generates 7 kinds of events:
setup
- when the legion subsystem is started on a nodejoin
- the first time quorum is reached, only on the newly joined nodelord
- when this node becomes the lordunlord
- when this node loses the lord titledeath
- when the legion subsystem is shutting downnode-joined
- when any new node joins our legionnode-left
- when any node leaves our legion
You can trigger actions every time such an event rises.
Note: openssl
headers must be installed to build uWSGI with Legion support.
IP takeover¶
This is a very common configuration for clustered environments. The IP address is a resource that must be owned by only one node. For this example, that node is our Lord. If we configure a Legion right (remember, a single uWSGI instances can be a member of all of the legions you need) we could easily implement IP takeover.
[uwsgi]
legion = clusterip 225.1.1.1:4242 98 bf-cbc:hello
legion-node = clusterip 225.1.1.1:4242
legion-lord = clusterip cmd:ip addr add 192.168.173.111/24 dev eth0
legion-lord = clusterip cmd:arping -c 3 -S 192.168.173.111 192.168.173.1
legion-setup = clusterip cmd:ip addr del 192.168.173.111/24 dev eth0
legion-unlord = clusterip cmd:ip addr del 192.168.173.111/24 dev eth0
legion-death = clusterip cmd:ip addr del 192.168.173.111/24 dev eth0
In this example we join a legion named clusterip
. To receive messages from
the other nodes we bind on the multicast address 225.1.1.1:4242. The valor of
this node will be 98 and each message will be encrypted using Blowfish in CBC
with the shared secret hello
. The legion-node
option specifies the
destination of our announce messages. As we are using multicast we only need to
specify a single “node”. The last options are the actions to trigger on the
various states of the cluster. For an IP takeover solution we simply rely on
the Linux iproute
commands to set/unset ip addresses and to send an extra
ARP message to announce the change. Obviously this specific example requires
root privileges or the CAP_NET_ADMIN
Linux capability, so be sure to not
run untrusted applications on the same uWSGI instance managing IP takeover.
The Quorum¶
To choose a Lord each member of the legion has to cast a vote. When all of the active members of a legion agree on a Lord, the Lord is elected and the old Lord is demoted. Every time a new node joins or leaves a legion the quorum is re-computed and logged to the whole cluster.
Choosing the Lord¶
Generally the node with the higher valor is chosen as the Lord, but there can be cases where multiple nodes have the same valor. When a node is started a UUID is assigned to it. If two nodes with same valor are found the one with the lexicographically higher UUID wins.
Split brain¶
Even though each member of the Legion has to send a checksum of its internal cluster-membership, the system is still vulnerable to the split brain problem. If a node loses network connectivity with the cluster, it could believe it is the only node available and starts going in Lord mode.
For many scenarios this is not optimal. If you have more than 2 nodes in a
legion you may want to consider tuning the quorum level. The quorum level is
the amount of votes (as opposed to nodes) needed to elect a lord.
legion-quorum
is the option for the job. You can reduce the split brain
problem asking the Legion subsystem to check for at least 2 votes:
[uwsgi]
legion = clusterip 225.1.1.1:4242 98 bf-cbc:hello
legion-node = clusterip 225.1.1.1:4242
legion-quorum = clusterip 2
legion-lord = clusterip cmd:ip addr add 192.168.173.111/24 dev eth0
legion-lord = clusterip cmd:arping -c 3 -S 192.168.173.111 192.168.173.1
legion-setup = clusterip cmd:ip addr del 192.168.173.111/24 dev eth0
legion-unlord = clusterip cmd:ip addr del 192.168.173.111/24 dev eth0
legion-death = clusterip cmd:ip addr del 192.168.173.111/24 dev eth0
As of 1.9.7 you can use nodes with valor 0 (concept similar to MongoDB’s Arbiter Nodes), such nodes will be counted when checking for quorum but may never become The Lord. This is useful when you only need a couple nodes while protecting against split-brain.
Actions¶
Each one of the four phases of a legion can trigger an action. The actions system is modular so you can add new kinds of actions. Currently the supported actions are:
cmd:<command>
¶
Run a shell command.
signal:<num>
¶
Raise a uWSGI signal.
log:<msg>
¶
Log a message. For example you could combine the log action with the alarm subsystem to have cluster monitoring for free.
Multicast, broadcast and unicast
¶
Even if multicast is probably the easiest way to implement clustering it is not available in all networks. If multicast is not an option, you can rely on normal IP addresses. Just bind to an address and add all of the legion-node options you need:
[uwsgi]
legion = mycluster 192.168.173.17:4242 98 bf-cbc:hello
legion-node = mycluster 192.168.173.22:4242
legion-node = mycluster 192.168.173.30:4242
legion-node = mycluster 192.168.173.5:4242
This is for a cluster of 4 nodes (this node + 3 other nodes)
Multiple Legions¶
You can join multiple legions in the same instance. Just remember to use different addresses (ports in case of multicast) for each legion.
[uwsgi]
legion = mycluster 192.168.173.17:4242 98 bf-cbc:hello
legion-node = mycluster 192.168.173.22:4242
legion-node = mycluster 192.168.173.30:4242
legion-node = mycluster 192.168.173.5:4242
legion = mycluster2 225.1.1.1:4243 99 aes-128-cbc:secret
legion-node = mycluster2 225.1.1.1:4243
legion = anothercluster 225.1.1.1:4244 91 aes-256-cbc:secret2
legion-node = anothercluster 225.1.1.1:4244
Security¶
Each packet sent by the Legion subsystem is encrypted using a specified cipher,
a preshared secret, and an optional IV (initialization vector). Depending on
cipher, the IV may be a required parameter. To get the list of supported
ciphers, run openssl enc -h
.
重要
Each node of a Legion has to use the same encryption parameters.
To specify the IV just add another parameter to the legion option.
[uwsgi]
legion = mycluster 192.168.173.17:4242 98 bf-cbc:hello thisistheiv
legion-node = mycluster 192.168.173.22:4242
legion-node = mycluster 192.168.173.30:4242
legion-node = mycluster 192.168.173.5:4242
To reduce the impact of replay-based attacks, packets with a timestamp lower than 30 seconds are rejected. This is a tunable parameter. If you have no control on the time of all of the nodes you can increase the clock skew tolerance.
Tuning and Clock Skew¶
Currently there are three parameters you can tune. These tuables affect all Legions in the system. The frequency (in seconds) at which each packet is sent (legion-freq <secs>), the amount of seconds after a node not sending packets is considered dead (legion-tolerance <secs>), and the amount of clock skew between nodes (legion-skew-tolerance <secs>). The Legion subsystem requires tight time synchronization, so the use of NTP or similar is highly recommended. By default each packet is sent every 3 seconds, a node is considered dead after 15 seconds, and a clock skew of 30 seconds is tolerated. Decreasing skew tolerance should increase security against replay attacks.
Lord scroll (coming soon)¶
The Legion subsystem can be used for a variety of purposes ranging from master election to node autodiscovery or simple monitoring. One example is to assign a “blob of data” (a scroll) to every node, One use of this is to pass reconfiguration parameters to your app, or to log specific messages. Currently the scroll system is being improved upon, so if you have ideas join our mailing list or IRC channel.
Legion API¶
You can know if the instance is a lord of a Legion by simply calling
int uwsgi_legion_i_am_the_lord(char *legion_name);
It returns 1 if the current instance is the lord for the specified Legion.
- The Python plugin exposes it as
uwsgi.i_am_the_lord(name)
- The PSGI plugin exposes it as
uwsgi::i_am_the_lord(name)
- The Rack plugin exposes it as
UWSGI::i_am_the_lord(name)
Obviously more API functions will be added in the future, feel free to expose your ideas.
Stats¶
The Legion information is available in the The uWSGI Stats Server. Be sure to understand the difference between “nodes” and “members”. Nodes are the peer you configure with the legion-node option while members are the effective nodes that joined the cluster.
The old clustering subsystem¶
During 0.9 development cycle a clustering subsystem (based on multicast) was added. It was very raw, unreliable and very probably no-one used it seriously. The new method is transforming it in a general API that can use different backends. The Legion subsystem can be one of those backends, as well as projects like corosync or the redhat cluster suite.
uWSGI Mules¶
Mules are worker processes living in the uWSGI stack but not reachable via socket connections, that can be used as a generic subsystem to offload tasks. You can see them as a more primitive spooler.
They can access the entire uWSGI API and can manage signals and be communicated with through a simple string-based message system.
To start a mule (you can start an unlimited number of them), use the mule
option as many times as you need.
Mules have two modes,
- Signal only mode (the default). In this mode the mules load your application as normal workers would. They can only respond to uWSGI signals.
- Programmed mode. In this mode mules load a program separate from your application. See ProgrammedMules.
By default each mule starts in signal-only mode.
uwsgi --socket :3031 --mule --mule --mule --mule
<uwsgi>
<socket>:3031</socket>
<mule/>
<mule/>
<mule/>
<mule/>
</uwsgi>
Basic usage¶
import uwsgi
from uwsgidecorators import timer, signal, filemon
# run a timer in the first available mule
@timer(30, target='mule')
def hello(signum):
print "Hi! I am responding to signal %d, running on mule %d" % (signum, uwsgi.mule_id())
# map signal 17 to mule 2
@signal(17, target='mule2')
def i_am_mule2(signum):
print "Greetings! I am running in mule number two."
# monitor /tmp and arouse all of the mules on modifications
@filemon('/tmp', target='mules')
def tmp_modified(signum):
print "/tmp has been modified. I am mule %d!" % uwsgi.mule_id()
Giving a brain to mules¶
As mentioned before, mules can be programmed. To give custom logic to a mule, pass the name of a script to the mule
option.
uwsgi --socket :3031 --mule=somaro.py --mule --mule --mule
This will run 4 mules, 3 in signal-only mode and one running somaro.py
.
# somaro.py
from threading import Thread
import time
def loop1():
while True:
print "loop1: Waiting for messages... yawn."
message = uwsgi.mule_get_msg()
print message
def loop2():
print "Hi! I am loop2."
while True:
time.sleep(2)
print "This is a thread!"
t = Thread(target=loop2)
t.daemon = True
t.start()
if __name__ == '__main__':
loop1()
So as you can see from the example, you can use mule_get_msg()
to receive messages in a programmed mule. Multiple threads in the same programmed mule can wait for messages.
If you want to block a mule to wait on an uWSGI signal instead of a message you can use uwsgi.signal_wait()
.
Use uwsgi.mule_msg()
to send a message to a programmed mule. Mule messages can be sent from anywhere in the uWSGI stack, including but not limited to workers, the spoolers, another mule.
# Send the string "ciuchino" to mule1.
# If you do not specify a mule ID, the message will be processed by the first available programmed mule.
uwsgi.mule_msg("ciuchino", 1)
As you can spawn an unlimited number of mules, you may need some form of synchronization – for example if you are developing a task management subsystem and do not want two mules to be able to start the same task simultaneously. You’re in luck – see Locks.
The uWSGI Spooler¶
Updated to uWSGI 2.0.1
Supported on: Perl, Python, Ruby
The Spooler is a queue manager built into uWSGI that works like a printing/mail system.
You can enqueue massive sending of emails, image processing, video encoding, etc. and let the spooler do the hard work in background while your users get their requests served by normal workers.
A spooler works by defining a directory in which “spool files” will be written, every time the spooler find a file in its directory it will parse it and will run a specific function.
You can have multiple spoolers mapped to different directories and even multiple spoolers mapped to the same one.
The --spooler <directory>
option allows you to generate a spooler process, while the --spooler-processes <n>
allows you to set how many processes to spawn for every spooler.
The spooler is able to manage uWSGI signals too, so you can use it as a target for your handlers.
This configuration will generate a spooler for your instance (myspool directory must exists)
[uwsgi]
spooler = myspool
...
while this one will create two spoolers:
[uwsgi]
spooler = myspool
spooler = myspool2
...
having multiple spoolers allows you to prioritize tasks (and eventually parallelize them)
Spool files¶
Spool files are serialized hashes/dictionaries of strings. The spooler will parse them and pass the resulting hash/dictionary to the spooler function (see below).
The serialization format is the same used for the ‘uwsgi’ protocol, so you are limited to 64k (even if there is a trick for passing bigger values, see the ‘body’ magic key below). The modifier1 for spooler packets is the 17, so a {‘hello’ => ‘world’} hash will be encoded as:
header | key1 | value1 |
---|---|---|
17|14|0|0 | |5|0|h|e|l|l|o | |5|0|w|o|r|l|d |
A locking system allows you to safely manually remove spool files if something goes wrong, or to move them between spooler directories.
Spool dirs over NFS are allowed, but if you do not have proper NFS locking in place, avoid mapping the same spooler NFS directory to spooler on different machines.
Setting the spooler function/callable¶
Because there are dozens of different ways to enqueue spooler requests, we’re going to cover receiving the requests first.
To have a fully operational spooler you need to define a “spooler function/callable” to process the requests.
Regardless of the the number of configured spoolers, the same function will be executed. It is up to the developer to instruct it to recognize tasks. If you don’t process requests, the spool directory will just fill up.
This function must returns an integer value:
- -2 (SPOOL_OK) – the task has been completed, the spool file will be removed
- -1 (SPOOL_RETRY) – something is temporarily wrong, the task will be retried at the next spooler iteration
- 0 (SPOOL_IGNORE) – ignore this task, if multiple languages are loaded in the instance all of them will fight for managing the task. This return values allows you to skip a task in specific languages.
Any other value will be interpreted as -1 (retry).
Each language plugin has its own way to define the spooler function:
Perl:
uwsgi::spooler(
sub {
my ($env) = @_;
print $env->{foobar};
return uwsgi::SPOOL_OK;
}
);
# hint - uwsgi:: is available when running using perl-exec= or psgi=
# no don't need to use "use" or "require" it, it's already there.
Python:
import uwsgi
def my_spooler(env):
print env['foobar']
return uwsgi.SPOOL_OK
uwsgi.spooler = my_spooler
Ruby:
module UWSGI
module_function
def spooler(env)
puts env.inspect
return UWSGI::SPOOL_OK
end
end
Spooler functions must be defined in the master process, so if you are in lazy-apps mode, be sure to place it in a file that is parsed early in the server setup. (in Python you can use –shared-import, in Ruby –shared-require, in Perl –perl-exec).
Python has support for importing code directly in the spooler with the --spooler-python-import
option.
Enqueueing requests to a spooler¶
The ‘spool’ api function allows you to enqueue a hash/dictionary into the spooler specified by the instance:
# add this to your instance .ini file
spooler=/path/to/spooler
# that's it! now use one of the code blocks below to send requests
# note: you'll still need to register some sort of receiving function (specified above)
# python
import uwsgi
uwsgi.spool({'foo': 'bar', 'name': 'Kratos', 'surname': 'the same of Zeus'})
# or
uwsgi.spool(foo='bar', name='Kratos', surname='the same of Zeus')
# for python3 use bytes instead of strings !!!
# perl
uwsgi::spool({foo => 'bar', name => 'Kratos', surname => 'the same of Zeus'})
# the uwsgi:: functions are available when executed within psgi or perl-exec
# ruby
UWSGI.spool(foo => 'bar', name => 'Kratos', surname => 'the same of Zeus')
Some keys have a special meaning:
- ‘spooler’ => specify the ABSOLUTE path of the spooler that has to manage this task
- ‘at’ => unix time at which the task must be executed (read: the task will not be run until the ‘at’ time is passed)
- ‘priority’ => this will be the subdirectory in the spooler directory in which the task will be placed, you can use that trick to give a good-enough prioritization to tasks (for better approach use multiple spoolers)
- ‘body’ => use this key for objects bigger than 64k, the blob will be appended to the serialzed uwsgi packet and passed back to the spooler function as the ‘body’ argument
注解
Spool arguments must be strings (or bytes for python3). The API functions will try to cast non-string values to strings/bytes, but do not rely on that functionality!
External spoolers¶
You could want to implement a centralized spooler for your server across many uWSGI instances.
A single instance will manage all of the tasks enqueued by multiple uWSGI instances.
To accomplish this setup, each uWSGI instance has to know which spooler directories are valid (consider it a form of security).
To add an external spooler directory use the --spooler-external <directory>
option, then add to it using the spool function.
The spooler locking subsystem will avoid any messes that you might think could occur.
Networked spoolers¶
You can even enqueue tasks over the network (be sure the ‘spooler’ plugin is loaded in your instance, but generally it is built in by default).
As we have already seen, spooler packets have modifier1 17, you can directly send those packets to an uWSGI socket of an instance with a spooler enabled.
We will use the Perl Net::uwsgi
module (exposing a handy uwsgi_spool function) in this example (but feel free to use whatever you want to write the spool files).
#!/usr/bin/perl
use Net::uwsgi;
uwsgi_spool('localhost:3031', {'test'=>'test001','argh'=>'boh','foo'=>'bar'});
uwsgi_spool('/path/to/my.sock', {'test'=>'test001','argh'=>'boh','foo'=>'bar'});
[uwsgi]
socket = /path/to/my.sock
socket = localhost:3031
spooler = /path/for/files
spooler-processes=1
perl-exec = /path/for/script-which-registers-spooler-sub.pl
...
(thanks brianhorakh for the example)
Priorities¶
We have already seen that you can use the ‘priority’ key to give order in spooler parsing.
While having multiple spoolers would be an extremely better approach, on system with few resources ‘priorities’ are a good trick.
They works only if you enable the --spooler-ordered
option. This option allows the spooler to scan directories entry in alphabetical order.
If during the scan a directory with a ‘number’ name is found, the scan is suspended and the content of this subdirectory will be explored for tasks.
/spool
/spool/ztask
/spool/xtask
/spool/1/task1
/spool/1/task0
/spool/2/foo
With this layout the order in which files will be parsed is:
/spool/1/task0
/spool/1/task1
/spool/2/foo
/spool/xtask
/spool/ztask
Remember, priorities only work for subdirectories named as ‘numbers’ and you need the --spooler-ordered
option.
The uWSGI spooler gives special names to tasks so the ordering of enqueuing is always respected.
Options¶
spooler=directory
run a spooler on the specified directory
spooler-external=directory
map spoolers requests to a spooler directory managed by an external instance
spooler-ordered
try to order the execution of spooler tasks (uses scandir instead of readdir)
spooler-chdir=directory
call chdir() to specified directory before each spooler task
spooler-processes=##
set the number of processes for spoolers
spooler-quiet
do not be verbose with spooler tasks
spooler-max-tasks=##
set the maximum number of tasks to run before recycling a spooler (to help alleviate memory leaks)
spooler-harakiri=##
set harakiri timeout for spooler tasks, see [harakiri] for more information.
spooler-frequency=##
set the spooler frequency
spooler-python-import=???
import a python module directly in the spooler
Tips and tricks¶
You can re-enqueue a spooler request by returning uwsgi.SPOOL_RETRY
in your callable:
def call_me_again_and_again(env):
return uwsgi.SPOOL_RETRY
You can set the spooler poll frequency using the --spooler-frequency <secs>
option (default 30 seconds).
You could use the The uWSGI caching framework or SharedArea – share memory pages between uWSGI components to exchange memory structures between spoolers and workers.
Python (uwsgidecorators.py) and Ruby (uwsgidsl.rb) exposes higher-level facilities to manage the spooler, try to use them instead of the low-level approach described here.
When using a spooler as a target for a uWSGI signal handler you can specify which one to route signal to using its ABSOLUTE directory name.
SNI - Server Name Identification (virtual hosting for SSL nodes)¶
uWSGI 1.9 (codenamed “ssl as p0rn”) added support for SNI (Server Name Identification) throughout the whole SSL subsystem. The HTTPS router, the SPDY router and the SSL router can all use it transparently.
SNI is an extension to the SSL standard which allows a client to specify a “name” for the resource
it wants. That name is generally the requested hostname, so you can implement virtual hosting-like behavior like you do using the HTTP Host:
header without requiring extra IP addresses etc.
In uWSGI an SNI object is composed of a name and a value. The name is the servername/hostname while the value is the “SSL context” (you can think of it as the sum of certificates, key and ciphers for a particular domain).
Adding SNI objects¶
To add an SNI object just use the --sni
option:
--sni <name> crt,key[,ciphers,client_ca]
For example:
--sni "unbit.com unbit.crt,unbit.key"
or for client-based SSL authentication and OpenSSL HIGH cipher levels
--sni "secure.unbit.com unbit.crt,unbit.key,HIGH,unbit.ca"
Adding complex SNI objects¶
Sometimes you need more complex keys for your SNI objects (like when using wildcard certificates)
If you have built uWSGI with PCRE/regexp support (as you should) you can use the --sni-regexp
option.
--sni-regexp "*.unbit.com unbit.crt,unbit.key,HIGH,unbit.ca"
Massive SNI hosting¶
One of uWSGI’s main purposes is massive hosting, so SSL without support for that would be pretty annoying.
If you have dozens (or hundreds, for that matter) of certificates mapped to the same IP address you can simply put them in a directory (following a simple convention we’ll elaborate in a bit) and let uWSGI scan it whenever it needs to find a context for a domain.
To add a directory just use
--sni-dir <path>
like
--sni-dir /etc/customers/certificates
Now, if you have unbit.com
and example.com
certificates (.crt) and keys (.key) just drop them in there following these naming rules:
/etc/customers/certificates/unbit.com.crt
/etc/customers/certificates/unbit.com.key
/etc/customers/certificates/unbit.com.ca
/etc/customers/certificates/example.com.crt
/etc/customers/certificates/example.com.key
As you can see, example.com
has no .ca file, so client authentication will be disabled for it.
If you want to force a default cipher set to the SNI contexts, use
--sni-dir-ciphers HIGH
(or whatever other value you need)
Note: Unloading SNI objects is not supported. Once they are loaded into memory they will be held onto until reload.
Subscription system and SNI¶
uWSGI 2.0 added support for SNI in the subscription system.
The https/spdy router and the sslrouter can dinamically load certificates and keys from the paths specified in a subscription packet:
uwsgi --subscribe2 key=mydomain.it,socket=0,sni_key=/foo/bar.key,sni_crt=/foo/bar.crt
the router will create a new SSL context based on the specified files (be sure the router can reach them) and will destroy it when the last node disconnect.
This is useful for massive hosting where customers have their certificates in the home and you want them the change/update those files without bothering you.
注解
We understand that directly encapsulating keys and cert in the subscription packets will be much more useful, but network transfer of keys is something really foolish from a security point of view. We are investigating if combining it with the secured subscription system (where each packet is encrypted) could be a solution.
The GeoIP plugin¶
The geoip
plugin adds new routing vars to your internal routing subsystem.
GeoIP’s vars are prefixed with the “geoip” tag. To build the geoip plugin you
need the official GeoIP C library and its headers. The supported databases are
the country and city one, and they are completely loaded on memory at startup.
The country database give access to the following variables:
${geoip[country_code]}
${geoip[country_code3]}
${geoip[country_name]}
while the city one offers a lot more at the cost of increased memory usage for storing the database
${geoip[continent]}
${geoip[country_code]}
${geoip[country_code3]}
${geoip[country_name]}
${geoip[region]}
${geoip[region_name]}
${geoip[city]}
${geoip[postal_code]}
${geoip[latitude]}
(${geoip[lat]}
)${geoip[longitude]}
(${geoip[lon]}
)${geoip[dma]}
${geoip[area]}
Enabling geoip lookup¶
To enable the GeoIP lookup system you need to load at least one database. After having loaded the geoip plugin you will get 2 new options:
--geoip-country
specifies a country database--geoip-city
specifies a city database
If you do not specify at least one of them, the system will always return empty strings.
An example¶
[uwsgi]
plugin = geoip
http-socket = :9090
; load the geoip city database
geoip-city = GeoLiteCity.dat
module = werkzeug.testapp:test_app
; first some debug info (addvar will ad WSGI variables you will see in the werkzeug testapp)
route-run = log:${geoip[country_name]}/${geoip[country_code3]}
route-run = addvar:COUNTRY=${geoip[country_name]}
route-run = log:${geoip[city]}/${geoip[region]}/${geoip[continent]}
route-run = addvar:COORDS=${geoip[lon]}/${geoip[lat]}
route-run = log:${geoip[region_name]}
route-run = log:${geoip[dma]}/${geoip[area]}
; then something more useful
; block access to all of the italians (hey i am italian do not start blasting me...)
route-if = equal:${geoip[country_name]};Italy break:403 Italians cannot see this site :P
; try to serve a specific page translation
route = ^/foo/bar/test.html static:/var/www/${geoip[country_code]}/test.html
Memory usage¶
The country database is tiny so you will generally have no problem in using it. Instead, the city database can be huge (from 20MB to more than 40MB). If you have lot of instances using the GeoIP city database and you are on a recent Linux system, consider using Using Linux KSM in uWSGI to reduce memory usage. All of the memory used by the GeoIP database can be shared by all instances with it.
uWSGI Transformations¶
Starting from uWSGI 1.9.7, a “transformations” API has been added to uWSGI internal routing.
A transformation is like a filter applied to the response generated by your application.
Transformations can be chained (the output of a transformation will be the input of the following one) and can completely overwrite response headers.
The most common example of transformation is gzip encoding. The output of your application is passed to a function compressing it with gzip and setting the Content-Encoding header. This feature rely on 2 external packages: libpcre3-dev, libz-dev on Ubuntu.
[uwsgi]
plugin = python,transformation_gzip
http-socket = :9090
; load the werkzeug test app
module = werkzeug.testapp:test_app
; if the client supports gzip encoding goto to the gzipper
route-if = contains:${HTTP_ACCEPT_ENCODING};gzip goto:mygzipper
route-run = last:
route-label = mygzipper
; pass the response to the gzip transformation
route = ^/$ gzip:
The cachestore
routing instruction is a transformation too, so you can cache various states of the response.
[uwsgi]
plugin = python,transformation_gzip
http-socket = :9090
; load the werkezeug test app
module = werkzeug.testapp:test_app
; create a cache of 100 items
cache = 100
; if the client support gzip encoding goto to the gzipper
route-if = contains:${HTTP_ACCEPT_ENCODING};gzip goto:mygzipper
route = ^/$ cache:key=werkzeug_homepage
route = ^/$ cachestore:key=werkzeug_homepage
route-run = last:
route-label = mygzipper
route = ^/$ cache:key=werkzeug_homepage.gz
; first cache the 'clean' response (for client not supporting gzip)
route = ^/$ cachestore:key=werkzeug_homepage
; then pass the response to the gzip transformation
route = ^/$ gzip:
; and cache it again in another item (gzipped)
route = ^/$ cachestore:key=werkzeug_homepage.gz
Another common transformation is applying stylesheets to XML files. (see The XSLT plugin)
The toxslt
transformation is exposed by the xslt
plugin:
uwsgi --plugin xslt --http-socket :9090 -w mycd --route-run "toxslt:stylesheet=t/xslt/cd.xml.xslt,params=foobar=test&agent=\${HTTP_USER_AGENT}"
The mycd
module here is a simple XML generator. Its output is then passed to the XSLT transformation.
Streaming vs. buffering¶
Each transformation announces itself as a “streaming” one or a “buffering” one.
Streaming ones are transformations that can be applied to response chunks (parts). An example of a streaming transformation is gzip (you do not need the whole body to begin compressing it). Buffering transformations are those requiring the full body before applying something to it. XSLT is an example of buffering transformation. Another example of buffering transformations are those used for storing response in some kind of cache.
If your whole pipeline is composed by only “streaming” transformations, your client will receive the output chunk by chunk. On the other hand a single buffering transformation will make the whole pipeline buffered, so your client will get the output only at the end.
An often using streaming functionality is gzip + chunked:
[uwsgi]
plugins = transformation_gzip,transformation_chunked
route-run = gzip:
route-run = chunked:
...
The whole transformation pipeline is composed by streaming plugins, so you will get each HTTP chunk in realtime.
Flushing magic¶
The “flush” transformation is a special one. It allows you to send the current contents of the transformation buffer to the client (without clearing the buffer).
You can use it for implementing streaming mode when buffering will be applied. A common example is having streaming + caching:
[uwsgi]
plugins = transformation_toupper,transform_tofile
; convert each char to uppercase
route-run = toupper:
; after each chunk converted to upper case, flush to the client
route-run = flush:
; buffer the whole response in memory for finally storing it in a file
route-run = tofile:filename=/tmp/mycache
...
You can call flush multiple times and in various parts of the chain. Experiment a bit with it...
Available transformations (last update 20130504)¶
gzip
, exposed by thetransformation_gzip
plugin (encode the response buffer to gzip)toupper
, exposed by thetransformation_toupper
plugin (example plugin transforming each character in uppercase)tofile
, exposed by thetransformation_tofile
plugin (used for caching to response buffer to a static file)toxslt
, exposed by thexslt
plugin (apply xslt stylesheet to an XML response buffer)cachestore
, exposed by therouter_cache
plugin (cache the response buffer in the uWSGI cache)chunked
, encode the output in HTTP chunkedflush
, flush the current buffer to the clientmemcachedstore
, store the response buffer in a memcached objectredisstore
, store the response buffer in a redis objecttemplate
, apply routing translations to each chunk
Working on¶
rpc
, allows applying rpc functions to a response buffer (limit 64k size)lua
, apply a lua function to a response buffer (no limit in size)
WebSocket support¶
In uWSGI 1.9, a high performance websocket (RFC 6455) implementation has been added.
Although many different solutions exist for WebSockets, most of them rely on a higher-level language implementation, that rarely is good enough for topics like gaming or streaming.
The uWSGI websockets implementation is compiled in by default.
Websocket support is sponsored by 20Tab S.r.l. http://20tab.com/
They released a full game (a bomberman clone based on uWSGI websockets api): https://github.com/20tab/Bombertab
An echo server¶
This is how a uWSGI websockets application looks like:
def application(env, start_response):
# complete the handshake
uwsgi.websocket_handshake(env['HTTP_SEC_WEBSOCKET_KEY'], env.get('HTTP_ORIGIN', ''))
while True:
msg = uwsgi.websocket_recv()
uwsgi.websocket_send(msg)
You do not need to worry about keeping the connection alive or reject dead peers. The uwsgi.websocket_recv()
function will do all of the dirty work for you in background.
Handshaking¶
Handshaking is the first phase of a websocket connection.
To send a full handshake response you can use the uwsgi.websocket_handshake([key,origin, proto])
function. Without a correct handshake the connection will never complete.
In the 1.9 series, the key parameter is required. In 2.0+ you can call websocket_handshake without arguments (the response will be built automatically from request’s data).
Sending¶
Sending data to the browser is really easy. uwsgi.websocket_send(msg)
– nothing more.
Receiving¶
This is the real core of the whole implementation.
This function actually lies about its real purpose. It does return a websocket message, but it really also holds the connection opened (using the ping/pong subsystem) and monitors the stream’s status.
msg = uwsgi.websocket_recv()
The function can receive messages from a named channel (see below) and automatically forward them to your websocket connection.
It will always return only websocket messages sent from the browser – any other communication happens in the background.
There is a non-blocking variant too – msg = uwsgi.websocket_recv_nb()
. See: https://github.com/unbit/uwsgi/blob/master/tests/websockets_chat_async.py
PING/PONG¶
To keep a websocket connection opened, you should constantly send ping (or pong, see later) to the browser and expect
a response from it. If the response from the browser/client does not arrive in a timely fashion the connection is closed (uwsgi.websocket_recv()
will raise an exception). In addition to ping, the uwsgi.websocket_recv()
function send the so called ‘gratuitous pong’. They are used
to inform the client of server availability.
All of these tasks happen in background. YOU DO NOT NEED TO MANAGE THEM!
Available proxies¶
Unfortunately not all of the HTTP webserver/proxies work flawlessly with websockets.
The uWSGI HTTP/HTTPS/SPDY router supports them without problems. Just remember to add the
--http-websockets
option.uwsgi --http :8080 --http-websockets --wsgi-file myapp.py
or
uwsgi --http :8080 --http-raw-body --wsgi-file myapp.py
This is slightly more “raw”, but supports things like chunked input.
- Haproxy works fine.
- nginx >= 1.4 works fine and without additional configuration.
Language support¶
- Python https://github.com/unbit/uwsgi/blob/master/tests/websockets_echo.py
- Perl https://github.com/unbit/uwsgi/blob/master/tests/websockets_echo.pl
- PyPy https://github.com/unbit/uwsgi/blob/master/tests/websockets_chat_async.py
- Ruby https://github.com/unbit/uwsgi/blob/master/tests/websockets_echo.ru
- Lua https://github.com/unbit/uwsgi/blob/master/tests/websockets_echo.lua
Supported concurrency models¶
- Multiprocess
- Multithreaded
- uWSGI native async api
- Coro::AnyEvent
- gevent
- Ruby fibers + uWSGI async
- Ruby threads
- greenlet + uWSGI async
- uGreen + uWSGI async
- PyPy continulets
wss:// (websockets over https)¶
The uWSGI HTTPS router works without problems with websockets. Just remember to use wss:// as the connection scheme in your client code.
Websockets over SPDY¶
n/a
Routing¶
The http proxy internal router supports websocket out of the box (assuming your front-line proxy already supports them)
[uwsgi]
route = ^/websocket uwsgi:127.0.0.1:3032,0,0
or
[uwsgi]
route = ^/websocket http:127.0.0.1:8080
Api¶
uwsgi.websocket_handshake([key, origin, proto])
uwsgi.websocket_recv()
uwsgi.websocket_send(msg)
uwsgi.websocket_send_binary(msg) (added in 1.9.21 to support binary messages)
uwsgi.websocket_recv_nb()
uwsgi.websocket_send_from_sharedarea(id, pos) (added in 1.9.21, allows sending directly from a SharedArea – share memory pages between uWSGI components)
uwsgi.websocket_send_binary_from_sharedarea(id, pos) (added in 1.9.21, allows sending directly from a SharedArea – share memory pages between uWSGI components)
The Metrics subsystem¶
Available from 1.9.19.
The uWSGI metrics subsystem allows you to manage “numbers” from your app.
While the caching subsystem got some math capabilities during the 1.9 development cycle, the metrics subsystem is optimized by design for storing numbers and applying functions over them. So, compared to the caching subsystem it’s way faster and requires a fraction of the memory.
When enabled, the metric subsystem configures a vast amount of metrics (like requests per-core, memory usage, etc) but, in addition to this, you can configure your own metrics, such as the number of active users or, say, hits of a particular URL, as well as the memory consumption of your app or the whole server.
To enable the metrics subsystem just add --enable-metrics
to your options, or configure a stats pusher (see below).
The metrics subsystem is completely thread-safe.
By default uWSGI creates a lot of metrics (and more are planned), so before adding your own be sure uWSGI does not already expose the one(s) you need.
Metric names and oids¶
Each metric must have a name (containing only numbers, letters, underscores, dashes and dots) and an optional oid
(required for mapping a metric to The embedded SNMP server).
Metric types¶
Before dealing with metrics you need to understand the various types represented by each metric:
COUNTER (type 0)¶
This is a generally-growing up number (like the number of requests).
GAUGE (type 1)¶
This is a number that can increase or decrease dynamically (like the memory used by a worker, or CPU load).
ABSOLUTE (type 2)¶
This is an absolute number, like the memory of the whole server, or the size of the hard disk.
ALIAS (type 3)¶
This is a virtual metric pointing to another one . You can use it to give different names to already existing metrics.
Metric collectors¶
Once you define a metric type, you need to tell uWSGI how to ‘collect’ the specific metric.
There are various collectors available (and more can be added via plugins).
ptr
– The value is collected from a memory pointerfile
– the value is collected from a filesum
– the value is the sum of other metricsavg
– compute the algebraic average of the children (added in 1.9.20)accumulator
– always add the sum of children to the final value. See below for an example.Round 1: child1 = 22, child2 = 17 -> metric_value = 39 Round 2: child1 = 26, child2 = 30 -> metric_value += 56
multiplier
- Multiply the sum of children by the specified argument (arg1n).child1 = 22, child2 = 17, arg1n = 3 -> metric_value = (22+17)*3
func
- the value is computed calling a specific function every timemanual
- the NULL collector. The value must be updated manually from applications using the metrics API.
Custom metrics¶
You can define additional metrics to manage from your app.
The --metric
option allows you to add more metrics.
It has two syntaxes: “simplified” and “keyval”.
uwsgi --http-socket :9090 --metric foobar
will create a metric ‘foobar’ with type ‘counter’, manual collector and no oid.
For creating advanced metrics you need the keyval way:
uwsgi --http-socket :9090 --metric name=foobar,type=gauge,oid=100.100.100
The following keys are available:
name
– set the metric nameoid
– set the metric oidtype
– set the metric type, can becounter
,gauge
,absolute
,alias
initial_value
– set the metric to a specific value on startupfreq
– set the collection frequency in seconds (default to 1)reset_after_push
– reset the metric to zero (or the configuredinitial_value
) after it’s been pushed to the backend (so everyfreq
seconds)children
– maps children to the metric (see below)alias
– the metric will be a simple alias for the specified one (–metric name=foobar,alias=worker.0.requests,type=alias)arg1
toarg3
– string based arguments (see below)arg1n
toarg3n
– number based arguments (see below)collector
set the collector, can beptr
,file
,sum
,func
or anything exposed by plugins. Not specifying a collector means the metric is manual (your app needs to update it).
The ptr is currently unimplemented, while the other collector requires a bit of additional configuration:
collector=file
requires arg1
for the filename and an optional arg1n
for the so-called split value.
uwsgi --metric name=loadavg,type=gauge,collector=file,arg1=/proc/loadavg,arg1n=1,freq=3
This will add a ‘loadavg` metric, of type gauge, updated every 3 seconds with the content of /proc/loadavg
. The content is split (using \n, \t, spaces, \r and zero as separator) and the item 1 (the returned array is zero-based) used as the return value.
The splitter is very powerful, making it possible to gather information from more complex files, such as /proc/meminfo
.
uwsgi --metric name=memory,type=gauge,collector=file,arg1=/proc/meminfo,arg1n=4,freq=3
Once split, /proc/meminfo
has the MemFree value in the 4th slot.
collector=sum
requires the list of metrics that must be summed up. Each metric has the concept of ‘children’. The sum collector
will sum the values of all of its children:
uwsgi --metric name=reqs,collector=sum,children=worker.1.requests;worker.2.requests
This will sum the value of worker.1.requests and worker.2.requests every second.
collector=func
is a convenience collector avoiding you to write a whole plugin for adding a new collector.
Let’s define a C function (call the file mycollector.c or whatever you want):
int64_t my_collector(void *metric) {
return 173;
}
and build it as a shared library...
gcc -shared -o mycollector.so mycollector.c
now run uWSGI loading the library...
uwsgi --dlopen ./mycollector.so --metric name=mine,collector=func,arg1=my_collector,freq=10
this will call the C function my_collector every 10 seconds and will set the value of the metric ‘mine’ to its return value.
The function must returns an int64_t
value. The argument it takes is a uwsgi_metric
pointer. You generally do not need to parse the metric, so just casting to void will avoid headaches.
The metrics directory¶
UNIX sysadmins love text files. They are generally the things they have to work on most of the time. If you want to make a UNIX sysadmin happy, just give him or her some text file to play with. (Or some coffee, or whiskey maybe, depending on their tastes. But generally, text files should do just fine.)
The metrics subsystem can expose all of its metrics in the form of text files in a directory:
uwsgi --metrics-dir mymetrics ...
The directory must exist in advance.
This will create a text file for each metric in the ‘mymetrics’ directory. The content of each file is the value of the metric (updated in real time).
Each file is mapped into the process address space, so do not worry if your virtual memory increases slightly.
Restoring metrics (persistent metrics)¶
When you restart a uWSGI instance, all of its metrics are reset.
This is generally the best thing to do, but if you want, you can restore the previous situation using the values stored in the metrics directory defined before.
Just add the --metrics-dir-restore
option to force the metric subsystem to read-back the values from the metric directory before
starting to collect values.
API¶
Your language plugins should expose at least the following api functions. Currently they are implemented in Perl, CPython, PyPy and Ruby
metric_get(name)
metric_set(name, value)
metric_set_max(name, value)
– only set the metric name if the give value is greater than the one currently storedmetric_set_min(name, value)
– only set the metric name if the give value is lower than the one currently storedmetric_set_max
andmetric_set_min
can be used to avoid having to callmetric_get
when you need a metric to be set at a maximal or minimal value. Another simple use case is to use theavg
collector to calculate an average between some max and min set metrics.metric_inc(name[, delta])
metric_dec(name[, delta])
metric_mul(name[, delta])
metric_div(name[, delta])
metrics (tuple/array of metric keys, should be immutable and not-callable, currently unimplemented)
Stats pushers¶
Collected metrics can be sent to external systems for analysis or chart generation.
Stats pushers are plugins aimed at sending metrics to those systems.
There are two kinds of stats pushers at the moment: JSON and raw.
The JSON stats pusher send the whole JSON stats blob (the same you get from the stats server), while ‘raw’ ones send the metrics list.
Currently available stats pushers:
rrdtool¶
- Type: raw
- Plugin: rrdtool (builtin by default)
- Requires (during runtime): librrd.so
- Syntax:
--stats-push rrdtool:my_rrds ...
This will store an rrd file for each metric in the specified directory. Each rrd file has a single data source named ‘metric’.
Usage:
uwsgi --rrdtool my_rrds ...
# or
uwsgi --stats-push rrdtool:my_rrds ...
By default the RRD files are updated every 300 seconds. You can tune this value with --rrdtool-freq
The librrd.so library is detected at runtime. If you need you can specify its absolute path with --rrdtool-lib
.
statsd¶
- Type: raw
- Plugin: stats_pusher_statsd
- Syntax:
--stats-push statsd:address[,prefix]
Push metrics to a statsd server.
Usage:
uwsgi --stats-push statsd:127.0.0.1:8125,myinstance ...
carbon¶
- Type: raw
- Plugin: carbon (built-in by default)
- See: Integration with Graphite/Carbon
zabbix¶
- Type: raw
- Plugin: zabbix
- Syntax:
--stats-push zabbix:address[,prefix]
Push metrics to a zabbix server.
The plugin exposes a --zabbix-template
option that will generate a zabbix template (on stdout or in the specified file) containing all of the exposed metrics as trapper items.
注解
On some Zabbix versions you will need to authorize the IP addresses allowed to push items.
Usage:
uwsgi --stats-push zabbix:127.0.0.1:10051,myinstance ...
mongodb¶
- Type: json
- Plugin: stats_pusher_mongodb
- Required (build time): libmongoclient.so
- Syntax (keyval):
--stats-push mongodb:addr=<addr>,collection=<db>,freq=<freq>
Push statistics (as JSON) the the specified MongoDB database.
socket¶
- Type: raw
- Plugin: stats_pusher_socket (builtin by default)
- Syntax:
--stats-push socket:address[,prefix]
Push metrics to a UDP server with the following format: <metric> <type> <value>
(<type> is in the numeric form previously reported).
Example:
uwsgi --stats-push socket:127.0.0.1:8125,myinstance ...
Alarms/Thresholds¶
You can configure one or more “thresholds” for each metric.
Once this limit is reached the specified alarm (see The uWSGI alarm subsystem (from 1.3)) is triggered.
Once the alarm is delivered you may choose to reset the counter to a specific value (generally 0), or continue triggering alarms with a specified rate.
[uwsgi]
...
metric-alarm = key=worker.0.avg_response_time,value=2000,alarm=overload,rate=30
metric-alarm = key=loadavg,value=3,alarm=overload,rate=120
metric-threshold = key=mycounter,value=1000,reset=0
...
Specifying an alarm is not required. Using the threshold value to automatically reset a metric is perfectly valid.
Note: --metric-threshold
and --metric-alarm
are aliases for the same option.
SNMP integration¶
The The embedded SNMP server server exposes metrics starting from the 1.3.6.1.4.1.35156.17.3 OID.
For example to get the value of worker.0.requests
:
snmpget -v2c -c <snmp_community> <snmp_addr>:<snmp_port> 1.3.6.1.4.1.35156.17.3.0.1
Remember: only metrics with an associated OID can be used via SNMP.
Internal Routing integration¶
The ‘’router_metrics’’ plugin (builtin by default) adds a series of actions to the internal routing subsystem.
metricinc:<metric>[,value]
increase the <metric>metricdec:<metric>[,value]
decrease the <metric>metricmul:<metric>[,value]
multiply the <metric>metricdiv:<metric>[,value]
divide the <metric>metricset:<metric>,<value>
set <metric> to <value>
In addition to action, a route var named “metric” is added.
Example:
[uwsgi]
metric = mymetric
route = ^/foo metricinc:mymetric
route-run = log:the value of the metric 'mymetric' is ${metric[mymetric]}
log-format = %(time) - %(metric.mymetric)
Request logging¶
You can access metrics values from your request logging format using the %(metric.xxx) placeholder:
[uwsgi]
log-format = [hello] %(time) %(metric.worker.0.requests)
Officially Registered Metrics¶
This is a work in progress.
The best way to know which default metrics are exposed is enabling the stats server and querying it (or adding the --metrics-dir
option).
- worker/3 (exports information about workers, example worker.1.requests [or 3.1.1] reports the number of requests served by worker 1)
- plugin/4 (namespace for metrics automatically added by plugins, example plugins.foo.bar)
- core/5 (namespace for general instance informations)
- router/6 (namespace for corerouters, example router.http.active_sessions)
- socket/7 (namespace for sockets, example socket.0.listen_queue)
- mule/8 (namespace for mules, example mule.1.signals)
- spooler/9 (namespace for spoolers, example spooler.1.signals)
- system/10 (namespace for system metrics, like loadavg or free memory)
OID assigment for plugins¶
If you want to write a plugin that will expose metrics, please add the OID namespace that you are going to use to the list below and make a pull request first.
This will ensure that all plugins are using unique OID namespaces.
Prefix all plugin metric names with plugin name to ensure no conflicts if same keys are used in multiple plugins (example plugin.myplugin.foo.bar, worker.1.plugin.myplugin.foo.bar)
- (3|4).100.1 - cheaper_busyness
External tools¶
The Chunked input API¶
An API for managing HTTP chunked input requests has been added in uWSGI 1.9.13.
The API is very low-level to allow easy integration with standard apps.
There are only two functions exposed:
chunked_read([timeout])
chunked_read_nb()
This API is supported (from uWSGI 1.9.20) on CPython, PyPy and Perl.
Reading chunks¶
To read a chunk (blocking) just run
my $msg = uwsgi::chunked_read
If no timeout is specified, the default one will be used. If you do not get a chunk in time, the function will croak (or raise an exception when under Python).
Under non-blocking/async engines you may want to use
my $msg = uwsgi::chunked_read_nb
The function will soon return undef
(or None
on Python) if no chunks are available (and croak/raise an exception on error).
A full PSGI streaming echo example:
# simple PSGI echo app reading chunked input
sub streamer {
$responder = shift;
# generate the headers and start streaming the response
my $writer = $responder->( [200, ['Content-Type' => 'text/plain']]);
while(1) {
my $msg = uwsgi::chunked_read;
last unless $msg;
$writer->write($msg);
}
$writer->close;
}
my $app = sub {
return \&streamer;
};
Tuning the chunks buffer¶
Before starting to read chunks, uWSGI allocates a fixed buffer for storing chunks.
All of the messages are always stored in the same buffer. If a message bigger than the buffer is received, an exception will be raised.
By default the buffer is limited to 1 MB. You can tune it with the --chunked-input-limit
option (bytes).
Integration with proxies¶
If you plan to put uWSGI behind a proxy/router be sure it supports chunked input requests (or generally raw HTTP requests).
When using the uWSGI HTTP router just add –http-raw-body to support chunked input.
HAProxy works out of the box.
Nginx >= 1.4 supports chunked input.
Options¶
--chunked-input-limit
: the limit (in bytes) of a chunk message (default 1MB)--chunked-input-timeout
: the default timeout (in seconds) for blocking chunked_read (default to the same –socket-timeout value, 4 seconds)
Notes¶
- Calling chunked API functions after having consumed even a single byte of the request body is wrong (this includes
--post-buffering
). - Chunked API functions can be called independently by the presence of “Transfer-Encoding: chunked” header.
Scaling with uWSGI¶
The uWSGI cheaper subsystem – adaptive process spawning¶
uWSGI provides the ability to dynamically scale the number of running workers
via pluggable algorithms. Use uwsgi --cheaper-algos-list
to get the list
of available algorithms.
Usage¶
To enable cheaper mode add the cheaper = N
option to the uWSGI
configuration file, where N is the minimum number of workers uWSGI can run. The
cheaper
value must be lower than the maximum number of configured workers
(workers
or processes
option).
# set cheaper algorithm to use, if not set default will be used
cheaper-algo = spare
# minimum number of workers to keep at all times
cheaper = 2
# number of workers to spawn at startup
cheaper-initial = 5
# maximum number of workers that can be spawned
workers = 10
# how many workers should be spawned at a time
cheaper-step = 1
This configuration will tell uWSGI to run up to 10 workers under load. If the
app is idle uWSGI will stop workers but it will always leave at least 2 of them
running. With cheaper-initial
you can control how many workers should be
spawned at startup. If your average load requires more than minimum number of
workers you can have them spawned right away and then “cheaped” (killed off) if
load is low enough. When the cheaper algorithm decides that it needs more
workers it will spawn cheaper-step
of them. This is useful if you have a
high maximum number of workers – in the event of a sudden load spike it would
otherwise take a lot of time to spawn enough workers one by one.
Setting memory limits¶
Starting with 1.9.16 rss memory limits can be set to stop cheaper spawning new workers if process count limit was not reached, but total sum of rss memory used by all workers reached given limit.
# soft limit will prevent cheaper from spawning new workers
# if workers total rss memory is equal or higher
# we use 128MB soft limit below (values are in bytes)
cheaper-rss-limit-soft = 134217728
# hard limit will force cheaper to cheap single worker
# if workers total rss memory is equal or higher
# we use 160MB hard limit below (values are in bytes)
cheaper-rss-limit-hard = 167772160
Notes:
- Hard limit is optional, soft limit alone can be used.
- Hard value must be higher then soft value, both values shouldn’t be too close to each other.
- Hard value should be soft + at least average worker memory usage for given app.
- Soft value is the limiter for cheaper, it won’t spawn more workers, but already running workers memory usage might grow, to handle that reload-on-rss can be set too. To set unbreakable barrier for app memory usage cgroups are recommended.
spare
cheaper algorithm¶
This is the default algorithm. If all workers are busy for
cheaper_overload
seconds then uWSGI will spawn new workers. When the load
is gone it will begin stopping processes one at a time.
backlog
cheaper algorithm¶
注解
backlog
is only available on Linux and only on TCP sockets (not UNIX domain sockets).
If the socket’s listen queue has more than cheaper_overload
requests
waiting to be processed, uWSGI will spawn new workers. If the backlog is lower
it will begin killing processes one at a time.
busyness
cheaper algorithm¶
注解
This algorithm is optional, it is only available if the cheaper_busyness
plugin is compiled and loaded.
This plugin implements an algorithm which adds or removes workers based on average utilization for a given time period. It’s goal is to keep more workers than the minimum needed available at any given time, so the app will always have capacity for new requests. If you want to run only minimum number of workers then use the spare or backlog algorithms.
This plugin primarily is used because the way spare and backlog plugins work
causes very aggressive scaling behavior. If you set a low cheaper
value
(for example 1), then uWSGI will keep only 1 worker running and spawn new
workers only when that running worker is overloaded. If an app requires more
workers, then uWSGI will be spawning and stopping workers all the time. Only
during times of very low load the would the minimum number of workers be
enough.
The Busyness algorithm tries to do the opposite: spawn as many workers as needed and stop some of them only when there is a good chance that they are not needed. This should lead to a more stable worker count and much less respawns. Since for most of the time we have more worker capacity than actually needed, average application response times should be lower than with other plugins.
Options:
cheaper-overload¶
Specifies the window, in seconds, for tracking the average busyness of workers. Example:
cheaper-overload = 30
This option will check busyness every 30 seconds. If during the last 30 seconds
all workers were running for 3 seconds and idle for the remaining 27 seconds
the calculated busyness will be 10% (3/30). This value will decide how fast
uWSGI can respond to load spikes. New workers will be spawned at most every
cheaper-overload
seconds (unless you are running uWSGI on Linux – see
cheaper-busyness-backlog-alert
for details).
If you want to react to load spikes faster, keep this value low so busyness is
calculated more often. Keep in mind this may cause workers to be
started/stopped more often than required since every minor spike may spawn new
workers. With a high cheaper-overload
value the worker count will change
much less since longer cycles will eat all short spikes of load and extreme
values.
Default is 3, for busyness plugin it’s best to use higher value (10-30).
cheaper-step¶
How many workers to spawn when the algorithm decides they are needed. Default is 1.
cheaper-initial¶
The number of workers to be started when starting the application. After the app is started the algorithm can stop or start workers if needed.
cheaper-busyness-max¶
This is the maximum busyness we allow. Every time the calculated busyness for
last cheaper-overload
seconds is higher than this value, uWSGI will spawn
cheaper-step
new workers. Default is 50.
cheaper-busyness-min¶
This is minimum busyness. If current busyness is below this value, the app is considered as being in an “idle cycl” and uWSGI will start counting them. Once we reach needed number of idle cycles uWSGI will kill one worker. Default is 25.
cheaper-busyness-multiplier¶
This option tells uWSGI how many idle cycles we need before stopping a worker. After reaching this limit uWSGI will stop a worker and reset this counter.
For example:
cheaper-overload = 10
cheaper-busyness-multiplier = 20
cheaper-busyness-min = 25
If average worker busyness is under 25% for 20 checks in a row, executed every
10 seconds (total of 200 seconds), tone worker will be stopped. The idle cycles
counter will be reset if average busyness jumps above cheaper-busyness-max
and we spawn new workers. If during idle cycle counting the average busyness
jumps above cheaper-busyness-min
but still below cheaper-busyness-max
,
then the idle cycles counter is adjusted and we need to wait extra one idle
cycle. If during idle cycle counting the average busyness jumps above
cheaper-busyness-min
but still below cheaper-busyness-max
three times
in a row, then the idle cycle counter is reset.
cheaper-busyness-penalty¶
uWSGI will automatically tune the number of idle cycles needed to stop worker
when worker is stopped due to enough idle cycles and then spawned back to fast
(less than the same time we need to cheap worker), then we will increment the
cheaper-busyness-multiplier
value this value. Default is 1.
Example:
cheaper-overload = 10
cheaper-busyness-multiplier = 20
cheaper-busyness-min = 25
cheaper-busyness-penalty = 2
If average worker busyness is under 25% for 20 checks in a row, executed every
10 seconds (total 200 seconds), one worker will be stopped. If new worker is
spawned in less than 200 seconds (counting from the time when we spawned the
last worker before it), the cheaper-busyness-multiplier
value will be
incremented up to 22 (20+2). Now we will need to wait 220 seconds (22*10) to
cheap another worker. This option is used to prevent workers from being
started and stopped all the time since once we stop one worker, busyness might
jump up enough to hit cheaper-busyness-max
. Without this, or if tuned
poorly, we can get into a stop/start feedback loop .
cheaper-busyness-verbose¶
This option enables debug logs from the cheaper_busyness
plugin.
cheaper-busyness-backlog-alert¶
This option is only available on Linux. It is used to allow quick response to
load spikes even with high cheaper-overload
values. On every uWSGI master
cycle (default 1 second) the current listen queue is checked. If it is higher
than this value, an emergency worker is spawned. When using this option it is
safe to use high cheaper-overload
values to have smoother scaling of worker
count. Default is 33.
cheaper-busyness-backlog-multiplier¶
This option is only available on Linux. It works just like
cheaper-busyness-multiplier
, except it is used only for emergency workers
spawned when listen queue was higher than cheaper-busyness-backlog-alert
.
Emergency workers are spawned in case of big load spike to prevent currently running workers from being overloaded. Sometimes load spike are random and short which can spawn a lot of emergency workers. In such cases we would need to wait several cycles before reaping those workers. This provides an alternate multiplier to reap these processes faster. Default is 3.
cheaper-busyness-backlog-step¶
This option is only available on Linux. It sets the number of emergency workers
spawned when listen queue is higher than cheaper-busyness-backlog-alert
.
Defaults to 1.
cheaper-busyness-backlog-nonzero¶
This option is only available on Linux. It will spawn new emergency workers if the request listen queue is > 0 for more than N seconds. It is used to protect the server from the corner case where there is only a single worker running and the worker is handling a long running request. If uWSGI receives new requests they would stay in the request queue until that long running request is completed. With this option we can detect such a condition and spawn new worker to prevent queued requests from being timed out. Default is 60.
Notes regarding Busyness¶
Experiment with settings, there is no one golden rule of what values should be used for everyone. Test and pick values that are best for you. Monitoring uWSGI stats (via Carbon, for instance) will make it easy to decide on good values.
Don’t expect busyness to be constant. it will change frequently. In the end, real users interact with your apps in very random way. It’s recommended to use longer –cheaper-overload values (>=30) to have less spikes.
If you want to run some benchmarks with this plugin, you should use tools that add randomness to the work load
With a low number of workers (2-3) starting new worker or stopping one might affect busyness a lot, if You have 2 workers with busyness of 50%, than stopping one of them will increase busyness to 100%. Keep that in mind when picking min and max levels, with only few workers running most of the time max should be more than double of min, otherwise every time one worker is stopped it might increase busyness to above max level.
With a low number of workers (1-4) and default settings expect this plugin will keep average busyness below the minimum level; adjust levels to compensate for this.
With a higher number of workers required to handle load, worker count should stabilize somewhere near minimum busyness level, jumping a little bit around this value
When experimenting with this plugin it is advised to enable
--cheaper-busyness-verbose
to get an idea of what it is doing. An example log follows.# These messages are logged at startup to show current settings [busyness] settings: min=20%, max=60%, overload=20, multiplier=15, respawn penalty=3 [busyness] backlog alert is set to 33 request(s) # With --cheaper-busyness-verbose enabled You can monitor calculated busyness [busyness] worker nr 1 20s average busyness is at 11% [busyness] worker nr 2 20s average busyness is at 11% [busyness] worker nr 3 20s average busyness is at 20% [busyness] 20s average busyness of 3 worker(s) is at 14% # Average busyness is under 20%, we start counting idle cycles # we have overload=20 and multiplier=15 so we need to wait 300 seconds before we can stop worker # cycle we just had was counted as idle so we need to wait another 280 seconds # 1 missing second below is just from rounding, master cycle is every 1 second but it also takes some time, this is normal [busyness] need to wait 279 more second(s) to cheap worker # We waited long enough and we can stop one worker [busyness] worker nr 1 20s average busyness is at 6% [busyness] worker nr 2 20s average busyness is at 22% [busyness] worker nr 3 20s average busyness is at 19% [busyness] 20s average busyness of 3 worker(s) is at 15% [busyness] 20s average busyness is at 15%, cheap one of 3 running workers # After stopping one worker average busyness is now higher, which is no surprise [busyness] worker nr 2 20s average busyness is at 36% [busyness] worker nr 3 20s average busyness is at 24% [busyness] 20s average busyness of 2 worker(s) is at 30% # 30% is above our minimum (20%), but it's still far from our maximum (60%) # since this is not idle cycle uWSGI will ignore it when counting when to stop worker [busyness] 20s average busyness is at 30%, 1 non-idle cycle(s), adjusting cheaper timer # After a while our average busyness is still low enough, so we stop another worker [busyness] 20s average busyness is at 3%, cheap one of 2 running workers # With only one worker running we won't see per worker busyness since it's the same as total average [busyness] 20s average busyness of 1 worker(s) is at 16% [busyness] 20s average busyness of 1 worker(s) is at 17% # Shortly after stopping second worker and with only one running we have load spike that is enough to hit our maximum level # this was just few cycles after stopping worker so uWSGI will increase multiplier # now we need to wait extra 3 cycles before stopping worker [busyness] worker(s) respawned to fast, increasing cheaper multiplier to 18 (+3) # Initially we needed to wait only 300 seconds, now we need to have 360 subsequent seconds when workers busyness is below minimum level # 10*20 + 3*20 = 360 [busyness] worker nr 1 20s average busyness is at 9% [busyness] worker nr 2 20s average busyness is at 17% [busyness] worker nr 3 20s average busyness is at 17% [busyness] worker nr 4 20s average busyness is at 21% [busyness] 20s average busyness of 4 worker(s) is at 16% [busyness] need to wait 339 more second(s) to cheap worker
The uWSGI Emperor – multi-app deployment¶
If you need to deploy a big number of apps on a single server, or a group of servers, the Emperor mode is just the ticket. It is a special uWSGI instance that will monitor specific events and will spawn/stop/reload instances (known as vassals, when managed by an Emperor) on demand.
By default the Emperor will scan specific directories for supported (.ini,
.xml, .yml, .json, etc.) uWSGI configuration files, but it is extensible using
imperial monitor plugins. The dir://
and glob://
plugins are
embedded in the core, so they need not be loaded, and are automatically
detected. The dir://
plugin is the default.
- Whenever an imperial monitor detects a new configuration file, a new uWSGI instance will be spawned with that configuration.
- Whenever a configuration file is modified (its modification time changed, so
touch --no-dereference
may be your friend), the corresponding app will be reloaded. - Whenever a config file is removed, the corresponding app will be stopped.
- If the emperor dies, all the vassals die.
- If a vassal dies for any reason, the emperor will respawn it.
Multiple sources of configuration may be monitored by specifying --emperor
multiple times.
参见
See Imperial monitors for a list of the Imperial Monitor plugins shipped with uWSGI and how to use them.
Imperial monitors¶
dir://
– scan a directory for uWSGI config files¶
Simply put all of your config files in a directory, then point the uWSGI emperor to it. The Emperor will start scanning this directory. When it finds a valid config file it will spawn a new uWSGI instance.
For our example, we’re deploying a Werkzeug test app, a Trac instance, a Ruby on Rails app and a Django app.
werkzeug.xml
<uwsgi>
<module>werkzeug.testapp:test_app</module>
<master/>
<processes>4</processes>
<socket>127.0.0.1:3031</socket>
</uwsgi>
trac.ini
[uwsgi]
master = true
processes = 2
module = trac.web.main:dispatch_request
env = TRAC_ENV=/opt/project001
socket = 127.0.0.1:3032
rails.yml
uwsgi:
plugins: rack
rack: config.ru
master: 1
processes: 8
socket: 127.0.0.1:3033
post-buffering: 4096
chdir: /opt/railsapp001
django.ini
[uwsgi]
socket = 127.0.0.1:3034
threads = 40
master = 1
env = DJANGO_SETTINGS_MODULE=myapp.settings
module = django.core.handlers.wsgi:WSGIHandler()
chdir = /opt/djangoapp001
Put these 4 files in a directory, for instance /etc/uwsgi/vassals
in our example, then spawn the Emperor:
uwsgi --emperor /etc/uwsgi/vassals
The emperor will find the uWSGI instance configuration files in that directory
(the dir://
plugin declaration is implicit) and start the daemons needed to
run them.
glob://
– monitor a shell pattern¶
glob://
is similar to dir://
, but a glob expression must be specified:
uwsgi --emperor "/etc/vassals/domains/*/conf/uwsgi.xml"
uwsgi --emperor "/etc/vassals/*.ini"
注解
Remember to quote the pattern, otherwise your shell will most likely interpret it and expand it at invocation time, which is not what you want.
As the Emperor can search for configuration files in subdirectory hierarchies, you could have a structure like this:
/opt/apps/app1/app1.xml
/opt/apps/app1/...all the app files...
/opt/apps/app2/app2.ini
/opt/apps/app2/...all the app files...
and run uWSGI with:
uwsgi --emperor /opt/apps/app*/app*.*
pg://
– scan a PostgreSQL table for configuration¶
You can specify a query to run against a PostgreSQL database. Its result must be a list of 3 to 5 fields defining a vassal:
- The instance name, including a valid uWSGI config file extension. (Such as
django-001.ini
) - A
TEXT
blob containing the vassal configuration, in the format based on the extension in field 1 - A number representing the modification time of this row in UNIX format (seconds since the epoch).
- The UID of the vassal instance. Required in Tyrant mode (secure multi-user hosting) mode only.
- The GID of the vassal instance. Required in Tyrant mode (secure multi-user hosting) mode only.
uwsgi --plugin emperor_pg --emperor "pg://host=127.0.0.1 user=foobar dbname=emperor;SELECT name,config,ts FROM vassals"
- Whenever a new tuple is added a new instance is created and spawned with the config specified in the second field.
- Whenever the modification time field changes, the instance is reloaded.
- If a tuple is removed, the corresponding vassal will be destroyed.
mongodb://
– Scan MongoDB collections for configuration¶
uwsgi --plugin emperor_mongodb --emperor "mongodb://127.0.0.1:27107,emperor.vassals,{enabled:1}"
This will scan all of the documents in the emperor.vassals
collection
having the field enabled
set to 1. An Emperor-compliant document must
define three fields: name
, config
and ts
. In Tyrant mode (secure multi-user hosting) mode, 2
more fields are required.
name
(string) is the name of the vassal (remember to give it a valid extension, like .ini)config
(multiline string) is the vassal config in the format described by thename
‘s extension.ts
(date) is the timestamp of the config (Note: MongoDB internally stores the timestamp in milliseconds.)uid
(number) is the UID to run the vassal as. Required in Tyrant mode (secure multi-user hosting) mode only.gid
(number) is the GID to run the vassal as. Required in Tyrant mode (secure multi-user hosting) mode only.
amqp://
– Use an AMQP compliant message queue to announce events¶
Set your AMQP (RabbitMQ, for instance) server address as the –emperor argument:
uwsgi --plugin emperor_amqp --emperor amqp://192.168.0.1:5672
Now the Emperor will wait for messages in the uwsgi.emperor
exchange. This
should be a fanout type exchange, but you can use other systems for your
specific needs. Messages are simple strings containing the absolute path of a
valid uWSGI config file.
# The pika module is used in this example, but you're free to use whatever adapter you like.
import pika
# connect to RabbitMQ server
connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.0.1'))
# get the channel
channel = connection.channel()
# create the exchange (if not already available)
channel.exchange_declare(exchange='uwsgi.emperor', type='fanout')
# publish a new config file
channel.basic_publish(exchange='uwsgi.emperor', routing_key='', body='/etc/vassals/mydjangoapp.xml')
The first time you launch the script, the emperor will add the new instance (if the config file is available). From now on every time you re-publish the message the app will be reloaded. When you remove the config file the app is removed too.
小技巧
You can subscribe all of your emperors in the various servers to this exchange to allow cluster-synchronized reloading/deploy.
AMQP with HTTP¶
uWSGI is capable of loading configuration files over HTTP. This is a very handy way to dynamically generate configuration files for massive hosting. Simply declare the HTTP URL of the config file in the AMQP message. Remember that it must end with one of the valid config extensions, but under the hood it can be generated by a script. If the HTTP URL returns a non-200 status code, the instance will be removed.
channel.basic_publish(exchange='uwsgi.emperor', routing_key='', body='http://example.com/confs/trac.ini')
Direct AMQP configuration¶
Configuration files may also be served directly over AMQP. The routing_key
will be the (virtual) config filename, and the message will be the content of
the config file.
channel.basic_publish(
exchange='uwsgi.emperor',
routing_key='mydomain_trac_config.ini',
body="""
[uwsgi]
socket=:3031
env = TRAC_ENV=/accounts/unbit/trac/uwsgi
module = trac.web.main:dispatch_request
processes = 4""")
The same reloading rules of previous modes are valid. When you want to remove an instance simply set an empty body as the “configuration”.
channel.basic_publish(exchange='uwsgi.emperor', routing_key='mydomain_trac_config.ini', body='')
zmq://
– ZeroMQ¶
The Emperor binds itself to a ZeroMQ PULL socket, ready to receive commands.
uwsgi --plugin emperor_zeromq --emperor zmq://tcp://127.0.0.1:5252
Each command is a multipart message sent over a PUSH zmq socket. A command is
composed by at least 2 parts: command
and name
command
is the
action to execute, while name
is the name of the vassal. 3 optional parts
can be specified.
config
(a string containing the vassal config)uid
(the user id to drop priviliges to in case of tyrant mode)gid
(the group id to drop priviliges to in case of tyrant mode)
There are 2 kind of commands (for now):
touch
destroy
The first one is used for creating and reloading instances while the second is for destroying. If you do not specify a config string, the Emperor will assume you are referring to a static file available in the Emperor current directory.
import zmq
c = zmq.Context()
s = zmq.Socket(c, zmq.PUSH)
s.connect('tcp://127.0.0.1:5252')
s.send_multipart(['touch','foo.ini',"[uwsgi]\nsocket=:4142"])
zoo://
– Zookeeper¶
Currently in development.
ldap://
– LDAP¶
Currently in development.
The Emperor protocol¶
As of 1.3 you can spawn custom applications via the Emperor.
Non-uWSGI Vassals should never daemonize, to maintain a link with the Emperor. If you want/need better integration with the Emperor, implement the Emperor protocol.
The protocol¶
An environment variable UWSGI_EMPEROR_FD
is passed to every vassal,
containing a file descriptor number.
import os
has_emperor = os.environ.get('UWSGI_EMPEROR_FD')
if has_emperor:
print "I'm a vassal snake!"
Or in Perl,
my $has_emperor = $ENV{'UWSGI_EMPEROR_FD'}
if ($has_emperor) {
print "I am a vassal.\n"
}
Or in C,
int emperor_fd = -1;
char *has_emperor = getenv("UWSGI_EMPEROR_FD");
if (has_emperor) {
emperor_fd = atoi(has_emperor);
fprintf(stderr, "I am a vassal.\n");
}
From now you can receive (and send) messages from (and to) the Emperor over this file descriptor.
Messages are byte sized (0-255), and each number (byte) has a meaning.
0 | Sent by the Emperor to stop a vassal |
1 | Sent by the Emperor to reload a vassal / sent by a vassal when it has been spawned |
2 | Sent by a vassal to ask the Emperor for configuration chunk |
5 | Sent by a vassal when it is ready to accept requests |
17 | Sent by a vassal after the first request to announce loyalty |
22 | Sent by a vassal to notify the Emperor of voluntary shutdown |
26 | Heartbeat sent by the vassal. After the first received heartbeat, the Emperor will expect more of them from the vassal. |
30 | Sent by the vassal to ask for Auto-scaling with Broodlord mode mode. |
Special configuration variables¶
Using Placeholders and Magic variables in conjunction with the Emperor will probably save you a lot of time and make your configuration more DRY. Suppose that in /opt/apps there are only Django apps. /opt/apps/app.skel (the .skel extension is not a known configuration file type to uWSGI and will be skipped)
[uwsgi]
chdir = /opt/apps/%n
master = true
threads = 20
socket = /tmp/sockets/%n.sock
env = DJANGO_SETTINGS_MODULE=%n.settings
module = django.core.handlers.wsgi:WSGIHandler()
And then for each app create a symlink:
ln -s /opt/apps/app.skel /opt/apps/app1.ini
ln -s /opt/apps/app.skel /opt/apps/app2.ini
Finally, start the Emperor with the --emperor-nofollow
option. Now you can reload each vassal separately with the command:
touch --no-dereference $INI_FILE
Passing configuration parameters to all vassals¶
Starting from 1.9.19 you can pass options using the --vassal-set
facility
[uwsgi]
emperor = /etc/uwsgi/vassals
vassal-set = processes=8
vassal-set = enable-metrics=1
this will add --set processes=8
and --set enable-metrics=1
to each vassal
You can force the Emperor to pass options to uWSGI instances using environment
variables too. Every environment variable of the form UWSGI_VASSAL_xxx
will be
rewritten in the new instance as UWSGI_xxx
, with the usual
configuration implications.
For example:
UWSGI_VASSAL_SOCKET=/tmp/%n.sock uwsgi --emperor /opt/apps
will let you avoid specifying the socket option in configuration files.
Alternatively, you can use the --vassals-include
option let each
vassal automatically include a complete config file:
uwsgi --emperor /opt/apps --vassals-include /etc/uwsgi/vassals-default.ini
Note that if you do this, %n
(and other magic variables) in the
included file will resolve to the name of the included file, not the
original vassal configuration file. If you want to set options in the
included file using the vassal name, you’ll have to use placeholders.
For example, in the vassal config, you write:
[uwsgi]
vassal_name = %n
... more options
In the vassal-defaults.ini
, you write:
[uwsgi]
socket = /tmp/sockets/%(vassal_name).sock
Tyrant mode (secure multi-user hosting)¶
The emperor is normally be run as root, setting the UID and GID in each
instance’s config. The vassal instance then drops privileges before serving
requests. In this mode, if your users have access to their own uWSGI
configuration files, you can’t trust them to set the correct uid
and
gid
. You could run the emperor as unprivileged user (with uid
and
gid
) but all of the vassals would then run under the same user, as
unprivileged users are not able to promote themselves to other users. For this
case the Tyrant mode is available – just add the emperor-tyrant
option.
In Tyrant mode the Emperor will run the vassal with the UID/GID of its configuration file (or for other Imperial Monitors, by some other method of configuration). If Tyrant mode is used, the vassal configuration files must have UID/GID > 0. An error will occur if the UID or GID is zero, or if the UID or GID of the configuration of an already running vassal changes.
Tyrant mode for paranoid sysadmins (Linux only)¶
If you have built a uWSGI version with Setting POSIX Capabilities options enabled, you can run the Emperor as unprivileged user but maintaining the minimal amount of root-capabilities needed to apply the tyrant mode
[uwsgi]
uid = 10000
gid = 10000
emperor = /tmp
emperor-tyrant = true
cap = setgid,setuid
On demand vassals (socket activation)¶
Inspired by the venerable xinetd/inetd approach, you can spawn your vassals only after the first connection to a specific socket. This feature is available as of 1.9.1. Check the changelog for more information: uWSGI 1.9.1
Loyalty¶
As soon as a vassal manages a request it will became “loyal”. This status is used by the Emperor to identify bad-behaving vassals and punish them.
Throttling¶
Whenever two or more vassals are spawned in the same second, the Emperor will start a throttling subsystem to avoid fork bombing. The system adds a throttle delta (specified in milliseconds via the OptionEmperorThrottle option) whenever it happens, and waits for that duration before spawning a new vassal. Every time a new vassal spawns without triggering throttling, the current throttling duration is halved.
Blacklist system¶
Whenever a non-loyal vassal dies, it is put in a shameful blacklist. When in a blacklist, that vassal will be throttled up to a maximum value (tunable via OptionEmperorMaxThrottle), starting from the default throttle delta of 3. Whenever a blacklisted vassal dies, its throttling value is increased by the delta (OptionEmperorThrottle).
Heartbeat system¶
Vassals can voluntarily ask the Emperor to monitor their status. Workers of heartbeat-enabled vassals will send “heartbeat” messages to the Emperor. If the Emperor does not receive heartbeats from an instance for more than N (default 30, OptionEmperorRequiredHeartbeat) seconds, that instance will be considered hung and thus reloaded. To enable sending of heartbeat packet in a vassal, add the OptionHeartbeat option.
重要
If all of your workers are stuck handling perfectly legal requests such as slow, large file uploads, the Emperor will trigger a reload as if the workers are hung. The reload triggered is a graceful one, so you can be able to tune your config/timeout/mercy for sane behaviour.
Using Linux namespaces for vassals¶
On Linux you can tell the Emperor to run vassals in “unshared” contexts. That means you can run each vassal with a dedicated view of the filesystems, ipc, uts, networking, pids and uids.
Things you generally do with tools like lxc
or its abstractions like docker
are native in uWSGI.
For example if you want to run each vassals in a new namespace:
[uwsgi]
emperor = /etc/uwsgi/vassals
emperor-use-clone = fs,net,ipc,pid,uts
now each vassal will be able to modify the filesystem layout, networking, hostname and so on without damaging the main system.
A couple of helper daemons are included in the uWSGI distribution to simplify management of jailed vassals. Most notably The TunTap Router allows full user-space networking in jails, while
the forkpty router
allows allocation of pseudoterminals in jails
It is not needed to unshare all of the subsystem in your vassals, sometimes you only want to give dedicated ipc and hostname to a vassal and hide from the processes list:
[uwsgi]
emperor = /etc/uwsgi/vassals
emperor-use-clone = fs,ipc,pid,uts
a vassal could be:
[uwsgi]
; set the hostname
exec-as-root = hostname foobar
; umount /proc and remount to hide processes
; as we are in the 'fs' namespace umounting /proc does not interfere with the main one
exec-as-root = umount /proc
exec-as-root = mount -t proc none /proc
; drop privileges
uid = foobar
gid = foobar
; bind to the socket
socket = /tmp/myapp.socket
psgi = myapp.pl
The Imperial Bureau of Statistics¶
You can enable a statistics/status service for the Emperor by adding the OptionEmperorStats option with a TCP address. By connecting to that address, you’ll get a JSON-format blob of statistics.
Running non-uWSGI apps or using alternative uWSGIs as vassals¶
You can exec()
a different binary as your vassal using the
privileged-binary-patch
/unprivileged-binary-patch
options. The first
one patches the binary after socket inheritance and shared socket
initialization (so you will be able to use uWSGI-defined sockets). The second
one patches the binary after privileges drop. In this way you will be able to
use uWSGI’s UID/GID/chroot/namespace/jailing options. The binary is called
with the same arguments that were passed to the vassal by the Emperor.
; i am a special vassal calling a different binary in a new linux network namespace
[uwsgi]
uid = 1000
gid = 1000
unshare = net
unprivileged-binary-patch = /usr/bin/myfunnyserver
重要
DO NOT DAEMONIZE your apps. If you do so, the Emperor will lose its connection with them.
The uWSGI arguments are passed to the new binary. If you do not like that
behaviour (or need to pass custom arguments) add -arg
to the binary patch
option, yielding:
; i am a special vassal calling a different binary in a new linux network namespace
; with custom options
[uwsgi]
uid = 1000
gid = 1000
unshare = net
unprivileged-binary-patch-arg = ps aux
or:
;nginx example
[uwsgi]
privileged-binary-patch-arg = nginx -g "daemon off;"
参见
Your custom vassal apps can also speak with the emperor using the emperor protocol.
Integrating the Emperor with the FastRouter¶
The FastRouter is a proxy/load-balancer/router speaking The uwsgi Protocol. Yann Malet from Lincoln Loop has released a draft about massive Emperor + Fastrouter deployment (PDF) using The uWSGI caching framework as a hostname to socket mapping storage.
Notes¶
At startup, the emperor
chdir()
to the vassal dir. All vassal instances will start from here.If the uwsgi binary is not in your system path you can force its path with
binary-path
:./uwsgi --emperor /opt/apps --binary-path /opt/uwsgi/uwsgi
Sending
SIGUSR1
to the emperor will print vassal status in its log.Stopping (
SIGINT
/SIGTERM
/SIGQUIT
) the Emperor will invoke Ragnarok and kill all the vassals.Sending
SIGHUP
to the Emperor will reload all vassals.The emperor should generally not be run with
--master
, unless master features like advanced logging are specifically needed.The emperor should generally be started at server boot time and left alone, not reloaded/restarted except for uWSGI upgrades; emperor reloads are a bit drastic, reloading all vassals at once. Instead vassals should be reloaded individually when needed, in the manner of the imperial monitor in use.
Todo¶
- Docs-TODO: Clarify what the “chdir-on-startup” behavior does with non-filesystem monitors.
- Export more magic vars
- Add support for multiple sections in xml/ini/yaml files (this will allow to have a single config file for multiple instances)
Auto-scaling with Broodlord mode¶
Broodlord (taken from Starcraft, like Zerg mode mode) is a way for a vassal to ask for “reinforcements” to the Emperor. “Reinforcements” are new vassals spawned on demand generally bound on the same socket. Broodlord mode alone is not very useful. However, when combined with Zerg mode, Idle and The uWSGI Emperor – multi-app deployment it can be used to implement auto-scaling for your apps.
WARNING: If you are looking for a way to dynamically adapt the number of workers of an instance, check the The uWSGI cheaper subsystem – adaptive process spawning mode, Broodlord mode is for spawning totally new instances.
A ‘simple’ example¶
We’ll start apps with a single worker, adding resources on demand. Broodlord mode expects an additional stanza in your config file to be used for zergs.
[uwsgi]
socket = :3031
master = true
vassal-sos-backlog = 10
module = werkzeug.testapp:test_app
processes = 1
zerg-server = /tmp/broodlord.sock
disable-logging = true
[zerg]
zerg = /tmp/broodlord.sock
master = true
module = werkzeug.testapp:test_app
processes = 1
disable-logging = true
idle = 30
die-on-idle = true
The vassal-sos-backlog
option (supported only on Linux and TCP sockets)
will ask the Emperor for zergs when the listen queue is higher than the given
value. By default the value is 10. More “vassal-sos-” options will be added in
the future to allow for more specific detect-overload systems.
The [zerg]
stanza is the config the Emperor will run when a vassal requires
resources. The die-on-idle
option will completely destroy the zerg when
inactive for more than 30 seconds. This configuration shows how to combine the
various uWSGI features to implement different means of scaling. To run the
Emperor we need to specify how many zerg instances can be run:
uwsgi --emperor /etc/vassals --emperor-broodlord 40
This will allow you to run up to 40 additional zerg workers for your apps.
–vassal-sos¶
注解
This flag has been added in 2.0.7.
–vassal-sos allows the vassal to ask for reinforcement as soon as all of its workers are busy.
The option takes an integer value, the number of seconds to wait between asking for a new reinforcements.
Manually asking for reinforcement¶
You can use the master FIFO’s “B” command to force an instance to ask for reinforcements from the Emperor.
echo B > /var/run/master.fifo
Under the hood (or: hacking broodlord mode)¶
Technically broodlord mode is a simple message sent by a vassal to “force” the Emperor to spawn another vassal with a ‘:zerg’ suffix in the instance name.
Even if the suffix is ‘:zerg’ this does not mean you need to use Zerg mode. A ‘zerg’ instance could be a completely independent one that simply subscribes to a router, or binds to a SO_REUSEPORT socket.
This is an example with subscription system.
[uwsgi]
socket = 127.0.0.1:0
subscribe2 = server=127.0.0.1:4040,key=foobar.it
psgi = app.pl
processes = 4
vassal-sos = 3
[zerg]
socket = 127.0.0.1:0
subscribe2 = server=127.0.0.1:4040,key=foobar.it
psgi = app.pl
idle = 60
processes = 1
Zerg mode¶
注解
Yes, that’s Zerg as in the “quantity-over-quality” Starcraft race. If you haven’t played Starcraft, be prepared for some nonsense.
注解
Also note that this nonsense is mostly limited to the nomenclature. Zerg Mode is serious business.
When your site load is variable, it would be nice to be able to add workers dynamically.
You can obviously edit your configuration to hike up workers
and reload your uWSGI instance, but for very loaded apps this is undesirable, and frankly – who wants to do manual work like that to scale an app?
Enabling Zerg mode you can allow “uwsgi-zerg” instances to attach to your already running server and help it in the work.
Zerg mode is obviously local only. You cannot use it to add remote instances – this is a job better done by the The uWSGI FastRouter, the HTTP plugin or your web server’s load balancer.
Enabling the zerg server¶
If you want an uWSGI instance to be rushed by zerg, you have to enable the Zerg server. It will be bound to an UNIX socket and will pass uwsgi socket file descriptors to the Zerg workers connecting to it.
警告
The socket must be an UNIX socket because it must be capable of passing through file descriptors. A TCP socket simply will not work.
For security reasons the UNIX socket does not inherit the chmod-socket
option, but will always use the current umask.
If you have filesystem permission issues, on Linux you can use the UNIX sockets in abstract namespace, by prepending an @
to the socket name.
A normal UNIX socket:
./uwsgi -M -p 8 --module welcome --zerg-server /var/run/mutalisk
A socket in a Linux abstract namespace:
./uwsgi -M -p 8 --module welcome --zerg-server @nydus
Attaching zergs to the zerg server¶
To add a new instance to your zerg pool, simply use the –zerg option
./uwsgi --zerg /var/run/mutalisk --master --processes 4 --module welcome
# (or --zerg @nydus, following the example above)
In this way 4 new workers will start serving requests.
When your load returns to normal values, you can simply shutdown all of the uwsgi-zerg instances without problems.
You can attach an unlimited number of uwsgi-zerg instances.
Fallback if a zerg server is not available¶
By default a Zerg client will not run if the Zerg server is not available. Thus, if your zerg server dies, and you reload the zerg client, it will simply shutdown.
If you want to avoid that behaviour, add a --socket
directive mapping to the required socket (the one that should be managed by the zerg server) and add the --zerg-fallback
option.
With this setup, if a Zerg server is not available, the Zerg client will continue binding normally to the specified socket(s).
Using Zerg as testers¶
A good trick you can use, is suspending the main instance with the SIGTSTP
signal and loading a new version of your app in a Zerg. If the code is not ok you can simply shutdown the Zerg and resume the main instance.
Zerg Pools¶
Zergpools are special Zerg servers that only serve Zerg clients, nothing more.
You can use them to build high-availability systems that reduce downtime during tests/reloads.
You can run an unlimited number of zerg pools (on several UNIX sockets) and map an unlimited number of sockets to them.
[uwsgi]
master = true
zergpool = /tmp/zergpool_1:127.0.0.1:3031,127.0.0.1:3032
zergpool = /tmp/zergpool_2:192.168.173.22:3031,192.168.173.22:3032
With a config like this, you will have two zergpools, each serving two sockets.
You can now attach instances to them.
uwsgi --zerg /tmp/zergpool_1 --wsgi-file myapp.wsgi --master --processes 8
uwsgi --zerg /tmp/zergpool_2 --rails /var/www/myapp --master --processes 4
or you can attach a single instance to multiple Zerg servers.
uwsgi --zerg /tmp/zergpool_1 --zerg /tmp/zergpool_2 --wsgi-file myapp.wsgi --master --processes 8
Adding applications dynamically¶
NOTE: this is not the best approach for hosting multiple applications. You’d better to run a uWSGI instance for each app.
You can start the uWSGI server without configuring an application.
To load a new application you can use these variables in the uwsgi packet:
UWSGI_SCRIPT
– pass the name of a WSGI script defining anapplication
callable- or
UWSGI_MODULE
andUWSGI_CALLABLE
– the module name (importable path) and the name of the callable to invoke from that module
Dynamic apps are officially supported on Cherokee, Nginx, Apache, cgi_dynamic. They are easily addable to the Tomcat and Twisted handlers.
Defining VirtualEnv with dynamic apps¶
Virtualenvs are based on the Py_SetPythonHome()
function. This function has
effect only if called before Py_Initialize()
so it can’t be used with
dynamic apps.
To define a VirtualEnv with DynamicApps, a hack is the only solution.
First you have to tell python to not import the site
module. This module
adds all site-packages
to sys.path
. To emulate virtualenvs, we must
load the site module only after subinterpreter initialization. Skipping the
first import site
, we can now simply set sys.prefix
and
sys.exec_prefix
on dynamic app loading and call
PyImport_ImportModule("site");
// Some users would want to not disable initial site module loading, so the site module must be reloaded:
PyImport_ReloadModule(site_module);
Now we can set the VirtualEnv dynamically using the UWSGI_PYHOME
var:
location / {
uwsgi_pass 192.168.173.5:3031;
include uwsgi_params;
uwsgi_param UWSGI_SCRIPT mytrac;
uwsgi_param UWSGI_PYHOME /Users/roberto/uwsgi/VENV2;
}
Scaling SSL connections (uWSGI 1.9)¶
Distributing SSL servers in a cluster is a hard topic. The biggest problem is sharing SSL sessions between different nodes.
The problem is amplified in non-blocking servers due to OpenSSL’s limits in the way sessions are managed.
For example, you cannot share sessions in Memcached servers and access them in a non-blocking way.
A common solution (well, a compromise, maybe) until now has been to use a single SSL terminator balancing requests to multiple non-encrypted backends. This solution kinda works, but obviously it does not scale.
Starting from uWSGI 1.9-dev an implementation (based on the stud project) of distributed caching has been added.
Setup 1: using the uWSGI cache for storing SSL sessions¶
You can configure the SSL subsystem of uWSGI to use the shared cache. The SSL sessions will time out according to the expiry value of the cache item. This way the cache sweeper thread (managed by the master) will destroy sessions in the cache.
重要
The order of the options is important. cache
options must be specified BEFORE ssl-sessions-use-cache
and https
options.
[uwsgi]
; spawn the master process (it will run the cache sweeper thread)
master = true
; store up to 20k sessions
cache = 20000
; 4k per object is enough for SSL sessions
cache-blocksize = 4096
; force the SSL subsystem to use the uWSGI cache as session storage
ssl-sessions-use-cache = true
; set SSL session timeout (in seconds)
ssl-sessions-timeout = 300
; set the session context string (see later)
https-session-context = foobar
; spawn an HTTPS router
https = 192.168.173.1:8443,foobar.crt,foobar.key
; spawn 8 processes for the HTTPS router (all sharing the same session cache)
http-processes = 8
; add a bunch of uwsgi nodes to relay traffic to
http-to = 192.168.173.10:3031
http-to = 192.168.173.11:3031
http-to = 192.168.173.12:3031
; add stats
stats = 127.0.0.1:5001
Now start blasting your HTTPS router and then telnet to port 5001. Under the “cache” object of the JSON output you should see the values “items” and “hits” increasing. The value “miss” is increased every time a session is not found in the cache. It is a good metric of the SSL performance users can expect.
Setup 2: synchronize caches of different HTTPS routers¶
The objective is to synchronize each new session in each distributed cache. To accomplish that you have to spawn a special thread
(cache-udp-server
) in each instance and list all of the remote servers that should be synchronized.
A pure-TCP load balancer (like HAProxy or uWSGI’s Rawrouter) can be used to load balance between the various HTTPS routers.
Here’s a possible Rawrouter config.
[uwsgi]
master = true
rawrouter = 192.168.173.99:443
rawrouter-to = 192.168.173.1:8443
rawrouter-to = 192.168.173.2:8443
rawrouter-to = 192.168.173.3:8443
Now you can configure the first node (the new options are at the end of the .ini config)
[uwsgi]
; spawn the master process (it will run the cache sweeper thread)
master = true
; store up to 20k sessions
cache = 20000
; 4k per object is enough for SSL sessions
cache-blocksize = 4096
; force the SSL subsystem to use the uWSGI cache as session storage
ssl-sessions-use-cache = true
; set SSL session timeout (in seconds)
ssl-sessions-timeout = 300
; set the session context string (see later)
https-session-context = foobar
; spawn an HTTPS router
https = 192.168.173.1:8443,foobar.crt,foobar.key
; spawn 8 processes for the HTTPS router (all sharing the same session cache)
http-processes = 8
; add a bunch of uwsgi nodes to relay traffic to
http-to = 192.168.173.10:3031
http-to = 192.168.173.11:3031
http-to = 192.168.173.12:3031
; add stats
stats = 127.0.0.1:5001
; spawn the cache-udp-server
cache-udp-server = 192.168.173.1:7171
; propagate updates to the other nodes
cache-udp-node = 192.168.173.2:7171
cache-udp-node = 192.168.173.3:7171
and the other two...
[uwsgi]
; spawn the master process (it will run the cache sweeper thread)
master = true
; store up to 20k sessions
cache = 20000
; 4k per object is enough for SSL sessions
cache-blocksize = 4096
; force the SSL subsystem to use the uWSGI cache as session storage
ssl-sessions-use-cache = true
; set SSL session timeout (in seconds)
ssl-sessions-timeout = 300
; set the session context string (see later)
https-session-context = foobar
; spawn an HTTPS router
https = 192.168.173.1:8443,foobar.crt,foobar.key
; spawn 8 processes for the HTTPS router (all sharing the same session cache)
http-processes = 8
; add a bunch of uwsgi nodes to relay traffic to
http-to = 192.168.173.10:3031
http-to = 192.168.173.11:3031
http-to = 192.168.173.12:3031
; add stats
stats = 127.0.0.1:5001
; spawn the cache-udp-server
cache-udp-server = 192.168.173.2:7171
; propagate updates to the other nodes
cache-udp-node = 192.168.173.1:7171
cache-udp-node = 192.168.173.3:7171
[uwsgi]
; spawn the master process (it will run the cache sweeper thread)
master = true
; store up to 20k sessions
cache = 20000
; 4k per object is enough for SSL sessions
cache-blocksize = 4096
; force the SSL subsystem to use the uWSGI cache as session storage
ssl-sessions-use-cache = true
; set SSL session timeout (in seconds)
ssl-sessions-timeout = 300
; set the session context string (see later)
https-session-context = foobar
; spawn an HTTPS router
https = 192.168.173.1:8443,foobar.crt,foobar.key
; spawn 8 processes for the HTTPS router (all sharing the same session cache)
http-processes = 8
; add a bunch of uwsgi nodes to relay traffic to
http-to = 192.168.173.10:3031
http-to = 192.168.173.11:3031
http-to = 192.168.173.12:3031
; add stats
stats = 127.0.0.1:5001
; spawn the cache-udp-server
cache-udp-server = 192.168.173.3:7171
; propagate updates to the other nodes
cache-udp-node = 192.168.173.1:7171
cache-udp-node = 192.168.173.2:7171
Start hammering the Rawrouter (remember to use a client supporting persistent SSL sessions, like your browser) and get cache statistics from the stats server of each HTTPS terminator node. If the count of “hits” is a lot higher than the “miss” value the system is working well and your load is distributed and in awesome hyper high performance mode.
So, what is https-session-context
, you ask? Basically each SSL session before being used is checked against a fixed string (the session context). If the session does not match that string, it is rejected. By default the session context is initialized to a value built from the HTTP server address. Forcing it to a shared value will avoid a session created in a node being rejected in another one.
Using named caches¶
Starting from uWSGI 1.9 you can have multiple caches. This is a setup with 2 nodes using a new generation cache named “ssl”.
The cache2
option allows also to set a custom key size. Since SSL session keys are not very long, we can use it to optimize memory usage. In this example we use 128 byte key size limit, which should be enough for session IDs.
[uwsgi]
; spawn the master process (it will run the cache sweeper thread)
master = true
; store up to 20k sessions
cache2 = name=ssl,items=20000,keysize=128,blocksize=4096,node=127.0.0.1:4242,udp=127.0.0.1:4141
; force the SSL subsystem to use the uWSGI cache as session storage
ssl-sessions-use-cache = ssl
; set sessions timeout (in seconds)
ssl-sessions-timeout = 300
; set the session context string
https-session-context = foobar
; spawn an HTTPS router
https = :8443,foobar.crt,foobar.key
; spawn 8 processes for the HTTPS router (all sharing the same session cache)
http-processes = 8
module = werkzeug.testapp:test_app
; add stats
stats = :5001
and the second node...
[uwsgi]
; spawn the master process (it will run the cache sweeper thread)
master = true
; store up to 20k sessions
cache2 = name=ssl,items=20000,blocksize=4096,node=127.0.0.1:4141,udp=127.0.0.1:4242
; force the SSL subsystem to use the uWSGI cache as session storage
ssl-sessions-use-cache = ssl
; set session timeout
ssl-sessions-timeout = 300
; set the session context string
https-session-context = foobar
; spawn an HTTPS router
https = :8444,foobar.crt,foobar.key
; spawn 8 processes for the HTTPS router (all sharing the same sessions cache)
http-processes = 8
module = werkzeug.testapp:test_app
; add stats
stats = :5002
Notes¶
If you do not want to manually configure the cache UDP nodes and your network configuration supports it, you can use UDP multicast.
[uwsgi]
...
cache-udp-server = 225.1.1.1:7171
cache-udp-node = 225.1.1.1:7171
- A new gateway server is in development, named “udprepeater”. It will basically forward all of UDP packets it receives to the subscribed back-end nodes. It will allow you to maintain the zero-config style of the subscription system (basically you only need to configure a single cache UDP node pointing to the repeater).
- Currently there is no security between the cache nodes. For some users this may be a huge problem, so a security mode (encrypting the packets) is in development.
让 uWSGI 更安全¶
Setting POSIX Capabilities¶
POSIX capabilities allow fine-grained permissions for processes. In addition
to the standard UNIX permission scheme, they define a new set of privileges for
system resources. To enable capabilities support (Linux Only) you have to
install the libcap
headers (libcap-dev
on Debian-based distros) before
building uWSGI. As usual your processes will lose practically all of the
capabilities after a setuid
call. The uWSGI cap
option allows you to
define a list of capabilities to maintain through the call.
For example, to allow your unprivileged app to bind on privileged ports and set the system clock, you will use the following options.
uwsgi --socket :1000 --uid 5000 --gid 5000 --cap net_bind_service,sys_time
All of the processes generated by uWSGI will then inherit this behaviour. If your system supports capabilities not available in the uWSGI list you can simply specify the number of the constant:
uwsgi --socket :1000 --uid 5000 --gid 5000 --cap net_bind_service,sys_time,42
In addition to net_bind_service
and sys_time
, a new capability numbered ‘42’ is added.
Available capabilities¶
This is the list of available capabilities.
audit_control | CAP_AUDIT_CONTROL |
audit_write | CAP_AUDIT_WRITE |
chown | CAP_CHOWN |
dac_override | CAP_DAC_OVERRIDE |
dac_read_search | CAP_DAC_READ_SEARCH |
fowner | CAP_FOWNER |
fsetid | CAP_FSETID |
ipc_lock | CAP_IPC_LOCK |
ipc_owner | CAP_IPC_OWNER |
kill | CAP_KILL |
lease | CAP_LEASE |
linux_immutable | CAP_LINUX_IMMUTABLE |
mac_admin | CAP_MAC_ADMIN |
mac_override | CAP_MAC_OVERRIDE |
mknod | CAP_MKNOD |
net_admin | CAP_NET_ADMIN |
net_bind_service | CAP_NET_BIND_SERVICE |
net_broadcast | CAP_NET_BROADCAST |
net_raw | CAP_NET_RAW |
setfcap | CAP_SETFCAP |
setgid | CAP_SETGID |
setpcap | CAP_SETPCAP |
setuid | CAP_SETUID |
sys_admin | CAP_SYS_ADMIN |
sys_boot | CAP_SYS_BOOT |
sys_chroot | CAP_SYS_CHROOT |
sys_module | CAP_SYS_MODULE |
sys_nice | CAP_SYS_NICE |
sys_pacct | CAP_SYS_PACCT |
sys_ptrace | CAP_SYS_PTRACE |
sys_rawio | CAP_SYS_RAWIO |
sys_resource | CAP_SYS_RESOURCE |
sys_time | CAP_SYS_TIME |
sys_tty_config | CAP_SYS_TTY_CONFIG |
syslog | CAP_SYSLOG |
wake_alarm | CAP_WAKE_ALARM |
Running uWSGI in a Linux CGroup¶
Linux cgroups are an amazing feature available in recent Linux kernels. They allow you to “jail” your processes in constrained environments with limited CPU, memory, scheduling priority, IO, etc..
注解
uWSGI has to be run as root to use cgroups. uid
and gid
are very, very necessary.
Enabling cgroups¶
First you need to enable cgroup support in your system. Create the /cgroup directory and add this to your /etc/fstab:
none /cgroup cgroup cpu,cpuacct,memory
Then mount /cgroup and you’ll have jails with controlled CPU and memory usage. There are other Cgroup subsystems, but CPU and memory usage are the most useful to constrain.
Let’s run uWSGI in a cgroup:
./uwsgi -M -p 8 --cgroup /cgroup/jail001 -w simple_app -m --http :9090
Cgroups are simple directories. With this command your uWSGI server and its workers are “jailed” in the ‘cgroup/jail001’ cgroup. If you make a bunch of requests to the server, you will see usage counters – cpuacct.* and memoryfiles.* in the cgroup directory growing. You can also use pre-existing cgroups by specifying a directory that already exists.
A real world example: Scheduling QoS for your customers¶
Suppose you’re hosting apps for 4 customers. Two of them are paying you $100 a month, one is paying $200, and the last is paying $400. To have a good Quality of Service implementation, the $100 apps should get 1/8, or 12.5% of your CPU power, the $200 app should get 1/4 (25%) and the last should get 50%. To implement this, we have to create 4 cgroups, one for each app, and limit their scheduling weights.
./uwsgi --uid 1001 --gid 1001 -s /tmp/app1 -w app1 --cgroup /cgroup/app1 --cgroup-opt cpu.shares=125
./uwsgi --uid 1002 --gid 1002 -s /tmp/app2 -w app1 --cgroup /cgroup/app2 --cgroup-opt cpu.shares=125
./uwsgi --uid 1003 --gid 1003 -s /tmp/app3 -w app1 --cgroup /cgroup/app3 --cgroup-opt cpu.shares=250
./uwsgi --uid 1004 --gid 1004 -s /tmp/app4 -w app1 --cgroup /cgroup/app4 --cgroup-opt cpu.shares=500
The cpu.shares
values are simply computed relative to each other, so you
can use whatever scheme you like, such as (125, 125, 250, 500) or even (1, 1,
2, 4). With CPU handled, we turn to limiting memory. Let’s use the same
scheme as before, with a maximum of 2 GB for all apps altogether.
./uwsgi --uid 1001 --gid 1001 -s /tmp/app1 -w app1 --cgroup /cgroup/app1 --cgroup-opt cpu.shares=125 --cgroup-opt memory.limit_in_bytes=268435456
./uwsgi --uid 1002 --gid 1002 -s /tmp/app2 -w app1 --cgroup /cgroup/app2 --cgroup-opt cpu.shares=125 --cgroup-opt memory.limit_in_bytes=268435456
./uwsgi --uid 1003 --gid 1003 -s /tmp/app3 -w app1 --cgroup /cgroup/app3 --cgroup-opt cpu.shares=250 --cgroup-opt memory.limit_in_bytes=536870912
./uwsgi --uid 1004 --gid 1004 -s /tmp/app4 -w app1 --cgroup /cgroup/app4 --cgroup-opt cpu.shares=500 --cgroup-opt memory.limit_in_bytes=1067459584
Using Linux KSM in uWSGI¶
Kernel Samepage Merging is a feature of Linux kernels >= 2.6.32 which allows processes to share pages of memory with the same content. This is accomplished by a kernel daemon that periodically performs scans, comparisons, and, if possible, merges of specific memory areas. Born as an enhancement for KVM it can be used for processes that use common data (such as uWSGI processes with language interpreters and standard libraries).
If you are lucky, using KSM may exponentially reduce the memory usage of your uWSGI instances. Especially in massive Emperor deployments: enabling KSM for each vassal may result in massive memory savings. KSM in uWSGI was the idea of Giacomo Bagnoli of Asidev s.r.l.. Many thanks to him.
Enabling the KSM daemon¶
To enable the KSM daemon (ksmd
), simply set /sys/kernel/mm/ksm/run
to 1,
like so:
echo 1 > /sys/kernel/mm/ksm/run
注解
Remember to do this on machine startup, as the KSM daemon does not run by default.
注解
KSM is an opt-in feature that has to be explicitly requested by processes, so just enabling KSM will not be a savior for everything on your machine.
Enabling KSM support in uWSGI¶
If you have compiled uWSGI on a kernel with KSM support, you will be able to
use the ksm
option. This option will instruct uWSGI to register process
memory mappings (via madvise
syscall) after each request or master cycle.
If no page mapping has changed from the last scan, no expensive syscalls are
used.
Performance impact¶
Checking for process mappings requires parsing the /proc/self/maps
file
after each request. In some setups this may hurt performance. You can tune the
frequency of the uWSGI page scanner by passing an argument to the ksm
option.
# Scan for process mappings every 10 requests (or 10 master cycles)
./uwsgi -s :3031 -M -p 8 -w myapp --ksm=10
Check if KSM is working well¶
The /sys/kernel/mm/ksm/pages_shared
and /sys/kernel/mm/ksm/pages_sharing
files contain statistics regarding KSM’s efficiency. The higher values, the
less memory consumption for your uWSGI instances.
KSM statistics with collectd¶
A simple Bash script like this is useful for keeping an eye on KSM’s efficiency:
#!/bin/bash
export LC_ALL=C
if [ -e /sys/kernel/mm/ksm/pages_sharing ]; then
pages_sharing=`cat /sys/kernel/mm/ksm/pages_sharing`;
page_size=`getconf PAGESIZE`;
saved=$(echo "scale=0;$pages_sharing * $page_size"|bc);
echo "PUTVAL <%= cn %>/ksm/gauge-saved interval=60 N:$saved"
fi
In your collectd configuration, add something like this:
LoadPlugin exec
<Plugin exec>
Exec "nobody" "/usr/local/bin/ksm_stats.sh"
</Plugin>
Jailing your apps using Linux Namespaces¶
If you have a recent Linux kernel (>2.6.26) you can use its support for namespaces.
What are namespaces?¶
They are an elegant (more elegant than most of the jailing systems you might find in other operating systems) way to “detach” your processes from a specific layer of the kernel and assign them to a new one.
The ‘chroot’ system available on UNIX/Posix systems is a primal form of namespaces: a process sees a completely new file system root and has no access to the original one.
Linux extends this concept to the other OS layers (PIDs, users, IPC, networking etc.), so a specific process can live in a “virtual OS” with a new group of pids, a new set of users, a completely unshared IPC system (semaphores, shared memory etc.), a dedicated network interface and its own hostname.
uWSGI got full namespaces support in 1.9/2.0 development cycle.
Supported namespaces¶
fs
-> CLONE_NEWNS, filesystemsipc
-> CLONE_NEWIPC, sysv ipcpid
-> CLONE_NEWPID, when used with unshare() requires an additionalfork()
. Use one of the –refork-* options.uts
-> CLONE_NEWUTS, hostnamenet
-> CLONE_NEWNET, new networking, UNIX sockets from different namespaces are still usable, they are a good way for inter-namespaces communicationsuser
-> CLONE_NEWUSER, still complex to manage (and has differences in behaviours between kernel versions) use with caution
setns()¶
In addition to creating new namespaces for a process you can attach to already running ones using the setns()
call.
Each process exposes its namespaces via the /proc/self/ns
directory. The setns() syscall uses the file descriptors obtained from the files in that directory
to attach to namespaces.
As we have already seen, UNIX sockets are a good way to communicate between namespaces, the uWSGI setns()
feature works by creating an UNIX socket that receives requests
from processes wanting to join its namespace. As UNIX sockets allow file descriptors passing, the “client” only need to call setns() on them.
setns-socket <addr>
exposes /proc/self/ns on the specified unix socket addresssetns <addr>
connect to the specified unix socket address, get the filedescriptors and use setns() on themsetns-preopen
if enabled the /proc/self/ns files are opened on startup (before privileges drop) and cached. This is useful for avoiding running the main instance as root.setns-socket-skip <name>
some file in /proc/self/ns can create problems (mostly the ‘user’ one). You can skip them specifying the name. (you can specify this option multiple times)
pivot_root¶
This option allows you to change the rootfs of your currently running instance.
It is better than chroot as it allows you to access the old file system tree before (manually) unmounting it.
It is a bit complex to master correctly as it requires a couple of assumptions:
pivot_root <new> <old>
<new> is the directory to mount as the new rootfs and <old> is where to access the old tree.
<new> must be a mounted file system, and <old> must be under this file system.
A common pattern is:
[uwsgi]
unshare = fs
hook-post-jail = mount:none /distros/precise /ns bind
pivot_root = /ns /ns/.old_root
...
(Remember to create /ns
and /distro/precise/.old_root
.)
When you have created the new file system layout you can umount /.old_root recursively:
[uwsgi]
unshare = fs
hook-post-jail = mount:none /distros/precise /ns bind
pivot_root = /ns /ns/.old_root
; bind mount some useful fs like /dev and /proc
hook-as-root = mount:proc none /proc nodev hidepid=2
hook-as-root = mount:none /.old_root/dev /dev bind
hook-as-root = mount:none /.old_root/dev/pts /dev/pts bind
; umount the old tree
hook-as-root = umount:/.old_root rec,detach
Why not lxc?¶
LXC (LinuX Containers) is a project allowing you to build full subsystems using Linux namespaces. You may ask why “reinvent the wheel” while LXC implements a fully “virtualized” system. Apples and oranges...
LXC’s objective is giving users the view of a virtual server. uWSGI namespaces support is lower level – you can use it to detach single components (for example you may only want to unshare IPC) to increase security and isolation.
Not all the scenario requires a full system-like view (and in lot of case is suboptimal, while in other is the best approach), try to see namespaces as a way to increase security and isolation, when you need/can isolate a component do it with clone/unshare. When you want to give users a full system-like access go with LXC.
The old way: the –namespace option¶
Before 1.9/2.0 a full featured system-like namespace support was added. It works as a chroot() on steroids.
It should be moved as an external plugin pretty soon, but will be always part of the main distribution, as it is used by lot of people for its simplicity.
You basically need to set a root filesystem and an hostname to start your instance in a new namespace:
Let’s start by creating a new root filesystem for our jail. You’ll need debootstrap
(or an equivalent package for your distribution).
We’re placing our rootfs in /ns/001
, and then create a ‘uwsgi’ user that will run the uWSGI server.
We will use the chroot command to ‘adduser’ in the new rootfs, and we will install the Flask package, required by uwsgicc.
(All this needs to be executed as root)
mkdir -p /ns/001
debootstrap maverick /ns/001
chroot /ns/001
# in the chroot jail now
adduser uwsgi
apt-get install mercurial python-flask
su - uwsgi
# as uwsgi now
git clone https://github.com/unbit/uwsgicc.git .
exit # out of su - uwsgi
exit # out of the jail
Now on your real system run
uwsgi --socket 127.0.0.1:3031 --chdir /home/uwsgi/uwsgi --uid uwsgi --gid uwsgi --module uwsgicc --master --processes 4 --namespace /ns/001:mybeautifulhostname
If all goes well, uWSGI will set /ns/001
as the new root filesystem, assign mybeautifulhostname
as the hostname and hide the PIDs and IPC of the host system.
The first thing you should note is the uWSGI master becoming PID 1 (the “init” process) in the new namespace. All processes generated by the uWSGI stack will be reparented to it if something goes wrong. If the master dies, all jailed processes die.
Now point your web browser to your web server and you should see the uWSGI Control Center interface.
Pay attention to the information area. The node name (used by cluster subsystem) matches the real hostname as it does not make sense to have multiple jail in the same cluster group. In the hostname field instead you will see the hostname you have set.
Another important thing is that you can see all the jail processes from your real system (they will have a different set of PIDs), so if you want to take control of the jail you can easily do it.
注解
A good way to limit hardware usage of jails is to combine them with the cgroups subsystem.
Reloading uWSGI¶
When running in a jail, uWSGI uses another system for reloading: it’ll simply tell workers to bugger off and then exit. The parent process living outside the namespace will see this and respawn the stack in a new jail.
How secure is this sort of jailing?¶
Hard to say! All software tends to be secure until a hole is found.
Additional filesystems¶
When app is jailed to namespace it only has access to its virtual jail root filesystem. If there is any other filesystem mounted inside the jail directory, it won’t be accessible, unless you use namespace-keep-mount
.
# app1 jail is located here
namespace = /apps/app1
# nfs share mounted on the host side
namespace-keep-mount = /apps/app1/nfs
This will bind /apps/app1/nfs to jail, so that jailed app can access it under /nfs directory
# app1 jail is located here
namespace = /apps/app1
# nfs share mounted on the host side
namespace-keep-mount = /mnt/nfs1:/nfs
If the filesystem that we want to bind is mounted in path not contained inside our jail, than we can use “<source>:<dest>” syntax for –namespace-keep-mount. In this case the /mnt/nfs1 will be binded to /nfs directory inside the jail.
FreeBSD Jails¶
uWSGI 1.9.16 introduced native FreeBSD jails support.
FreeBSD jails can be seen as new-generation chroot() with fine-grained tuning of what this “jail” can see.
They are very similar to Linux namespaces even if a bit higher-level (from the API point of view).
Jails are available since FreeBSD 4
Why managing jails with uWSGI ?¶
Generally jails are managed using the system tool “jail” and its utilities.
Til now running uWSGI in FreeBSD jails was pretty common, but for really massive setups (read: hosting business) where an Emperor (for example) manages hundreds of unrelated uWSGI instances, the setup could be really overkill.
Managing jails directly in uWSGI config files highly reduce sysadmin costs and helps having a better organization of the whole infrastructure.
Old-style jails (FreeBSD < 8)¶
FreeBSD exposes two main api for managing jails. The old (and easier) one is based on the jail() function.
It is available since FreeBSD 4 and allows you to set the rootfs, the hostname and one ore more ipv4/ipv6 addresses
Two options are needed for running a uWSGI instance in a jail: –jail and –jail-ip4/–jail-ip6 (effectively they are 3 if you use IPv6)
--jail <rootfs> [hostname] [jailname]
--jail-ip4 <address>
(can be specified multiple times)
--jail-ip6 <address>
(can be specified multiple times)
Showing how to create the rootfs for your jail is not the objective of this document, but personally i hate rebuilding from sources, so generally i simply explode the base.tgz file from an official repository and chroot() to it to make the fine tuning.
An important thing you have to remember is that the ip addresses you attach to a jail must be available in the system (as aliases). As always we tend to abuse uWSGI facilities. In our case the –exec-pre-jail hook will do the trick
[uwsgi]
; create the jail with /jails/001 as rootfs and 'foobar' as hostname
jail = /jails/001 foobar
; create the alias on 'em0'
exec-pre-jail = ifconfig em0 192.168.0.40 alias
; attach the alias to the jail
jail-ip4 = 192.168.0.40
; bind the http-socket (we are now in the jail)
http-socket = 192.168.0.40:8080
; load the application (remember we are in the jail)
wsgi-file = myapp.wsgi
; drop privileges
uid = kratos
gid = kratos
; common options
master = true
processes = 2
New style jails (FreeBSD >= 8)¶
FreeBSD 8 introdiced a new advanced api for managing jails. Based on the jail_set() syscall, libjail exposes dozens of features and allows fine-tuning of your jails. To use the new api you need the –jail2 option (aliased as –libjail)
--jail2 <key>[=value]
Each –jail2 option maps 1:1 with a jail attribute so you can basically tune everything !
[uwsgi]
; create the jail with /jails/001 as rootfs
jail2 = path=/jails/001
; set hostname to 'foobar'
jail2 = host.hostname=foobar
; create the alias on 'em0'
exec-pre-jail = ifconfig em0 192.168.0.40 alias
; attach the alias to the jail
jail2 = ip4.addr=192.168.0.40
; bind the http-socket (we are now in the jail)
http-socket = 192.168.0.40:8080
; load the application (remember we are in the jail)
wsgi-file = myapp.wsgi
; drop privileges
uid = kratos
gid = kratos
; common options
master = true
processes = 2
Note for FreeBSD >= 8.4 but < 9.0¶
uWSGI uses ipc semaphores on FreeBSD < 9 (newer FreeBSD releases have POSIX semaphores support).
Since FreeBSD 8.4 you need to explicitely allows sysvipc in jails. So be sure to have
[uwsgi]
...
jail2 = allow.sysvipc=1
...
DevFS¶
The DevFS virtual filesystem manages the /dev directory on FreeBSD.
The /dev filesystem is not mounted in the jail, but you can need it for literally hundreds of reasons.
Two main approaches are available: mounting it in the /dev/ directory of the roots before creating the jail, or allowing the jail to mount it
[uwsgi]
; avoid re-mounting the file system every time
if-not-exists = /jails/001/dev/zero
exec-pre-jail = mount -t devfs devfs /jails/001/dev
endif =
; create the jail with /jails/001 as rootfs
jail2 = path=/jails/001
; set hostname to 'foobar'
jail2 = host.hostname=foobar
; create the alias on 'em0'
exec-pre-jail = ifconfig em0 192.168.0.40 alias
; attach the alias to the jail
jail2 = ip4.addr=192.168.0.40
; bind the http-socket (we are now in the jail)
http-socket = 192.168.0.40:8080
; load the application (remember we are in the jail)
wsgi-file = myapp.wsgi
; drop privileges
uid = kratos
gid = kratos
; common options
master = true
processes = 2
or (allow the jail itself to mount it)
[uwsgi]
; create the jail with /jails/001 as rootfs
jail2 = path=/jails/001
; set hostname to 'foobar'
jail2 = host.hostname=foobar
; create the alias on 'em0'
exec-pre-jail = ifconfig em0 192.168.0.40 alias
; attach the alias to the jail
jail2 = ip4.addr=192.168.0.40
; allows mount of devfs in the jail
jail2 = enforce_statfs=1
jail2 = allow.mount
jail2 = allow.mount.devfs
; ... and mount it
if-not-exists = /dev/zero
exec-post-jail = mount -t devfs devfs /dev
endif =
; bind the http-socket (we are now in the jail)
http-socket = 192.168.0.40:8080
; load the application (remember we are in the jail)
wsgi-file = myapp.wsgi
; drop privileges
uid = kratos
gid = kratos
; common options
master = true
processes = 2
Reloading¶
Reloading (or binary patching) is a bit annoying to manage as uWSGI need to re-exec itself, so you need a copy of the binary, plugins and the config file in your jail (unless you can sacrifice graceful reload and simply delegate the Emperor to respawn the instance)
Another approach is (like with devfs) mounting the directory with the uwsgi binary (and the eventual plugins) in the jail itself and instruct uWSGI to use this new path with –binary-path
The jidfile¶
Each jail can be referenced by a unique name (optional) or its “jid”. This is similar to a “pid”, as you can use it to send commands (and updates) to an already running jail. The –jidfile <file> option allows you to store the jid in a file for use with external applications.
Attaching to a jail¶
You can attach uWSGI instances to already running jails (they can be standard persistent jail too) using –jail-attach <id>
The id argument can be a jid or the name of the jail.
This feature requires FreeBSD 8
Debian/kFreeBSD¶
This is an official Debian project aiming at building an os with FreeBSD kernel and common Debian userspace.
It works really well, and it has support for jails too.
Let’s create a jail with debootstrap
debootstrap wheezy /jails/wheezy
add a network alias
ifconfig em0 192.168.173.105 netmask 255.255.255.0 alias
(change em0 with your network interface name)
and run it
uwsgi --http-socket 192.168.173.105:8080 --jail /jails/wheezy -jail-ip4 192.168.173.105
Jails with Forkpty Router¶
You can easily attach to FreeBSD jails with The Forkpty Router
Just remember to have /dev (well, /dev/ptmx) mounted in your jail to allow the forkpty() call
Learn how to deal with devfs_ruleset to increase security of your devfs
Notes¶
A jail is destroyed when the last process running in it dies
By default everything mounted under the rootfs (before entering the jail) will be seen by the jail it self (we have seen it before when dealing with devfs)
The Forkpty Router¶
Dealing with containers is now a common deployment pattern. One of the most annoying tasks when dealing with jails/namespaces is ‘attaching’ to already running instances.
The forkpty router aims at simplifyng the process giving a pseudoterminal server to your uWSGI instances.
A client connect to the socket exposed by the forkpty router and get a new pseudoterminal connected to a process (generally a shell, but can be whatever you want)
uwsgi mode VS raw mode¶
Clients connecting to the forkpty router can use two protocols for data exchange: uwsgi and raw mode.
The raw mode simply maps the socket to the pty, for such a reason you will not be able to resize your terminal or send specific signals. The advantage of this mode is in performance: no overhead for each char.
The uwsgi mode encapsulates every instruction (stdin, signals, window changes) in a uwsgi packet. This is very similar to how ssh works, so if you plan to use the forkpty router for shell sessions the uwsgi mode is the best choice (in terms of user experience).
The overhead of the uwsgi protocol (worst case) is 5 bytes for each stdin event (single char)
Running the forkpty router¶
The plugin is not builtin by default, so you have to compile it:
uwsgi --build-plugin plugins/forkptyrouter
or, using the old plugin build system:
python uwsgiconfig.py --plugin plugins/forkptyrouter
generally compiling the pty plugin is required too (for client access)
uwsgi --build-plugin plugins/pty
or again, using the old build system:
python uwsgiconfig.py --plugin plugins/pty
Alternatively, you can build all in one shot with:
UWSGI_EMBED_PLUGINS=pty,forkptyrouter make
Now you can run the forkptyrouter as a standard gateway (we use UNIX socket as we want a communication channel with jails, and we unshare the uts namespace to give a new hostname)
[uwsgi]
master = true
unshare = uts
exec-as-root = hostname iaminajail
uid = kratos
gid = kratos
forkpty-router = /tmp/fpty.socket
and connect with the pty client:
uwsgi --pty-connect /tmp/fpty.socket
now you have a shell (/bin/sh by default) in the uWSGI instance. Running hostname
will give you ‘iaminajail’
Eventually you can avoid using uWSGI to attacj to the pty and instead you can rely on this simple python script:
import socket
import sys
import os
import select
import copy
from termios import *
import atexit
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect(sys.argv[1])
tcattr = tcgetattr(0)
orig_tcattr = copy.copy(tcattr)
atexit.register(tcsetattr, 0, TCSANOW, orig_tcattr)
tcattr[0] |= IGNPAR
tcattr[0] &= ~(ISTRIP | IMAXBEL | BRKINT | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
tcattr[0] &= ~IUCLC;
tcattr[3] &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL);
tcattr[3] &= ~IEXTEN;
tcattr[1] &= ~OPOST;
tcattr[6][VMIN] = 1;
tcattr[6][VTIME] = 0;
tcsetattr(0, TCSANOW, tcattr);
while True:
(rl, wl, xl) = select.select([0, s], [], [])
if s in rl:
buf = s.recv(4096)
if not buf: break
os.write(1, buf)
if 0 in rl:
buf = os.read(0, 4096)
if not buf: break
s.send(buf)
The previous example uses raw mode, if you resize the client terminal you will se no updates.
To use the ‘uwsgi’ mode add a ‘u’:
[uwsgi]
master = true
unshare = uts
exec-as-root = hostname iaminajail
uid = kratos
gid = kratos
forkpty-urouter = /tmp/fpty.socket
uwsgi --pty-uconnect /tmp/fpty.socket
a single instance can expose both protocols on different sockets
[uwsgi]
master = true
unshare = uts
exec-as-root = hostname iaminajail
uid = kratos
gid = kratos
forkpty-router = /tmp/raw.socket
forkpty-urouter = /tmp/uwsgi.socket
Changing the default command¶
By default the forkpty router run /bin/sh on new connections.
You can change the command using the –forkptyrouter-command
[uwsgi]
master = true
unshare = uts
exec-as-root = hostname iaminajail
uid = kratos
gid = kratos
forkpty-router = /tmp/raw.socket
forkpty-urouter = /tmp/uwsgi.socket
forkptyrouter-command= /bin/zsh
The TunTap Router¶
The TunTap router is an ad-hoc solution for giving network connectivity to Linux processes running in a dedicated network namespace (well obviously it has other uses, but very probably this is the most interesting one, and the one for which it was developed)
The TunTap router is not compiled in by default.
For having it in one shot:
UWSGI_EMBED_PLUGINS=tuntap make
(yes the plugin is named only ‘tuntap’ as effectively it exposes various tuntap devices features)
The best way to use it is binding it to a unix socket, allowing processes in new namespaces to reach it (generally unix sockets are the best communication channel for linux namespaces).
The first config¶
We want our vassals to live in the 192.168.0.0/24 network, with 192.168.0.1 as default gateway.
The default gateway (read: the tuntap router) is managed by the Emperor itself
[uwsgi]
; create the tun device 'emperor0' and bind it to a unix socket
tuntap-router = emperor0 /tmp/tuntap.socket
; give it an ip address
exec-as-root = ifconfig emperor0 192.168.0.1 netmask 255.255.255.0 up
; setup nat
exec-as-root = iptables -t nat -F
exec-as-root = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
; enable linux ip forwarding
exec-as-root = echo 1 >/proc/sys/net/ipv4/ip_forward
; force vassals to be created in a new network namespace
emperor-use-clone = net
emperor = /etc/vassals
The vassals spawned by this Emperor will born without network connectivity.
To give them access to the public network we create a new tun device (it will exist only in the vassal network namespace) instructing it to route traffic to the Emperor tuntap unix socket:
[uwsgi]
; we need it as the vassal have no way to know it is jailed
; without it post_jail plugin hook would be never executed
jailed = true
; create uwsgi0 tun interface and force it to connect to the Emperor exposed unix socket
tuntap-device = uwsgi0 /tmp/tuntap.socket
; bring up loopback
exec-as-root = ifconfig lo up
; bring up interface uwsgi0
exec-as-root = ifconfig uwsgi0 192.168.0.2 netmask 255.255.255.0 up
; and set the default gateway
exec-as-root = route add default gw 192.168.0.1
; classic options
uid = customer001
gid = customer001
socket = /var/www/foobar.socket
psgi-file = foobar.pl
...
The embedded firewall¶
The TunTap router includes a very simple firewall for governing vassal’s traffic
Firewalling is based on 2 chains (in and out), and each rule is formed by 3 parameters: <action> <src> <dst>
The firewall is applied to traffic from the clients to the tuntap device (out) and the opposite (in)
The first matching rule stops the chain, if no rule applies, the policy is “allow”
the following rules allows access from vassals to the internet, but block vassals intercommunication
[uwsgi]
tuntap-router = emperor0 /tmp/tuntap.socket
tuntap-router-firewall-out = allow 192.168.0.0/24 192.168.0.1
tuntap-router-firewall-out = deny 192.168.0.0/24 192.168.0.0/24
tuntap-router-firewall-out = allow 192.168.0.0/24 0.0.0.0
tuntap-router-firewall-out = deny
tuntap-router-firewall-in = allow 192.168.0.1 192.168.0.0/24
tuntap-router-firewall-in = deny 192.168.0.0/24 192.168.0.0/24
tuntap-router-firewall-in = allow 0.0.0.0 192.168.0.0/24
tuntap-router-firewall-in = deny
exec-as-root = ifconfig emperor0 192.168.0.1 netmask 255.255.255.0 up
; setup nat
exec-as-root = iptables -t nat -F
exec-as-root = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
; enable linux ip forwarding
exec-as-root = echo 1 >/proc/sys/net/ipv4/ip_forward
; force vassals to be created in a new network namespace
emperor-use-clone = net
emperor = /etc/vassals
Security¶
The “switching” part of the TunTap router (read: mapping ip addresses to vassals) is pretty simple: the first packet received from a vassal by the TunTap router register the vassal for that ip address. A good approach (from a security point of view) is sending a ping packet soon after network setup in the vassal:
[uwsgi]
; create uwsgi0 tun interface and force it to connect to the Emperor exposed unix socket
tuntap-device = uwsgi0 /tmp/tuntap.socket
; bring up loopback
exec-as-root = ifconfig lo up
; bring up interface uwsgi0
exec-as-root = ifconfig uwsgi0 192.168.0.2 netmask 255.255.255.0 up
; and set the default gateway
exec-as-root = route add default gw 192.168.0.1
; ping something to register
exec-as-root = ping -c 1 192.168.0.1
; classic options
...
after a vassal/ip pair is registered, only that combo will be valid (so other vassals will not be able to use that address until the one holding it dies)
The Future¶
This is becoming a very important part of the unbit.it networking stack. We are currently working on:
- dynamic firewall rules (luajit resulted a great tool for writing fast networking rules)
- federation/proxy of tuntap router (the tuntaprouter can multiplex vassals networking over a tcp connection to an external tuntap router [that is why you can bind a tuntap router to a tcp address])
- authentication of vassals (maybe the old UNIX ancillary credentials could be enough)
- a stats server for network statistics (rx/tx/errors)
- a bandwidth shaper based on the blastbeat project
盯着你的应用(Keeping an eye on your apps)¶
Monitoring uWSGI with Nagios¶
The official uWSGI distribution includes a plugin adding Nagios-friendly output.
To monitor, and eventually get warning messages, via Nagios, launch the following command, where node
is the socket (UNIX or TCP) to monitor.
uwsgi --socket <node> --nagios
Setting warning messages¶
You can set a warning message directly from your app with the uwsgi.set_warning_message()
function. All ping responses (used by Nagios too) will report this message.
The embedded SNMP server¶
The uWSGI server embeds a tiny SNMP server that you can use to integrate your web apps with your monitoring infrastructure.
To enable SNMP support, you must run the uWSGI UDP server and choose a SNMP community string (which is the rudimentary authentication system used by SNMP).
./uwsgi -s :3031 -w staticfilesnmp --udp 192.168.0.1:2222 --snmp --snmp-community foo
# or the following. Using the SNMP option to pass the UDP address is a lot more elegant. ;)
./uwsgi -s :3031 -w myapp --master --processes 4 --snmp=192.168.0.1:2222 --snmp-community foo
This will run the uWSGI server on TCP port 3031 and UDP port 2222 with SNMP enabled with “foo” as the community string.
Please note that the SNMP server is started in the master process after dropping the privileges. If you want it to listen on a privileged port, you can either use Capabilities on Linux, or use the master-as-root
option to run the master process as root. The staticfilesnmp.py
file is included in the distribution and is a simple app that exports a counter via SNMP.
The uWSGI SNMP server exports 2 group of information:
- General information is managed by the uWSGI server itself. The base OID to access uWSGI SNMP information is
1.3.6.1.4.1.35156.17
(iso.org.dod.internet.private.enterprise.unbit.uwsgi
). General options are mapped to1.3.6.1.4.1.35156.17.1.x
. - Custom information is managed by the apps and accessed via
1.3.6.1.4.1.35156.17.2.x
So, to get the number of requests managed by the uWSGI server, you could do
snmpget -v2c -c foo 192.168.0.1:2222 1.3.6.1.4.1.35156.17.1.1 # 1.1 corresponds to ``general.requests``
Exporting custom values¶
To manage custom values from your app you have these Python functions,
uwsgi.snmp_set_counter32()
uwsgi.snmp_set_counter64()
uwsgi.snmp_set_gauge()
uwsgi.snmp_incr_counter32()
uwsgi.snmp_incr_counter64()
uwsgi.snmp_incr_gauge()
uwsgi.snmp_decr_counter32()
uwsgi.snmp_decr_counter64()
uwsgi.snmp_decr_gauge()
So if you wanted to export the number of users currently logged in (this is a gauge as it can lower) as custom OID 40, you’d call
users_logged_in = random.randint(0, 1024) # a more predictable source of information would be better.
uwsgi.snmp_set_gauge(40, users_logged_in)
and to look it up,
snmpget -v2c -c foo 192.168.0.1:2222 1.3.6.1.4.1.35156.17.2.40
The system snmp daemon (net-snmp) can be configured to proxy SNMP requests to uwsgi. This allows you to run the system daemon and uwsgi at the same time, and runs all SNMP requests through the system daemon first. To configure the system snmp daemon (net-snmp) to proxy connections to uwsgi, add these lines to the bottom of /etc/snmp/snmpd.conf and restart the daemon:
proxy -v 2c -c foo 127.0.0.1:2222 .1.3.6.1.4.1.35156.17
view systemview included .1.3.6.1.4.1.35156.17
Replace ‘foo’ and ‘2222’ with the community and port configured in uwsgi.
Pushing statistics (from 1.4)¶
IMPORTANT: the Metrics subsystem offers a better introduction to the following concepts. See The Metrics subsystem
Starting from uWSGI 1.4 you can push statistics (the same JSON blob you get with the The uWSGI Stats Server) via various systems (called stats pushers).
Statistics are pushed at regular intervals (default 3 seconds).
The ‘file’ stats pusher¶
By default the ‘file’ stats pusher is available up to 1.9.18. Starting from 1.9.19 is available as a plugin (stats_pusher_file).
It allows you to save json chunks to a file (open in appended mode)
[uwsgi]
socket = :3031
module = foobar
master = true
stats-push = file:path=/tmp/foobar,freq=10
this config will append JSON to the /tmp/foobar file every 10 seconds
The ‘mongodb’ stats pusher¶
This is the first developed stats pusher plugin, allowing you to store JSON data directly on a mongodb collection
[uwsgi]
plugins = stats_pusher_mongodb
socket = :3031
module = foobar
master = true
stats-push = mongodb:addr=127.0.0.1:5151,collection=uwsgi.mystats,freq=4
This config will insert JSON data to the collection uwsgi.mystats on the mongodb server 127.0.0.1:5151 every 4 seconds.
To build the plugin you need mongodb development headers (mongodb-dev on Debian/Ubuntu)
python uwsgiconfig.py --plugin plugins/stats_pusher_mongodb
will do the trick
Notes¶
You can configure all of the stats pusher you need, just specify multiple stats-push options
[uwsgi]
plugins = stats_pusher_mongodb
socket = :3031
module = foobar
master = true
stats-push = mongodb:addr=127.0.0.1:5151,collection=uwsgi.mystats,freq=4
stats-push = mongodb:addr=127.0.0.1:5152,collection=uwsgi.mystats,freq=4
stats-push = mongodb:addr=127.0.0.1:5153,collection=uwsgi.mystats,freq=4
stats-push = mongodb:addr=127.0.0.1:5154,collection=uwsgi.mystats,freq=4
Integration with Graphite/Carbon¶
Graphite is a kick-ass realtime graphing application built on top of three components:
- Whisper – a data storage system
- Carbon – a server for receiving data
- Python web application for graph rendering and management.
The uWSGI Carbon plugin allows you to send uWSGI’s internal statistics to one or more Carbon servers. It is compiled in by default as of uWSGI 1.0, though it can also be built as a plugin.
Quickstart¶
For the sake of illustration, let’s say your Carbon server is listening on
127.0.0.1:2003
and your uWSGI instance is on the machine debian32
,
listening on 127.0.0.1:3031
with 4 processes. By adding the --carbon
option to your uWSGI instance you’ll instruct it to send its statistics to
the Carbon server periodically. The default period is 60 seconds.
uwsgi --socket 127.0.0.1:3031 --carbon 127.0.0.1:2003 --processes 4
Metrics are named like uwsgi.<hostname>.<id>.requests
and
uwsgi.<hostname>.<id>.worker<n>.requests
, where:
hostname
– machine’s hostnameid
– name of the first uWSGI socket (with dots replaced by underscores)n
– number of the worker processes (1-based).
Examples of names of Carbon metrics generated by uWSGI:
uwsgi.debian32.127_0_0_1:3031.requests
(uwsgi.<hostname>.<id>.requests
)uwsgi.debian32.127_0_0_1:3031.worker1.requests
(uwsgi.<hostname>.<id>.worker<n>.requests
)uwsgi.debian32.127_0_0_1:3031.worker2.requests
(uwsgi.<hostname>.<id>.worker<n>.requests
)uwsgi.debian32.127_0_0_1:3031.worker3.requests
(uwsgi.<hostname>.<id>.worker<n>.requests
)uwsgi.debian32.127_0_0_1:3031.worker4.requests
(uwsgi.<hostname>.<id>.worker<n>.requests
).
The uWSGI Stats Server¶
In addition to SNMP, uWSGI also supports a Stats Server mechanism which exports the uWSGI state as a JSON object to a socket.
Simply use the stats
option followed by a valid socket address.
--stats 127.0.0.1:1717
--stats /tmp/statsock
--stats :5050
--stats @foobar
If a client connects to the specified socket it will get a JSON object containing uWSGI internal statistics before the connection ends.
uwsgi --socket :3031 --stats :1717 --module welcome --master --processes 8
then
nc 127.0.0.1 1717
# or for convenience...
uwsgi --connect-and-read 127.0.0.1:1717
will return something like this:
{
"workers": [{
"id": 1,
"pid": 31759,
"requests": 0,
"exceptions": 0,
"status": "idle",
"rss": 0,
"vsz": 0,
"running_time": 0,
"last_spawn": 1317235041,
"respawn_count": 1,
"tx": 0,
"avg_rt": 0,
"apps": [{
"id": 0,
"modifier1": 0,
"mountpoint": "",
"requests": 0,
"exceptions": 0,
"chdir": ""
}]
}, {
"id": 2,
"pid": 31760,
"requests": 0,
"exceptions": 0,
"status": "idle",
"rss": 0,
"vsz": 0,
"running_time": 0,
"last_spawn": 1317235041,
"respawn_count": 1,
"tx": 0,
"avg_rt": 0,
"apps": [{
"id": 0,
"modifier1": 0,
"mountpoint": "",
"requests": 0,
"exceptions": 0,
"chdir": ""
}]
}, {
"id": 3,
"pid": 31761,
"requests": 0,
"exceptions": 0,
"status": "idle",
"rss": 0,
"vsz": 0,
"running_time": 0,
"last_spawn": 1317235041,
"respawn_count": 1,
"tx": 0,
"avg_rt": 0,
"apps": [{
"id": 0,
"modifier1": 0,
"mountpoint": "",
"requests": 0,
"exceptions": 0,
"chdir": ""
}]
}, {
"id": 4,
"pid": 31762,
"requests": 0,
"exceptions": 0,
"status": "idle",
"rss": 0,
"vsz": 0,
"running_time": 0,
"last_spawn": 1317235041,
"respawn_count": 1,
"tx": 0,
"avg_rt": 0,
"apps": [{
"id": 0,
"modifier1": 0,
"mountpoint": "",
"requests": 0,
"exceptions": 0,
"chdir": ""
}]
}, {
"id": 5,
"pid": 31763,
"requests": 0,
"exceptions": 0,
"status": "idle",
"rss": 0,
"vsz": 0,
"running_time": 0,
"last_spawn": 1317235041,
"respawn_count": 1,
"tx": 0,
"avg_rt": 0,
"apps": [{
"id": 0,
"modifier1": 0,
"mountpoint": "",
"requests": 0,
"exceptions": 0,
"chdir": ""
}]
}, {
"id": 6,
"pid": 31764,
"requests": 0,
"exceptions": 0,
"status": "idle",
"rss": 0,
"vsz": 0,
"running_time": 0,
"last_spawn": 1317235041,
"respawn_count": 1,
"tx": 0,
"avg_rt": 0,
"apps": [{
"id": 0,
"modifier1": 0,
"mountpoint": "",
"requests": 0,
"exceptions": 0,
"chdir": ""
}]
}, {
"id": 7,
"pid": 31765,
"requests": 0,
"exceptions": 0,
"status": "idle",
"rss": 0,
"vsz": 0,
"running_time": 0,
"last_spawn": 1317235041,
"respawn_count": 1,
"tx": 0,
"avg_rt": 0,
"apps": [{
"id": 0,
"modifier1": 0,
"mountpoint": "",
"requests": 0,
"exceptions": 0,
"chdir": ""
}]
}, {
"id": 8,
"pid": 31766,
"requests": 0,
"exceptions": 0,
"status": "idle",
"rss": 0,
"vsz": 0,
"running_time": 0,
"last_spawn": 1317235041,
"respawn_count": 1,
"tx": 0,
"avg_rt": 0,
"apps": [{
"id": 0,
"modifier1": 0,
"mountpoint": "",
"requests": 0,
"exceptions": 0,
"chdir": ""
}]
}]
}
uwsgitop¶
uwsgitop
is a top-like command that uses the stats server. It is available on PyPI, so use easy_install
or pip
to install it (package name uwsgitop
, naturally).
The sources are available on Github. https://github.com/unbit/uwsgitop
异步和循环引擎 (Async and loop engines)¶
uWSGI asynchronous/non-blocking modes (updated to uWSGI 1.9)¶
警告
Beware! Async modes will not speed up your app, they are aimed at improving concurrency. Do not expect that enabling some of the modes will work flawlessly, asynchronous/evented/non-blocking systems require app cooperation, so if your app is developed without taking specific async engine rules into consideration, you are doing it wrong. Do not trust people suggesting you to blindly use async/evented/non-blocking systems!
Glossary¶
uWSGI, following its modular approach, splits async engines into two families.
Suspend/Resume engines¶
They simply implement coroutine/green threads techniques. They have no event engine, so you have to use
the one supplied by uWSGI. An Event engine is generally a library exporting primitives for platform-independent
non-blocking I/O (libevent, libev, libuv, etc.). The uWSGI event engine is enabled using the --async <n>
option.
Currently the uWSGI distribution includes the following suspend/resume engines:
uGreen
- Unbit’s green thread implementation (based onswapcontext()
)Greenlet
- Python greenlet moduleStackless
- Stackless PythonFiber
- Ruby 1.9 fibers
Running the uWSGI async mode without a proper suspend/resume engine will raise a warning, so for a minimal non-blocking app you will need something like that:
uwsgi --async 100 --ugreen --socket :3031
An important aspect of suspend/resume engines is that they can easily destroy your process if it is not aware of them. Some of the language plugins (most notably Python) have hooks to cooperate flawlessly with coroutines/green threads. Other languages may fail miserably. Always check the uWSGI mailing list or IRC channel for updated information.
Older uWSGI releases supported an additional system: callbacks. Callbacks is the approach used by popular systems like node.js. This approach requires heavy app cooperation, and for complex projects like uWSGI dealing with this is extremely complex. For that reason, callback approach is not supported (even if technically possible) Software based on callbacks (like The Tornado loop engine) can be used to combine them with some form of suspend engine.
I/O engines (or event systems)¶
uWSGI includes an highly optimized evented technology, but can use alternative approaches too.
I/O engines always require some suspend/resume engine, otherwise ugly things happen (the whole uWSGI codebase is coroutine-friendly, so you can play with stacks pretty easily).
Currently supported I/O engines are:
- The Tornado loop engine
- libuv (work in progress)
- libev (work in progress)
Loop engines¶
Loop engines are packages/libraries exporting both suspend/resume techniques and an event system. When loaded, they override the way uWSGI manages connections and signal handlers (uWSGI signals, not POSIX signals).
Currently uWSGI supports the following loop engines:
Gevent
(Python, libev, greenlet)Coro::AnyEvent
(Perl, coro, anyevent)
Although they are generally used by a specific language, pure-C uWSGI plugins (like the CGI one) can use them to increase concurrency without problems.
Async switches¶
To enable async mode, you use the --async
option (or some shortcut for it, exported by loop engine plugins).
The argument of the --async
option is the number of “cores” to initialize. Each core can manage a single request, so the more core you
spawn, more requests you will be able to manage (and more memory you will use). The job of the suspend/resume engines
is to stop the current request management, move to another core, and eventually come back to the old one (and so on).
Technically, cores are simple memory structures holding request’s data, but to give the user the illusion of a multithreaded system we use that term.
The switch between cores needs app cooperation. There are various ways to accomplish that, and generally, if you are using a loop engine, all is automagic (or requires very little effort).
警告
If you are in doubt, do not use async mode.
Running uWSGI in Async mode¶
To start uWSGI in async mode, pass the --async
option with the number of “async cores” you want.
./uwsgi --socket :3031 -w tests.cpubound_async --async 10
This will start uWSGI with 10 async cores. Each async core can manage a request, so with this setup you can accept 10 concurrent requests with only one process. You can also start more processes (with the --processes
option), each will have its own pool of async cores.
When using harakiri mode, every time an async core accepts a request, the harakiri timer is reset. So even if a request blocks the async system, harakiri will save you.
The tests.cpubound_async
app is included in the source distribution. It’s very simple:
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
for i in range(1, 10000):
yield "<h1>%s</h1>" % i
Every time the application does yield
from the response function, the execution of the app is stopped, and a new request or a previously suspended request on another async core will take over. This means the number of async cores is the number of requests that can be queued.
If you run the tests.cpubound_async
app on a non-async server, it will block all processing: will not accept other requests until the heavy cycle of 10000 <h1>
s is done.
Waiting for I/O¶
If you are not under a loop engine, you can use the uWSGI API to wait for I/O events.
Currently only 2 functions are exported:
These functions may be called in succession to wait for multiple file descriptors:
uwsgi.wait_fd_read(fd0)
uwsgi.wait_fd_read(fd1)
uwsgi.wait_fd_read(fd2)
yield "" # yield the app, let uWSGI do its magic
Sleeping¶
On occasion you might want to sleep in your app, for example to throttle bandwidth.
Instead of using the blocking time.sleep(N)
function, use uwsgi.async_sleep(N)
to yield control for N seconds.
参见
See tests/sleeping_async.py
for an example.
Suspend/Resume¶
Yielding from the main application routine is not very practical, as most of the time your app is more advanced than a simple callable and is formed of tons of functions and various levels of call depth.
Worry not! You can force a suspend (using coroutine/green thread) by simply calling uwsgi.suspend()
:
uwsgi.wait_fd_read(fd0)
uwsgi.suspend()
uwsgi.suspend()
will automatically call the chosen suspend engine (uGreen, greenlet, etc.).
Static files¶
Static file server will automatically use the loaded async engine.
The Gevent loop engine¶
Gevent is an amazing non-blocking Python network library built on top of
libev
and greenlet
. Even though uWSGI supports Greenlet as
suspend-resume/greenthread/coroutine library, it requires a lot of effort and
code modifications to work with gevent. The gevent plugin requires gevent
1.0.0 and uWSGI asynchronous/non-blocking modes (updated to uWSGI 1.9) mode.
Notes¶
- The SignalFramework is fully working with Gevent mode. Each handler
will be executed in a dedicated greenlet. Look at
tests/ugevent.py
for an example. - uWSGI multithread mode (
threads
option) will not work with Gevent. Running Python threads in your apps is supported. - Mixing uWSGI’s Async API with gevent’s is EXPLICITLY FORBIDDEN.
Building the plugin (uWSGI >= 1.4)¶
The gevent plugin is compiled in by default when the default profile is used. Doing the following will install the python plugin as well as the gevent one:
pip install uwsgi
Building the plugin (uWSGI < 1.4)¶
A ‘gevent’ build profile can be found in the buildconf
directory.
python uwsgiconfig --build gevent
# or...
UWSGI_PROFILE=gevent make
# or...
UWSGI_PROFILE=gevent pip install git+git://github.com/unbit/uwsgi.git
# or...
python uwsgiconfig --plugin plugins/gevent # external plugin
Running uWSGI in gevent mode¶
uwsgi --gevent 100 --socket :3031 --module myapp
or for a modular build:
uwsgi --plugins python,gevent --gevent 100 --socket :3031 --module myapp
the argument of –gevent is the number of async cores to spawn
A crazy example¶
The following example shows how to sleep in a request, how to make asynchronous network requests and how to continue doing logic after a request has been closed.
import gevent
import gevent.socket
def bg_task():
for i in range(1,10):
print "background task", i
gevent.sleep(2)
def long_task():
for i in range(1,10):
print i
gevent.sleep()
def application(e, sr):
sr('200 OK', [('Content-Type','text/html')])
t = gevent.spawn(long_task)
t.join()
yield "sleeping for 3 seconds...<br/>"
gevent.sleep(3)
yield "done<br>"
yield "getting some ips...<br/>"
urls = ['www.google.com', 'www.example.com', 'www.python.org', 'projects.unbit.it']
jobs = [gevent.spawn(gevent.socket.gethostbyname, url) for url in urls]
gevent.joinall(jobs, timeout=2)
for j in jobs:
yield "ip = %s<br/>" % j.value
gevent.spawn(bg_task) # this task will go on after request end
Monkey patching¶
uWSGI uses native gevent api, so it does not need monkey patching. That said,
your code may need it, so remember to call gevent.monkey.patch_all()
at the
start of your app. As of uWSGI 1.9, the convenience option
--gevent-monkey-patch
will do that for you.
A common example is using psycopg2_gevent
with django. Django will make a
connection to postgres for each thread (storing it in thread locals).
As the uWSGI gevent plugin runs on a single thread this approach will lead to a
deadlock in psycopg. Enabling monkey patch will allow you to map thread locals
to greenlets (though you could avoid full monkey patching and only call
gevent.monkey.patch_thread()
) and solves the issue:
import gevent.monkey
gevent.monkey.patch_thread()
import gevent_psycopg2
gevent_psycopg2.monkey_patch()
or (to monkey patch everything)
import gevent.monkey
gevent.monkey.patch_all()
import gevent_psycopg2
gevent_psycopg2.monkey_patch()
Notes on clients and frontends¶
- If you’re testing a WSGI application that generates a stream of data, you
should know that
curl
by default buffers data until a newline. So make sure you either disable curl’s buffering with the-N
flag or have regular newlines in your output. - If you are using Nginx in front of uWSGI and wish to stream data from your app, you’ll probably want to disable Nginx’s buffering.
uwsgi_buffering off;
The Tornado loop engine¶
Available from: `uWSGI 1.9.19-dev`
Supported suspend engines: `greenlet`
Supported CPython versions: `all of tornado supported versions`
The tornado loop engine allows you to integrate your uWSGI stack with the Tornado IOLoop class.
Basically every I/O operation of the server is mapped to a tornado IOLoop callback. Making RPC, remote caching, or simply writing responses is managed by the Tornado engine.
As uWSGI is not written with a callback-based programming approach, integrating with those kind of libraries requires some form of “suspend” engine (green threads/coroutines)
Currently the only supported suspend engine is the “greenlet” one. Stackless python could work too (needs testing).
PyPy is currently not supported (albeit technically possibile thanks to continulets). Drop a mail to Unbit staff if you are interested.
Why ?¶
The Tornado project includes a simple WSGI server by itself. In the same spirit of the Gevent plugin, the purpose of Loop engines is allowing external prejects to use (and abuse) the uWSGI api, for better performance, versatility and (maybe the most important thing) resource usage.
All of the uWSGI subsystems are available (from caching, to websockets, to metrics) in your tornado apps, and the WSGI engine is the battle-tested uWSGI one.
Installation¶
The tornado plugin is currently not built-in by default. To have both tornado and greenlet in a single binary you can do
UWSGI_EMBED_PLUGINS=tornado,greenlet pip install tornado greenlet uwsgi
or (from uWSGI sources, if you already have tornado and greenlet installed)
UWSGI_EMBED_PLUGINS=tornado,greenlet make
Running it¶
The --tornado
option is exposed by the tornado plugin, allowing you to set optimal parameters:
uwsgi --http-socket :9090 --wsgi-file myapp.py --tornado 100 --greenlet
this will run a uWSGI instance on http port 9090 using tornado as I/O (and time) management and greenlet as suspend engine
100 async cores are allocated, allowing you to manage up to 100 concurrent requests
Integrating WSGI with the tornado api¶
For the way WSGI works, dealing with callback based programming is pretty hard (if not impossible).
Thanks to greenlet we can suspend the execution of our WSGI callable until a tornado IOLoop event is available:
from tornado.httpclient import AsyncHTTPClient
import greenlet
import functools
# this gives us access to the main IOLoop (the same used by uWSGI)
from tornado.ioloop import IOLoop
io_loop = IOLoop.instance()
# this is called at the end of the external HTTP request
def handle_request(me, response):
if response.error:
print("Error:", response.error)
else:
me.result = response.body
# back to the WSGI callable
me.switch()
def application(e, sr):
me = greenlet.getcurrent()
http_client = AsyncHTTPClient()
http_client.fetch("http://localhost:9191/services", functools.partial(handle_request, me))
# suspend the execution until an IOLoop event is available
me.parent.switch()
sr('200 OK', [('Content-Type','text/plain')])
return me.result
Welcome to Callback-Hell¶
As always, it is not the job of uWSGI to judge programming approaches. It is a tool for sysadmins, and sysadmins should be tolerant with developers choices.
One of the things you will pretty soon experiment with this approach to programming is the callback-hell.
Let’s extend the previous example to wait 10 seconds before sending back the response to the client
from tornado.httpclient import AsyncHTTPClient
import greenlet
import functools
# this gives us access to the main IOLoop (the same used by uWSGI)
from tornado.ioloop import IOLoop
io_loop = IOLoop.instance()
def sleeper(me):
#TIMED OUT
# finally come back to WSGI callable
me.switch()
# this is called at the end of the external HTTP request
def handle_request(me, response):
if response.error:
print("Error:", response.error)
else:
me.result = response.body
# add another callback in the chain
me.timeout = io_loop.add_timeout(time.time() + 10, functools.partial(sleeper, me))
def application(e, sr):
me = greenlet.getcurrent()
http_client = AsyncHTTPClient()
http_client.fetch("http://localhost:9191/services", functools.partial(handle_request, me))
# suspend the execution until an IOLoop event is available
me.parent.switch()
# unregister the timer
io_loop.remove_timeout(me.timeout)
sr('200 OK', [('Content-Type','text/plain')])
return me.result
here we have chained two callbacks, with the last one being responsable for giving back control to the WSGI callable
The code could looks ugly or overcomplex (compared to other approaches like gevent) but this is basically the most efficient way to increase concurrency (both in terms of memory usage and performance). Technologies like node.js are becoming popular thanks to the results they allow to accomplish.
WSGI generators (aka yield all over the place)¶
Take the following WSGI app:
def application(e, sr):
sr('200 OK', [('Content-Type','text/html')])
yield "one"
yield "two"
yield "three"
if you have already played with uWSGI async mode, you knows that every yield internally calls the used suspend engine (greenlet.switch() in our case).
That means we will enter the tornado IOLoop engine soon after having called “application()”. How we can give the control back to our callable if we are not waiting for events ?
The uWSGI async api has been extended to support the “schedule_fix” hook. It allows you to call a hook soon after the suspend engine has been called.
In the tornado’s case this hook is mapped to something like:
io_loop.add_callback(me.switch)
in this way after every yield a me.switch() function is called allowing the resume of the callable.
Thanks to this hook you can transparently host standard WSGI applications without changing them.
Binding and listening with Tornado¶
The Tornado IOLoop is executed after fork() in every worker. If you want to bind to network addresses with Tornado, remember to use different ports for each workers:
from uwsgidecorators import *
import tornado.web
# this is our Tornado-managed app
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
t_application = tornado.web.Application([
(r"/", MainHandler),
])
# here happens the magic, we bind after every fork()
@postfork
def start_the_tornado_servers():
application.listen(8000 + uwsgi.worker_id())
# this is our WSGI callable managed by uWSGI
def application(e, sr):
...
Remember: do no start the IOLoop class. uWSGI will do it by itself as soon as the setup is complete
uGreen – uWSGI Green Threads¶
uGreen is an implementation of green threads on top of the uWSGI async platform.
It is very similar to Python’s greenlet but built on top of the POSIX swapcontext()
function. To take advantage of uGreen you have to set the number of async cores that will be mapped to green threads.
For example if you want to spawn 30 green threads:
./uwsgi -w tests.cpubound_green -s :3031 --async 30 --ugreen
The ugreen
option will enable uGreen on top of async mode.
Now when you call uwsgi.suspend()
in your app, you’ll be switched off to another green thread.
Security and performance¶
To ensure (relative) isolation of green threads, every stack area is protected by so called “guard pages”.
An attempt to write out of the stack area of a green thread will result in a segmentation fault/bus error (and the process manager, if enabled, will respawn the worker without too much damage).
The context switch is very fast, we can see it as:
- On switch
- Save the Python Frame pointer
- Save the recursion depth of the Python environment (it is simply an int)
- Switch to the main stack
- On return
- Re-set the uGreen stack
- Re-set the recursion depth
- Re-set the frame pointer
The stack/registers switch is done by the POSIX swapcontext()
call and we don’t have to worry about it.
Async I/O¶
For managing async I/O you can use the Async mode FD wait functions uwsgi.wait_fd_read()
and uwsgi.wait_fd_write()
.
Stack size¶
You can choose the uGreen stack size using the ugreen-stacksize <pages>
option. The argument is in pages, not bytes.
Is this better than Greenlet or Stackless Python?¶
Weeeeelll... it depends. uGreen is faster (the stack is preallocated) but requires more memory (to allocate a stack area for every core). Stackless and Greenlet probably require less memory... but Stackless requires a heavily patched version of Python.
If you’re heavily invested in making your app as async-snappy as possible, it’s always best to do some tests to choose the best one for you. As far as uWSGI is concerned, you can move from async engine to another without changing your code.
What about python-coev
?¶
Lots of uGreen has been inspired by it. The author’s way to map Python threads to their implementation allows python-coev
to be a little more “trustworthy” than Stackless Python. However, like Stackless, it requires a patched version of Python... :(
Can I use uGreen to write Comet apps?¶
Yeah! Sure! Go ahead. In the distribution you will find the ugreenchat.py
script. It is a simple/dumb multiuser Comet chat. If you want to test it (for example 30 users) run it with
./uwsgi -s :3031 -w ugreenchat --async 30 --ugreen
The code has comments for every ugreen-related line. You’ll need Bottle, an amazing Python web micro framework to use it.
Psycopg2 improvements¶
uGreen can benefit from the new psycopg2 async extensions and the psycogreen project. See the tests/psycopg2_green.py
and tests/psycogreen_green.py
files for examples.
The asyncio loop engine (CPython >= 3.4, uWSGI >= 2.0.4)¶
警告
Status: EXPERIMENTAL, lot of implications, especially in respect to the WSGI standard
The asyncio
plugin exposes a loop engine built on top of the asyncio
CPython API (https://docs.python.org/3.4/library/asyncio.html#module-asyncio).
As uWSGI is not callback based, you need a suspend engine (currently only the ‘greenlet’ one is supported) to manage the WSGI callable.
Why not map the WSGI callable to a coroutine?¶
The reason is pretty simple: this would break WSGI in every possible way. (Let’s not go into the details here.)
For this reason each uWSGI core is mapped to a greenlet (running the WSGI callable).
This greenlet registers events and coroutines in the asyncio event loop.
Callback vs. coroutines¶
When starting to playing with asyncio you may get confused between callbacks and coroutines.
Callbacks are executed when a specific event raises (for example when a file descriptor is ready for read). They are basically standard functions executed in the main greenlet (and eventually they can switch back control to a specific uWSGI core).
Coroutines are more complex: they are pretty close to a greenlet, but internally they work on Python frames instead of C stacks. From a Python programmer point of view, coroutines are very special generators. Your WSGI callable can spawn coroutines.
Building uWSGI with asyncio support¶
An ‘asyncio’ build profile is available in the official source tree (it will build greenlet support too).
CFLAGS="-I/usr/local/include/python3.4" make PYTHON=python3.4 asyncio
or
CFLAGS="-I/usr/local/include/python3.4" UWSGI_PROFILE="asyncio" pip3 install uwsgi
be sure to use Python 3.4+ as the Python version and to add the greenlet include directory to CFLAGS
(this may not be needed if you installed greenlet support from your distribution’s packages).
The first example: a simple callback¶
Let’s start with a simple WSGI callable triggering a function 2 seconds after the callable has returned (magic!).
import asyncio
def two_seconds_elapsed():
print("Hello 2 seconds elapsed")
def application(environ, start_response):
start_response('200 OK', [('Content-Type','text/html')])
asyncio.get_event_loop().call_later(2, two_seconds_elapsed)
return [b"Hello World"]
Once called, the application function will register a callable in the asyncio event loop and then will return to the client.
After two seconds the event loop will run the function.
You can run the example with:
uwsgi --asyncio 10 --http-socket :9090 --greenlet --wsgi-file app.py
--asyncio
is a shortcut enabling 10 uWSGI async cores, enabling you to manage up to 10 concurrent requests with a single process.
But how to wait for a callback completion in the WSGI callable? We can suspend our WSGI function using greenlets (remember our WSGI callable is wrapped on a greenlet):
import asyncio
import greenlet
def two_seconds_elapsed(me):
print("Hello 2 seconds elapsed")
# back to WSGI callable
me.switch()
def application(environ, start_response):
start_response('200 OK', [('Content-Type','text/html')])
myself = greenlet.getcurrent()
asyncio.get_event_loop().call_later(2, two_seconds_elapsed, myself)
# back to event loop
myself.parent.switch()
return [b"Hello World"]
And we can go even further abusing the uWSGI support for WSGI generators:
import asyncio
import greenlet
def two_seconds_elapsed(me):
print("Hello 2 seconds elapsed")
me.switch()
def application(environ, start_response):
start_response('200 OK', [('Content-Type','text/html')])
myself = greenlet.getcurrent()
asyncio.get_event_loop().call_later(2, two_seconds_elapsed, myself)
myself.parent.switch()
yield b"One"
asyncio.get_event_loop().call_later(2, two_seconds_elapsed, myself)
myself.parent.switch()
yield b"Two"
Another example: Futures and coroutines¶
You can spawn coroutines from your WSGI callable using the asyncio.Task
facility:
import asyncio
import greenlet
@asyncio.coroutine
def sleeping(me):
yield from asyncio.sleep(2)
# back to callable
me.switch()
def application(environ, start_response):
start_response('200 OK', [('Content-Type','text/html')])
myself = greenlet.getcurrent()
# enqueue the coroutine
asyncio.Task(sleeping(myself))
# suspend to event loop
myself.parent.switch()
# back from event loop
return [b"Hello World"]
Thanks to Futures we can even get results back from coroutines...
import asyncio
import greenlet
@asyncio.coroutine
def sleeping(me, f):
yield from asyncio.sleep(2)
f.set_result(b"Hello World")
# back to callable
me.switch()
def application(environ, start_response):
start_response('200 OK', [('Content-Type','text/html')])
myself = greenlet.getcurrent()
future = asyncio.Future()
# enqueue the coroutine with a Future
asyncio.Task(sleeping(myself, future))
# suspend to event loop
myself.parent.switch()
# back from event loop
return [future.result()]
A more advanced example using the aiohttp
module (remember to pip install aiohttp
it, it’s not a standard library module)
import asyncio
import greenlet
import aiohttp
@asyncio.coroutine
def sleeping(me, f):
yield from asyncio.sleep(2)
response = yield from aiohttp.request('GET', 'http://python.org')
body = yield from response.read_and_close()
# body is a byterray !
f.set_result(body)
me.switch()
def application(environ, start_response):
start_response('200 OK', [('Content-Type','text/html')])
myself = greenlet.getcurrent()
future = asyncio.Future()
asyncio.Task(sleeping(myself, future))
myself.parent.switch()
# this time we use yield, just for fun...
yield bytes(future.result())
Status¶
- The plugin is considered experimental (the implications of asyncio with WSGI are currently unclear). In the future it could be built by default when Python >= 3.4 is detected.
- While (more or less) technically possible, mapping a WSGI callable to a Python 3 coroutine is not expected in the near future.
- The plugin registers hooks for non blocking reads/writes and timers. This means you can automagically use the uWSGI API with asyncio. Check the https://github.com/unbit/uwsgi/blob/master/tests/websockets_chat_asyncio.py example.
支持的 Web 服务器¶
Apache support¶
Currently there are three uwsgi-protocol related apache2 modules available.
mod_uwsgi¶
This is the original module. It is solid, but incredibly ugly and does not follow a lot of apache coding convention style.
mod_uwsgi
can be used in two ways:
- The “assbackwards” way (the default one). It is the fastest but somewhat far from the Apache2 API. If you do not use Apache2 filters (including gzip) for content generated by uWSGI, use this mode.
- The “cgi” mode. This one is somewhat slower but better integrated with Apache. To use the CGI mode, pass
-C
to the uWSGI server.
Options¶
注解
All of the options can be set per-host or per-location.
uWSGISocket <path> [timeout] Absolute path and optional timeout in seconds of uwsgi server socket.
uWSGISocket2 <path> Absolute path of failover uwsgi server socket
uWSGIServer <host:port> Address and port of an UWSGI server (e.g. localhost:4000)
uWSGIModifier1 <int> Set uWSGI modifier1
uWSGIModifier2 <int> Set uWSGI modifier2
uWSGIForceScriptName <value> Force SCRIPT_NAME
(app name)
uWSGIForceCGIMode <on/off> Force uWSGI CGI mode for perfect integration with apache filters
uWSGIForceWSGIScheme <value> Force the WSGI scheme var (set by default to “http”)
uWSGIMaxVars <int> Set the maximum allowed number of uwsgi protocol variables (default 128)
To pass custom variables use the SetEnv
directive:
SetEnv UWSGI_SCRIPT yourapp
mod_proxy_uwsgi¶
This is the latest module and probably the best bet for the future. It is a “proxy” module, so you will get all of the features exported by mod_proxy. It is fully “apache api compliant” so it should be easy to integrate with the available modules. Using it is easy; just remember to load mod_proxy and mod_proxy_uwsgi modules in your apache config.
ProxyPass /foo uwsgi://127.0.0.1:3032/
ProxyPass /bar uwsgi://127.0.0.1:3033/
ProxyPass / uwsgi://127.0.0.1:3031/
The first two forms set SCRIPT_NAME respectively to /foo and /bar while the last one use an empty SCRIPT_NAME. You can set additional uwsgi vars using the SetEnv directive and load balance requests using mod_proxy_balancer.
<Proxy balancer://mycluster>
BalancerMember uwsgi://192.168.1.50:3031/
BalancerMember uwsgi://192.168.1.51:3031/
</Proxy>
ProxyPass / balancer://mycluster
Pay attention to the last slash in the member/node definition. It is optional for non-empty SCRIPT_NAME/mountpoints but required for apps mounted in the root of the domain. Currently the module lacks the ability to set modifiers, though this will be fixed soon.
注解
mod_proxy_uwsgi is considered stable starting from uWSGI 2.0.6
注解
If you want to use this module (and help the uWSGI project), report any bugs you find, rather than falling back to the ancient (and ugly) mod_uwsgi
Starting from Apache 2.4.9, support for Unix sockets has been added. The syntax is pretty simple:
ProxyPass / unix:/tmp/uwsgi.sock|uwsgi://
mod_Ruwsgi¶
This module is based on the SCGI module written by Roger Florkowski.
注解
This module is currently undocumented.
Cherokee support¶
注解
Recent official versions of Cherokee have an uWSGI configuration wizard. If
you want to use it you have to install uWSGI in a directory included in your
system PATH
.
- Set the UWSGI handler for your target.
- If you are using the default target (
/
) remember to uncheck thecheck_file
property. - Configure an “information source” of type “Remote”, specifying the socket name of uWSGI. If your uWSGI has TCP support, you can build a cluster by spawning the uWSGI server on a different machine.
注解
Remember to add a target for all of your URI containing static files (ex. /media /images ...) using an appropriate handler
Dynamic apps¶
If you want to hot-add apps specify the UWSGI_SCRIPT
var in the uWSGI handler options:
- In the section: Add new custom environment variable specify
UWSGI_SCRIPT
as name and the name of your WSGI script (without the .py extension) as the value.
Your app will be loaded automatically at the first request.
Native HTTP support¶
HTTPS support (from 1.3)¶
Use the https <socket>,<certificate>,<key>
option. This option may be
specified multiple times. First generate your server key, certificate signing
request, and self-sign the certificate using the OpenSSL toolset:
注解
You’ll want a real SSL certificate for production use.
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
Then start the server using the SSL certificate and key just generated:
uwsgi --master --https 0.0.0.0:8443,foobar.crt,foobar.key
As port 443, the port normally used by HTTPS, is privileged (ie. non-root processes may not bind to it), you can use the shared socket mechanism and drop privileges after binding like thus:
uwsgi --shared-socket 0.0.0.0:443 --uid roberto --gid roberto --https =0,foobar.crt,foobar.key
uWSGI will bind to 443 on any IP, then drop privileges to those of roberto
,
and use the shared socket 0 (=0
) for HTTPS.
注解
The =0 syntax is currently undocumented.
Setting SSL/TLS ciphers¶
The https
option takes an optional fourth argument you can use to specify
the OpenSSL cipher suite.
[uwsgi]
master = true
shared-socket = 0.0.0.0:443
uid = www-data
gid = www-data
https = =0,foobar.crt,foobar.key,HIGH
http-to = /tmp/uwsgi.sock
This will set all of the HIGHest ciphers (whenever possible) for your SSL/TLS transactions.
Client certificate authentication¶
The https
option can also take an optional 5th argument. You can use it to
specify a CA certificate to authenticate your clients with. Generate your CA
key and certificate (this time the key will be 4096 bits and
password-protected):
openssl genrsa -des3 -out ca.key 4096
openssl req -new -x509 -days 365 -key ca.key -out ca.crt
Generate the server key and CSR (as before):
openssl genrsa -out foobar.key 2048
openssl req -new -key foobar.key -out foobar.csr
Sign the server certificate with your new CA:
openssl x509 -req -days 365 -in foobar.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out foobar.crt
Create a key and a CSR for your client, sign it with your CA and package it as PKCS#12. Repeat these steps for each client.
openssl genrsa -des3 -out client.key 2048
openssl req -new -key client.key -out client.csr
openssl x509 -req -days 365 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
openssl pkcs12 -export -in client.crt -inkey client.key -name "Client 01" -out client.p12
Then configure uWSGI for certificate client authentication
[uwsgi]
master = true
shared-socket = 0.0.0.0:443
uid = www-data
gid = www-data
https = =0,foobar.crt,foobar.key,HIGH,!ca.crt
http-to = /tmp/uwsgi.sock
注解
If you don’t want the client certificate authentication to be mandatory, remove the ‘!’ before ca.crt in the https options.
HTTP sockets¶
The http-socket <bind>
option will make uWSGI natively speak HTTP. If your
web server does not support the uwsgi protocol but is able to
speak to upstream HTTP proxies, or if you are using a service like Webfaction
or Heroku to host your application, you can use http-socket
. If you plan
to expose your app to the world with uWSGI only, use the http
option
instead, as the router/proxy/load-balancer will then be your shield.
The uWSGI HTTP/HTTPS router¶
uWSGI includes an HTTP/HTTPS router/proxy/load-balancer that can forward requests to uWSGI workers. The server can be used in two ways: embedded and standalone. In embedded mode, it will automatically spawn workers and setup the communication socket. In standalone mode you have to specify the address of a uwsgi socket to connect to.
Embedded mode:
./uwsgi --http 127.0.0.1:8080 --master --module mywsgiapp --processes 4
This will spawn a HTTP server on port 8080 that forwards requests to a pool of 4 uWSGI workers managed by the master process.
Standalone mode:
./uwsgi --master --http 127.0.0.1:8080 --http-to /tmp/uwsgi.sock
This will spawn a HTTP router (governed by a master for your safety) that will
forward requests to the uwsgi socket /tmp/uwsgi.sock
. You can bind to
multiple addresses/ports.
[uwsgi]
http = 0.0.0.0:8080
http = 192.168.173.17:8181
http = 127.0.0.1:9090
master = true
http-to = /tmp/uwsgi.sock
And load-balance to multiple nodes:
[uwsgi]
http = 0.0.0.0:8080
http = 192.168.173.17:8181
http = 127.0.0.1:9090
master = true
http-to = /tmp/uwsgi.sock
http-to = 192.168.173.1:3031
http-to = 192.168.173.2:3031
http-to = 192.168.173.3:3031
- If you want to go massive (virtualhosting and zero-conf scaling) combine the HTTP router with the uWSGI Subscription Server.
- You can make the HTTP server pass custom uwsgi variables to workers with the
http-var KEY=VALUE
option. - You can use the
http-modifier1
option to pass a custom modifier1 value to workers.
HTTPS support¶
HTTP Keep-Alive¶
If your backends set the correct HTTP headers, you can use the
http-keepalive
option. Your backends will need to set a valid
Content-Length
in each response or use chunked encoding. Simply setting
“Connection: close” is not enough. Also remember to set “Connection:
Keep-Alive” in your response. You can automate that using the add-header
"Connection: Keep-Alive"
option.
Can I use uWSGI’s HTTP capabilities in production?¶
If you need a load balancer/proxy it can be a very good idea. It will automatically find new uWSGI instances and can load balance in various ways. If you want to use it as a real webserver you should take into account that serving static files in uWSGI instances is possible, but not as good as using a dedicated full-featured web server. If you host static assets in the cloud or on a CDN, using uWSGI’s HTTP capabilities you can definitely avoid configuring a full webserver.
注解
If you use Amazon’s ELB (Elastic Load Balancer) in HTTP mode in
front of uWSGI in HTTP mode, a valid Content-Length
must be set by the
backend.
The SPDY router (uWSGI 1.9)¶
Starting from uWSGI 1.9 the HTTPS router has been extended to support version 3 of the SPDY protocol.
To run the HTTPS router with SPDY support, use the --https2
option:
uwsgi --https2 addr=0.0.0.0:8443,cert=foobart.crt,key=foobar.key,spdy=1 --module werkzeug.testapp:test_app
This will start an HTTPS router on port 8443 with SPDY support, forwarding requests to the Werkzeug’s test app the instance is running. If you’ll go to https://address:8443/ with a SPDY-enabled browser, you will see additional WSGI variables reported by Werkzeug:
SPDY
–on
SPDY.version
– protocol version (generally3
)SPDY.stream
– stream identifier (an odd number).
Opening privileged ports as a non-root user will require the use of the shared-socket option and a slightly different syntax:
uwsgi --shared-socket :443 --https2 addr==0,cert=foobart.crt,key=foobar.key,spdy=1 --module werkzeug.testapp:test_app --uid user
Both HTTP and HTTPS can be used at the same time (=0 and =1 are references to the privileged ports opened by shared-socket commands):
uwsgi --shared-socket :80 --shared-socket :443 --http =0 --https2 addr==1,cert=foobart.crt,key=foobar.key,spdy=1 --module werkzeug.testapp:test_app --uid user
Notes¶
- You need at least OpenSSL 1.x to use SPDY (all modern Linux distributions should have it).
- During uploads, the window size is constantly updated.
- The
--http-timeout
directive is used to set the SPDY timeout. This is the maximum amount of inactivity after the SPDY connection is closed. PING
requests from the browsers are all acknowledged.- On connect, the SPDY router sends a settings packet to the client with optimal values.
- If a stream fails in some catastrophic way, the whole connection is closed hard.
RST
messages are always honoured.
TODO¶
- Add old SPDY v2 support (is it worth it?)
- Allow PUSHing of resources from the uWSGI cache
- Allow tuning internal buffers
Lighttpd support¶
注解
Lighttpd support is experimental.
The uwsgi handler for Lighttpd lives in the /lighttpd
directory of the
uWSGI distribution.
Building the module¶
First download the source of lighttpd and uncompress it. Copy the
lighttpd/mod_uwsgi.c
file from the uWSGI distribution into Lighttpd’s
/src
directory. Add the following to the lighttpd src/Makefile.am
file, after the accesslog block:
lib_LTLIBRARIES += mod_uwsgi.la
mod_uwsgi_la_SOURCES = mod_uwsgi.c
mod_uwsgi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
mod_uwsgi_la_LIBADD = $(common_libadd)
Then launch
autoreconf -fi
and as usual,
./configure && make && make install
Configuring Lighttpd¶
Modify your configuration file:
server.modules = (
...
"mod_uwsgi",
...
)
# ...
uwsgi.server = (
"/pippo" => (( "host" => "192.168.173.15", "port" => 3033 )),
"/" => (( "host" => "127.0.0.1", "port" => 3031 )),
)
If you specify multiple hosts under the same virtual path/URI, load balancing will be activated with the “Fair” algorithm.
Attaching uWSGI to Mongrel2¶
Mongrel2 is a next-next-generation webserver that focuses on modern webapps.
Just like uWSGI, it is fully language agnostic, cluster-friendly and delightfully controversial :)
It uses the amazing ZeroMQ library for communication, allowing reliable, easy message queueing and configuration-free scalability.
Starting from version 0.9.8-dev, uWSGI can be used as a Mongrel2 handler.
Requirements¶
To enable ZeroMQ/Mongrel2 support in uWSGI you need the zeromq library (2.1+) and the uuid library.
Mongrel2 can use JSON or tnetstring to pass data (such as headers and various other information) to handlers. uWSGI supports tnetstring out of the box but requires the Jansson library to parse JSON data.
If you don’t install jansson or do not want to use JSON, make sure you specify protocol='tnetstring'
in the Handler in the Mongrel2 configuration, as the default is to use JSON. This would result in a rather obscure “JSON support not enabled. Skip request” message in the uWSGI log.
Configuring Mongrel2¶
You can find mongrel2-uwsgi.conf
shipped with the uWSGI source. You can use this file as a base to configure Mongrel2.
main = Server(
uuid="f400bf85-4538-4f7a-8908-67e313d515c2",
access_log="/logs/access.log",
error_log="/logs/error.log",
chroot="./",
default_host="192.168.173.11",
name="test",
pid_file="/run/mongrel2.pid",
port=6767,
hosts = [
Host(name="192.168.173.11", routes={
'/': Handler(send_spec='tcp://192.168.173.11:9999',
send_ident='54c6755b-9628-40a4-9a2d-cc82a816345e',
recv_spec='tcp://192.168.173.11:9998', recv_ident='',
protocol='tnetstring')
})
]
)
settings = {'upload.temp_store':'tmp/mongrel2.upload.XXXXXX'}
servers = [main]
It is a pretty standard Mongrel2 configuration with upload streaming enabled.
Configuring uWSGI for Mongrel2¶
To attach uWSGI to Mongrel2, simply use the OptionZeromq option:
uwsgi --zeromq tcp://192.168.173.11:9999,tcp://192.168.173.11:9998
You can spawn multiple processes (each one will subscribe to Mongrel2 with a different uuid)
uwsgi --zeromq tcp://192.168.173.11:9999,tcp://192.168.173.11:9998 -p 4
You can use threads too. Each thread will subscribe to the Mongrel2 queue but the responder socket will be shared by all the threads and protected by a mutex.
uwsgi --zeromq tcp://192.168.173.11:9999,tcp://192.168.173.11:9998 -p 4 --threads 8
# This will spawn 4 processes with 8 threads each, totaling 32 threads.
Test them all¶
Add an application to uWSGI (we will use the werkzeug.testapp as always)
uwsgi --zeromq tcp://192.168.173.11:9999,tcp://192.168.173.11:9998 -p 4 --threads 8 --module werkzeug.testapp:test_app
Now launch the command on all the servers you want, Mongrel2 will distribute requests to them automagically.
Async mode¶
警告
Async support for ZeroMQ is still under development, as ZeroMQ uses edge triggered events that complicate things in the uWSGI async architecture.
Chroot¶
By default Mongrel2 will chroot()
. This is a good thing for security, but can cause headaches regarding file upload streaming. Remember that Mongrel2 will save the uploaded file
in its own chroot jail, so if your uWSGI instance does not live in the same chroot jail, you’ll have to choose the paths carefully. In the example Mongrel2 configuration file we have used a relative path to easily allow uWSGI to reach the file.
Performance¶
Mongrel2 is extremely fast and reliable even under huge loads. tnetstring and JSON are text-based (so they are a little less effective than the binary uwsgi protocol. However, as Mongrel2 does not require the expensive one-connection-for-request method, you should get pretty much the same (if not higher) results compared to a (for example) Nginx + uWSGI approach.
uWSGI clustering + ZeroMQ¶
You can easily mix uWSGI clustering with ZeroMQ.
Choose the main node and run
uwsgi --zeromq tcp://192.168.173.11:9999,tcp://192.168.173.11:9998 -p 4 --threads 8 --module werkzeug.testapp:test_app --cluster 225.1.1.1:1717
And on all the other nodes simply run
uwsgi --cluster 225.1.1.1:1717
Mixing standard sockets with ZeroMQ¶
You can add uwsgi/HTTP/FastCGI/... sockets to your uWSGI server in addition to ZeroMQ, but if you do, remember to disable threads! This limitation will probably be fixed in the future.
Logging via ZeroMQ¶
参见
ZeroMQLogging
Nginx support¶
Nginx natively includes support for upstream servers speaking the uwsgi protocol since version 0.8.40.
If you are unfortunate enough to use an older version (that nevertheless is 0.7.63 or newer), you can find a module in the nginx directory of the uWSGI distribution.
Building the module (Nginx 0.8.39 and older)¶
Download a >=0.7.63 release of nginx and untar it at the same level of your uWSGI distribution directory.
Move yourself into the nginx-0.7.x directory and ./configure
nginx to add the uwsgi handler to its module list:
./configure --add-module=../uwsgi/nginx/
then make
and make install
it.
If all goes well you can now configure Nginx to pass requests to the uWSGI server.
Configuring Nginx¶
First of all copy the uwsgi_params
file (available in the nginx directory of the uWSGI distribution) into your Nginx configuration directory, then in a location directive in your Nginx configuration add:
uwsgi_pass unix:///tmp/uwsgi.sock;
include uwsgi_params;
– or if you are using TCP sockets,
uwsgi_pass 127.0.0.1:3031;
include uwsgi_params;
Then simply reload Nginx and you are ready to rock your uWSGI powered applications through Nginx.
What is the uwsgi_params
file?¶
It’s convenience, nothing more! For your reading pleasure, the contents of the file as of uWSGI 1.3:
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_ADDR $server_addr;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
Clustering¶
Nginx has a beautiful integrated cluster support for all the upstream handlers.
Add an upstream directive outside the server configuration block:
upstream uwsgicluster {
server unix:///tmp/uwsgi.sock;
server 192.168.1.235:3031;
server 10.0.0.17:3017;
}
Then modify your uwsgi_pass directive:
uwsgi_pass uwsgicluster;
Your requests will be balanced between the uWSGI servers configured.
Dynamic apps¶
The uWSGI server can load applications on demand when passed special vars.
uWSGI can be launched without passing it any application configuration:
./uwsgi -s /tmp/uwsgi.sock
If a request sets the UWSGI_SCRIPT
var, the server will load the specified module:
location / {
root html;
uwsgi_pass uwsgicluster;
uwsgi_param UWSGI_SCRIPT testapp;
include uwsgi_params;
}
You can even configure multiple apps per-location:
location / {
root html;
uwsgi_pass uwsgicluster;
uwsgi_param UWSGI_SCRIPT testapp;
include uwsgi_params;
}
location /django {
uwsgi_pass uwsgicluster;
include uwsgi_params;
uwsgi_param UWSGI_SCRIPT django_wsgi;
uwsgi_param SCRIPT_NAME /django;
uwsgi_modifier1 30;
}
The WSGI standard dictates that SCRIPT_NAME
is the variable used to select a specific application.
The uwsgi_modifier1 30
option sets the uWSGI modifier UWSGI_MODIFIER_MANAGE_PATH_INFO
.
This per-request modifier instructs the uWSGI server to rewrite the PATH_INFO value removing the SCRIPT_NAME from it.
Static files¶
For best performance and security, remember to configure Nginx to serve static files instead of letting your poor application handle that instead.
The uWSGI server can serve static files flawlessly but not as quickly and efficiently as a dedicated web server like Nginx.
For example you can the Django /media
path could be mapped like this:
location /media {
alias /var/lib/python-support/python2.6/django/contrib/admin/media;
}
Some applications need to pass control to the UWSGI server only if the requested filename does not exist:
if (!-f $request_filename) {
uwsgi_pass uwsgicluster;
}
WARNING
If used incorrectly a configuration like this may cause security problems. For your sanity’s sake, double-triple-quadruple check that your application files, configuration files and any other sensitive files are outside of the root of the static files.
Virtual Hosting¶
You can use Nginx’s virtual hosting without particular problems.
If you run “untrusted” web apps (such as those of your clients if you happen to be an ISP) you should limit their memory/address space usage and use a different uid for each host/application:
server {
listen 80;
server_name customersite1.com;
access_log /var/log/customersite1/access_log;
location / {
root /var/www/customersite1;
uwsgi_pass 127.0.0.1:3031;
include uwsgi_params;
}
}
server {
listen 80;
server_name customersite2.it;
access_log /var/log/customersite2/access_log;
location / {
root /var/www/customersite2;
uwsgi_pass 127.0.0.1:3032;
include uwsgi_params;
}
}
server {
listen 80;
server_name sivusto3.fi;
access_log /var/log/customersite3/access_log;
location / {
root /var/www/customersite3;
uwsgi_pass 127.0.0.1:3033;
include uwsgi_params;
}
}
The customers’ applications can now be run (using the process manager of your choice, such as rc.local, Running uWSGI via Upstart, Supervisord or whatever strikes your fancy) with a different uid and a limited (if you want) address space for each socket:
uwsgi --uid 1001 -w customer1app --limit-as 128 -p 3 -M -s 127.0.0.1:3031
uwsgi --uid 1002 -w customer2app --limit-as 128 -p 3 -M -s 127.0.0.1:3032
uwsgi --uid 1003 -w django3app --limit-as 96 -p 6 -M -s 127.0.0.1:3033
语言支持¶
Python support¶
The uwsgi Python module¶
The uWSGI server automagically adds a uwsgi
module into your Python apps.
This is useful for configuring the uWSGI server, use its internal functions and get statistics (as well as detecting whether you’re actually running under uWSGI).
注解
Many of these functions are currently woefully undocumented.
Module-level globals¶
-
uwsgi.
numproc
¶ The number of processes/workers currently running.
-
uwsgi.
buffer_size
¶ The current configured buffer size in bytes.
-
uwsgi.
started_on
(int)¶ The Unix timestamp of uWSGI’s startup.
-
uwsgi.
fastfuncs
¶ This is the dictionary used to define FastFuncs.
-
uwsgi.
applist
¶ This is the list of applications currently configured.
-
uwsgi.
applications
¶ This is the dynamic applications dictionary.
-
uwsgi.
message_manager_marshal
¶ The callable to run when the uWSGI server receives a marshalled message.
-
uwsgi.
magic_table
¶ The magic table of configuration placeholders.
-
uwsgi.
opt
¶ The current configuration options, including any custom placeholders.
Cache functions¶
-
uwsgi.
cache_get
(key[, cache_server])¶ Get a value from the cache.
参数: - key – The cache key to read.
- cache_server – The UNIX/TCP socket where the cache server is listening. Optional.
-
uwsgi.
cache_set
(key, value[, expire, cache_server])¶ Set a value in the cache.
参数: - key – The cache key to write.
- value – The cache value to write.
- expire – Expiry time of the value, in seconds.
- cache_server – The UNIX/TCP socket where the cache server is listening. Optional.
-
uwsgi.
cache_update
(key, value[, expire, cache_server])¶
-
uwsgi.
cache_del
(key[, cache_server])¶ Delete the given cached value from the cache.
参数: - key – The cache key to delete.
- cache_server – The UNIX/TCP socket where the cache server is listening. Optional.
-
uwsgi.
cache_exists
(key[, cache_server])¶ Quickly check whether there is a value in the cache associated with the given key.
参数: - key – The cache key to check.
- cache_server – The UNIX/TCP socket where the cache server is listening. Optional.
-
uwsgi.
cache_clear
()¶
Queue functions¶
-
uwsgi.
queue_get
()¶
-
uwsgi.
queue_set
()¶
-
uwsgi.
queue_last
()¶
-
uwsgi.
queue_push
()¶
-
uwsgi.
queue_pull
()¶
-
uwsgi.
queue_pop
()¶
-
uwsgi.
queue_slot
()¶
-
uwsgi.
queue_pull_slot
()¶
SNMP functions¶
-
uwsgi.
snmp_set_community
(str)¶ 参数: str – The string containing the new community value. Sets the SNMP community string.
-
uwsgi.
snmp_set_counter32
(oidnum, value)¶
-
uwsgi.
snmp_set_counter64
(oidnum, value)¶
-
uwsgi.
snmp_set_gauge
(oidnum, value)¶ 参数: - oidnum – An integer containing the oid number target.
- value – An integer containing the new value of the counter or gauge.
Sets the counter or gauge to a specific value.
-
uwsgi.
snmp_incr_counter32
(oidnum, value)¶
-
uwsgi.
snmp_incr_counter64
(oidnum, value)¶
-
uwsgi.
snmp_incr_gauge
(oidnum, value)¶
-
uwsgi.
snmp_decr_counter32
(oidnum, value)¶
-
uwsgi.
snmp_decr_counter64
(oidnum, value)¶
-
uwsgi.
snmp_decr_gauge
(oidnum, value)¶ 参数: - oidnum – An integer containing the oid number target.
- value – An integer containing the amount to increase or decrease the counter or gauge. If not specified the default is 1.
Increases or decreases the counter or gauge by a specific amount.
注解
uWSGI OID tree starts at 1.3.6.1.4.1.35156.17
Spooler functions¶
-
uwsgi.
send_to_spooler
(message_dict=None, spooler=None, priority=None, at=None, body=None, **kwargs)¶ 参数: - message_dict – The message (string keys, string values) to spool. Either this, or **kwargs may be set.
- spooler – The spooler (id or directory) to use.
- priority – The priority of the message. Larger = less important.
- at – The minimum UNIX timestamp at which this message should be processed.
- body – A binary (bytestring) body to add to the message, in addition to the message dictionary itself. Its value will be available in the key
body
in the message.
Send data to the The uWSGI Spooler. Also known as spool().
注解
Any of the keyword arguments may also be passed in the message dictionary. This means they’re reserved words, in a way...
-
uwsgi.
set_spooler_frequency
(seconds)¶ Set how often the spooler runs.
-
uwsgi.
spooler_jobs
()¶
-
uwsgi.
spooler_pid
()¶
Advanced methods¶
-
uwsgi.
send_message
()¶ Send a generic message using The uwsgi Protocol.
注解
Until version 2f970ce58543278c851ff30e52758fd6d6e69fdc this function was called
send_uwsgi_message()
.
-
uwsgi.
route
()¶
-
uwsgi.
send_multi_message
()¶ Send a generic message to multiple recipients using The uwsgi Protocol.
注解
Until version 2f970ce58543278c851ff30e52758fd6d6e69fdc this function was called
send_multi_uwsgi_message()
.参见
Clustering for examples
-
uwsgi.
reload
()¶ Gracefully reload the uWSGI server stack.
参见
Reload
-
uwsgi.
stop
()¶
-
uwsgi.
workers
() → dict¶ Get a statistics dictionary of all the workers for the current server. A dictionary is returned.
-
uwsgi.
masterpid
() → int¶ Return the process identifier (PID) of the uWSGI master process.
-
uwsgi.
total_requests
() → int¶ Returns the total number of requests managed so far by the pool of uWSGI workers.
-
uwsgi.
get_option
()¶ Also available as getoption().
-
uwsgi.
set_option
()¶ Also available as setoption().
-
uwsgi.
sorry_i_need_to_block
()¶
-
uwsgi.
request_id
()¶
-
uwsgi.
worker_id
()¶
-
uwsgi.
mule_id
()¶
-
uwsgi.
log
()¶
-
uwsgi.
log_this_request
()¶
-
uwsgi.
set_logvar
()¶
-
uwsgi.
get_logvar
()¶
-
uwsgi.
disconnect
()¶
-
uwsgi.
grunt
()¶
-
uwsgi.
lock
(locknum=0)¶ 参数: locknum – The lock number to lock. Lock 0 is always available.
-
uwsgi.
is_locked
()¶
-
uwsgi.
unlock
(locknum=0)¶ 参数: locknum – The lock number to unlock. Lock 0 is always available.
-
uwsgi.
cl
()¶
-
uwsgi.
setprocname
()¶
-
uwsgi.
listen_queue
()¶
-
uwsgi.
register_signal
(num, who, function)¶ 参数: - num – the signal number to configure
- who –
a magic string that will set which process/processes receive the signal.
worker
/worker0
will send the signal to the first available worker. This is the default if you specify an empty string.workers
will send the signal to every worker.workerN
(N > 0) will send the signal to worker N.mule
/mule0
will send the signal to the first available mule. (See uWSGI Mules)mules
will send the signal to all mulesmuleN
(N > 0) will send the signal to mule N.cluster
will send the signal to all the nodes in the cluster. Warning: not implemented.subscribed
will send the signal to all subscribed nodes. Warning: not implemented.spooler
will send the signal to the spooler.
cluster
andsubscribed
are special, as they will send the signal to the master of all cluster/subscribed nodes. The other nodes will have to define a local handler though, to avoid a terrible signal storm loop. - function – A callable that takes a single numeric argument.
-
uwsgi.
signal
(num)¶ 参数: num – the signal number to raise
-
uwsgi.
signal_wait
([signum])¶ Block the process/thread/async core until a signal is received. Use
signal_received
to get the number of the signal received. If a registered handler handles a signal,signal_wait
will be interrupted and the actual handler will handle the signal.参数: signum – Optional - the signal to wait for
-
uwsgi.
signal_registered
()¶
-
uwsgi.
signal_received
()¶ Get the number of the last signal received. Used in conjunction with
signal_wait
.
-
uwsgi.
add_file_monitor
()¶
-
uwsgi.
add_timer
(signum, seconds[, iterations=0])¶ 参数: - signum – The signal number to raise.
- seconds – The interval at which to raise the signal.
- iterations – How many times to raise the signal. 0 (the default) means infinity.
-
uwsgi.
add_probe
()¶
-
uwsgi.
add_rb_timer
(signum, seconds[, iterations=0])¶ Add an user-space (red-black tree backed) timer.
参数: - signum – The signal number to raise.
- seconds – The interval at which to raise the signal.
- iterations – How many times to raise the signal. 0 (the default) means infinity.
-
uwsgi.
add_cron
(signal, minute, hour, day, month, weekday)¶ For the time parameters, you may use the syntax
-n
to denote “every n”. For instancehour=-2
would declare the signal to be sent every other hour.参数: - signal – The signal number to raise.
- minute – The minute on which to run this event.
- hour – The hour on which to run this event.
- day – The day on which to run this event. This is “OR”ed with
weekday
. - month – The month on which to run this event.
- weekday – The weekday on which to run this event. This is “OR”ed with
day
. (In accordance with the POSIX standard, 0 is Sunday, 6 is Monday)
-
uwsgi.
register_rpc
()¶
-
uwsgi.
rpc
()¶
-
uwsgi.
rpc_list
()¶
-
uwsgi.
call
()¶
-
uwsgi.
sendfile
()¶
-
uwsgi.
set_warning_message
()¶
-
uwsgi.
mem
()¶
-
uwsgi.
has_hook
()¶
-
uwsgi.
logsize
()¶
-
uwsgi.
send_multicast_message
()¶
-
uwsgi.
cluster_nodes
()¶
-
uwsgi.
cluster_node_name
()¶
-
uwsgi.
cluster
()¶
-
uwsgi.
cluster_best_node
()¶
-
uwsgi.
connect
()¶
-
uwsgi.
connection_fd
()¶
-
uwsgi.
is_connected
()¶
-
uwsgi.
send
()¶
-
uwsgi.
recv
()¶
-
uwsgi.
recv_block
()¶
-
uwsgi.
recv_frame
()¶
-
uwsgi.
close
()¶
-
uwsgi.
i_am_the_spooler
()¶
-
uwsgi.
fcgi
()¶
-
uwsgi.
parsefile
()¶
-
uwsgi.
embedded_data
(symbol_name)¶ 参数: string – The symbol name to extract. Extracts a symbol from the uWSGI binary image.
-
uwsgi.
extract
()¶
-
uwsgi.
mule_msg
(string[, id])¶ 参数: - string – The bytestring message to send.
- id – Optional - the mule ID to receive the message. If you do not specify an ID, the message will go to the first available programmed mule.
Send a message to a mule.
-
uwsgi.
farm_msg
()¶
-
uwsgi.
mule_get_msg
()¶ 返回: A mule message, once one is received. Block until a mule message is received and return it. This can be called from multiple threads in the same programmed mule.
-
uwsgi.
farm_get_msg
()¶
-
uwsgi.
in_farm
()¶
-
uwsgi.
ready
()¶
-
uwsgi.
set_user_harakiri
()¶
Async functions¶
-
uwsgi.
async_sleep
(seconds)¶ Suspend handling the current request for
seconds
seconds and pass control to the next async core.参数: seconds – Sleep time, in seconds.
-
uwsgi.
async_connect
()¶
-
uwsgi.
async_send_message
()¶
-
uwsgi.
green_schedule
()¶
-
uwsgi.
suspend
()¶ Suspend handling the current request and pass control to the next async core clamoring for attention.
-
uwsgi.
wait_fd_read
(fd[, timeout])¶ Suspend handling the current request until there is something to be read on file descriptor
fd
. May be called several times before yielding/suspending to add more file descriptors to the set to be watched.参数: - fd – File descriptor number.
- timeout – Optional timeout (infinite if omitted).
-
uwsgi.
wait_fd_write
(fd[, timeout])¶ Suspend handling the current request until there is nothing more to be written on file descriptor
fd
. May be called several times to add more file descriptors to the set to be watched.参数: - fd – File descriptor number.
- timeout – Optional timeout (infinite if omitted).
uWSGI API - Python decorators¶
The uWSGI API is very low-level, as it must be language-independent.
That said, being too low-level is not a Good Thing for many languages, such as Python.
Decorators are, in our humble opinion, one of the more kick-ass features of Python, so in the uWSGI source tree you will find a module exporting a bunch of decorators that cover a good part of the uWSGI API.
Notes¶
Signal-based decorators execute the signal handler in the first available worker.
If you have enabled the spooler you can execute the signal handlers in it, leaving workers free to manage normal requests. Simply pass target='spooler'
to the decorator.
@timer(3, target='spooler')
def hello(signum):
print("hello")
Example: a Django session cleaner and video encoder¶
Let’s define a task.py
module and put it in the Django project directory.
from uwsgidecorators import *
from django.contrib.sessions.models import Session
import os
@cron(40, 2, -1, -1, -1)
def clear_django_session(num):
print("it's 2:40 in the morning: clearing django sessions")
Session.objects.all().delete()
@spool
def encode_video(arguments):
os.system("ffmpeg -i \"%s\" image%%d.jpg" % arguments['filename'])
The session cleaner will be executed every day at 2:40, to enqueue a video encoding we simply need to spool it from somewhere else.
from task import encode_video
def index(request):
# launching video encoding
encode_video.spool(filename=request.POST['video_filename'])
return render_to_response('enqueued.html')
Now run uWSGI with the spooler enabled:
[uwsgi]
; a couple of placeholder
django_projects_dir = /var/www/apps
my_project = foobar
; chdir to app project dir and set pythonpath
chdir = %(django_projects_dir)/%(my_project)
pythonpath = %(django_projects_dir)
; load django
module = django.core.handlers:WSGIHandler()
env = DJANGO_SETTINGS_MODULE=%(my_project).settings
; enable master
master = true
; 4 processes should be enough
processes = 4
; enable the spooler (the mytasks dir must exist!)
spooler = %(chdir)/mytasks
; load the task.py module
import = task
; bind on a tcp socket
socket = 127.0.0.1:3031
The only especially relevant option is the import
one. It works in the same way as module
but skips the WSGI callable search.
You can use it to preload modules before the loading of WSGI apps. You can specify an unlimited number of ‘’‘import’‘’ directives.
Example: web2py + spooler + timer¶
First of all define your spooler and timer functions (we will call it :file:mytasks.py
)
from uwsgidecorators import *
@spool
def a_long_task(args):
print(args)
@spool
def a_longer_task(args)
print("longer.....")
@timer(3)
def three_seconds(signum):
print("3 seconds elapsed")
@timer(10, target='spooler')
def ten_seconds_in_the_spooler(signum):
print("10 seconds elapsed in the spooler")
Now run web2py.
uwsgi --socket :3031 --spooler myspool --master --processes 4 --import mytasks --module web2py.wsgihandler
As soon as the application is loaded, you will see the 2 timers running in your logs.
Now we want to enqueue tasks from our web2py controllers.
Edit one of them and add
import mytasks # be sure mytasks is importable!
def index(): # this is a web2py action
mytasks.a_long_task.spool(foo='bar')
return "Task enqueued"
uwsgidecorators API reference¶
-
uwsgidecorators.
postfork
(func)¶ uWSGI is a preforking (or “fork-abusing”) server, so you might need to execute a fixup task after each
fork()
. Thepostfork
decorator is just the ticket. You can declare multiplepostfork
tasks. Each decorated function will be executed in sequence after eachfork()
.@postfork def reconnect_to_db(): myfoodb.connect() @postfork def hello_world(): print("Hello World")
-
uwsgidecorators.
spool
(func)¶ The uWSGI spooler can be very useful. Compared to Celery or other queues it is very “raw”. The
spool
decorator will help!@spool def a_long_long_task(arguments): print(arguments) for i in xrange(0, 10000000): time.sleep(0.1) @spool def a_longer_task(args): print(args) for i in xrange(0, 10000000): time.sleep(0.5) # enqueue the tasks a_long_long_task.spool(foo='bar',hello='world') a_longer_task.spool({'pippo':'pluto'})
The functions will automatically return
uwsgi.SPOOL_OK
so they will be executed one time independently by their return status.
-
uwsgidecorators.
spoolforever
(func)¶ Use
spoolforever
when you want to continuously execute a spool task. A@spoolforever
task will always returnuwsgi.SPOOL_RETRY
.@spoolforever def a_longer_task(args): print(args) for i in xrange(0, 10000000): time.sleep(0.5) # enqueue the task a_longer_task.spool({'pippo':'pluto'})
-
uwsgidecorators.
spoolraw
(func)¶ Advanced users may want to control the return value of a task.
@spoolraw def a_controlled_task(args): if args['foo'] == 'bar': return uwsgi.SPOOL_OK return uwsgi.SPOOL_RETRY a_controlled_task.spool(foo='bar')
-
uwsgidecorators.
rpc
("name", func)¶ uWSGI uWSGI RPC Stack is the fastest way to remotely call functions in applications hosted in uWSGI instances. You can easily define exported functions with the @rpc decorator.
@rpc('helloworld') def ciao_mondo_function(): return "Hello World"
-
uwsgidecorators.
signal
(num)(func)¶ You can register signals for the signal framework in one shot.
@signal(17) def my_signal(num): print("i am signal %d" % num)
-
uwsgidecorators.
timer
(interval, func)¶ Execute a function at regular intervals.
@timer(3) def three_seconds(num): print("3 seconds elapsed")
-
uwsgidecorators.
rbtimer
(interval, func)¶ Works like @timer but using red black timers.
-
uwsgidecorators.
cron
(min, hour, day, mon, wday, func)¶ Easily register functions for the CronInterface.
@cron(59, 3, -1, -1, -1) def execute_me_at_three_and_fiftynine(num): print("it's 3:59 in the morning")
Since 1.2, a new syntax is supported to simulate
crontab
-like intervals (every Nth minute, etc.).*/5 * * * *
can be specified in uWSGI like thus:@cron(-5, -1, -1, -1, -1) def execute_me_every_five_min(num): print("5 minutes, what a long time!")
-
uwsgidecorators.
filemon
(path, func)¶ Execute a function every time a file/directory is modified.
@filemon("/tmp") def tmp_has_been_modified(num): print("/tmp directory has been modified. Great magic is afoot")
-
uwsgidecorators.
erlang
(process_name, func)¶ Map a function as an Erlang process.
@erlang('foobar') def hello(): return "Hello"
-
uwsgidecorators.
thread
(func)¶ Mark function to be executed in a separate thread.
重要
Threading must be enabled in uWSGI with the
enable-threads
orthreads <n>
option.@thread def a_running_thread(): while True: time.sleep(2) print("i am a no-args thread") @thread def a_running_thread_with_args(who): while True: time.sleep(2) print("Hello %s (from arged-thread)" % who) a_running_thread() a_running_thread_with_args("uWSGI")
You may also combine
@thread
with@postfork
to spawn the postfork handler in a new thread in the freshly spawned worker.@postfork @thread def a_post_fork_thread(): while True: time.sleep(3) print("Hello from a thread in worker %d" % uwsgi.worker_id())
-
uwsgidecorators.
lock
(func)¶ This decorator will execute a function in fully locked environment, making it impossible for other workers or threads (or the master, if you’re foolish or brave enough) to run it simultaneously. Obviously this may be combined with @postfork.
@lock def dangerous_op(): print("Concurrency is for fools!")
-
uwsgidecorators.
mulefunc
([mulespec, ]func)¶ Offload the execution of the function to a mule. When the offloaded function is called, it will return immediately and execution is delegated to a mule.
@mulefunc def i_am_an_offloaded_function(argument1, argument2): print argument1,argument2
You may also specify a mule ID or mule farm to run the function on. Please remember to register your function with a uwsgi import configuration option.
@mulefunc(3) def on_three(): print "I'm running on mule 3." @mulefunc('old_mcdonalds_farm') def on_mcd(): print "I'm running on a mule on Old McDonalds' farm."
-
uwsgidecorators.
harakiri
(time, func)¶ Starting from uWSGI 1.3-dev, a customizable secondary harakiri subsystem has been added. You can use this decorator to kill a worker if the given call is taking too long.
@harakiri(10) def slow_function(foo, bar): for i in range(0, 10000): for y in range(0, 10000): pass # or the alternative lower level api uwsgi.set_user_harakiri(30) # you have 30 seconds. fight! slow_func() uwsgi.set_user_harakiri(0) # clear the timer, all is well
Pump support¶
注解
Pump is not a PEP nor a standard.
Pump is a new project aiming at a “better” WSGI.
An example Pump app, for your convenience:
def app(req):
return {
"status": 200,
"headers": {"content_type": "text/html"},
"body": "<h1>Hello!</h1>"
}
To load a Pump app simply use the pump
option to declare the callable.
uwsgi --http-socket :8080 -M -p 4 --pump myapp:app
myapp
is the name of the module (that must be importable!) and app is the callable. The callable part is optional – by default uWSGI will search for a callable named ‘application’.
Python Tracebacker¶
1.3-dev 新版功能.
Usually if you want to get a real-time traceback from your app you’d have to modify your code to add a hook or entry point for that as described on the TipsAndTricks page.
Starting from 1.3-dev, uWSGI includes a similar technique allowing you to get realtime traceback via a UNIX socket.
To enable the tracebacker, add the option py-tracebacker=<socket>
where <socket>
is the _basename_ for the created UNIX sockets.
If you have 4 uWSGI workers and you add py-tracebacker=/tmp/tbsocket
, four sockets named /tmp/tbsocket1
through /tmp/tbsocket4
will be created.
Connecting to one of them will return the current traceback of the threads running in the worker. To connect to those sockets you can use whatever application or method you like the best, but uWSGI includes a convenience option connect-and-read
you can use:
uwsgi --connect-and-read /tmp/tbsocket1
An example¶
Let’s write a silly test application called slow.py
:
import time
def dormi():
time.sleep(60)
def dormi2():
dormi()
def dormi3():
dormi2()
def dormi4():
dormi3()
def dormi5():
dormi4()
def application(e, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
dormi5()
return "hello"
And then run it:
uwsgi --http :8080 -w slow --master --processes 2 --threads 4 --py-tracebacker /tmp/tbsocket.
Then make a bunch of requests into it:
curl http://localhost:8080 &
curl http://localhost:8080 &
curl http://localhost:8080 &
curl http://localhost:8080 &
Now, while these requests are running (they’ll take pretty much exactly a minute to complete each), you can retrieve the traceback for, let’s say, the two first workers:
./uwsgi --connect-and-read /tmp/tbsocket.1
./uwsgi --connect-and-read /tmp/tbsocket.2
The tracebacker output will be something like this:
*** uWSGI Python tracebacker output ***
thread_id = uWSGIWorker1Core1 filename = ./slow.py lineno = 22 function = application line = dormi5()
thread_id = uWSGIWorker1Core1 filename = ./slow.py lineno = 14 function = dormi5 line = def dormi5(): dormi4()
thread_id = uWSGIWorker1Core1 filename = ./slow.py lineno = 13 function = dormi4 line = def dormi4(): dormi3()
thread_id = uWSGIWorker1Core1 filename = ./slow.py lineno = 12 function = dormi3 line = def dormi3(): dormi2()
thread_id = uWSGIWorker1Core1 filename = ./slow.py lineno = 11 function = dormi2 line = def dormi2(): dormi()
thread_id = uWSGIWorker1Core1 filename = ./slow.py lineno = 9 function = dormi line = time.sleep(60)
thread_id = uWSGIWorker1Core3 filename = ./slow.py lineno = 22 function = application line = dormi5()
thread_id = uWSGIWorker1Core3 filename = ./slow.py lineno = 14 function = dormi5 line = def dormi5(): dormi4()
thread_id = uWSGIWorker1Core3 filename = ./slow.py lineno = 13 function = dormi4 line = def dormi4(): dormi3()
thread_id = uWSGIWorker1Core3 filename = ./slow.py lineno = 12 function = dormi3 line = def dormi3(): dormi2()
thread_id = uWSGIWorker1Core3 filename = ./slow.py lineno = 11 function = dormi2 line = def dormi2(): dormi()
thread_id = uWSGIWorker1Core3 filename = ./slow.py lineno = 9 function = dormi line = time.sleep(60)
thread_id = MainThread filename = ./slow.py lineno = 22 function = application line = dormi5()
thread_id = MainThread filename = ./slow.py lineno = 14 function = dormi5 line = def dormi5(): dormi4()
thread_id = MainThread filename = ./slow.py lineno = 13 function = dormi4 line = def dormi4(): dormi3()
thread_id = MainThread filename = ./slow.py lineno = 12 function = dormi3 line = def dormi3(): dormi2()
thread_id = MainThread filename = ./slow.py lineno = 11 function = dormi2 line = def dormi2(): dormi()
thread_id = MainThread filename = ./slow.py lineno = 9 function = dormi line = time.sleep(60)
Aliasing Python modules¶
Having multiple version of a Python package/module/file is very common.
Manipulating PYTHONPATH or using virtualenvs are a way to use various versions without changing your code.
But hey, why not have an aliasing system that lets you arbitrarily map module names to files? That’s why we have the pymodule-alias
option!
Case 1 - Mapping a simple file to a virtual module¶
Let’s say we have swissknife.py
that contains lots of useful classes and functions.
It’s imported in gazillions of places in your app. Now, we’ll want to modify it, but keep the original file intact for whichever reason, and call it swissknife_mk2
.
Your options would be
- to modify all of your code to import and use swissknife_mk2 instead of swissknife. Yeah, no, not’s going to happen.
- modify the first line of all your files to read
import swissknife_mk2 as swissknife
. A lot better but you make software for money... and time is money, so why the fuck not use something more powerful?
So don’t touch your files – just remap!
./uwsgi -s :3031 -w myproject --pymodule-alias swissknife=swissknife_mk2
# Kapow! uWSGI one-two ninja punch right there!
# You can put the module wherever you like, too:
./uwsgi -s :3031 -w myproject --pymodule-alias swissknife=/mnt/floppy/KNIFEFAC/SWISSK~1.PY
# Or hey, why not use HTTP?
./uwsgi -s :3031 -w myproject --pymodule-alias swissknife=http://uwsgi.it/modules/swissknife_extreme.py
You can specify multiple pymodule-alias
directives.
uwsgi:
socket: :3031
module: myproject
pymodule-alias: funnymodule=/opt/foo/experimentalfunnymodule.py
pymodule-alias: uglymodule=/opt/foo/experimentaluglymodule.py
Case 2 - mapping a packages to directories¶
You have this shiny, beautiful Django project and something occurs to you: Would it work with Django trunk? On to set up a new virtualenv... nah. Let’s just use pymodule-alias
!
./uwsgi -s :3031 -w django_uwsgi --pymodule-alias django=django-trunk/django
Case 3 - override specific submodules¶
You have a Werkzeug project where you want to override - for whichever reason - werkzeug.test_app
with one of your own devising. Easy, of course!
./uwsgi -s :3031 -w werkzeug.testapp:test_app() --pymodule-alias werkzeug.testapp=mytestapp
参见
Python configuration options
Application dictionary¶
You can use the application dictionary mechanism to avoid setting up your application in your configuration.
import uwsgi
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
def myapp(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
yield 'Hello World\n'
uwsgi.applications = {
'': application,
'/django': 'application',
'/myapp': myapp
}
Passing this Python module name (that is, it should be importable and without the .py
extension) to uWSGI’s module
/ wsgi
option, uWSGI will search the uwsgi.applications
dictionary for the URL prefix/callable mappings.
The value of every item can be a callable, or its name as a string.
Virtualenv support¶
virtualenv is a mechanism that lets you isolate one (or more) Python applications’ libraries (and interpreters, when not using uWSGI) from each other. Virtualenvs should be used by any respectable modern Python application.
Quickstart¶
Create your virtualenv:
$ virtualenv myenv New python executable in myenv/bin/python Installing setuptools...............done. Installing pip.........done.
Install all the modules you need (using Flask as an example):
$ ./myenv/bin/pip install flask $ # Many modern Python projects ship with a `requirements.txt` file that you can use with pip like this: $ ./myenv/bin/pip install -r requirements.txt
Copy your WSGI module into this new environment (under
lib/python2.x
if you do not want to modify yourPYTHONPATH
).
注解
It’s common for many deployments that your application will live outside the virtualenv. How to configure this is not quite documented yet, but it’s probably very easy.
Run the uwsgi server using the
home
/virtualenv
option (-H
for short):$ uwsgi -H myenv -s 127.0.0.1:3031 -M -w envapp
Python 3¶
The WSGI specification was updated for Python 3 as PEP3333.
One major change is that applications are required to respond only with bytes
instances, not (Unicode) strings, back to the WSGI stack.
You should encode strings or use bytes literals:
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
yield 'Hello '.encode('utf-8')
yield b'World\n'
Paste support¶
If you are a user or developer of Paste-compatible frameworks, such as Pylons and Turbogears or applications using them, you can use the uWSGI --paste
option to conveniently deploy your application.
For example, if you have a virtualenv in /opt/tg2env
containing a Turbogears app called addressbook
configured in /opt/tg2env/addressbook/development.ini
:
uwsgi --paste config:/opt/tg2env/addressbook/development.ini --socket :3031 -H /opt/tg2env
That’s it! No additional configuration or Python modules to write.
警告
If you setup multiple process/workers (master mode) you will receive an error:
AssertionError: The EvalException middleware is not usable in a multi-process environment
in which case you’ll have to set the debug
option in your paste configuration file to False – or revert to single process environment.
Pecan support¶
If you are a user or developer of the Pecan WSGI framework, you can use the uWSGI --pecan
option to conveniently deploy your application.
For example, if you have a virtualenv in /opt/pecanenv
containing a Pecan app called addressbook
configured in /opt/pecanenv/addressbook/development.py
:
uwsgi --pecan /opt/pecanenv/addressbook/development.py --socket :3031 -H /opt/pecanenv
警告
If you setup multiple process/workers (master mode) you will receive an error:
AssertionError: The DebugMiddleware middleware is not usable in a multi-process environment
in which case you’ll have to set the debug
option in your pecan configuration file to False – or revert to single process environment.
Using the uwsgi_admin Django app¶
First of all you need to get the uwsgi_admin
app from https://github.com/unbit/uwsgi_django (once it was in the django
directory of the distribution).
It plugs into Django’s admin app, so if uwsgi_admin
is importable, just add it into your INSTALLED_APPS
.
INSTALLED_APPS = (
# ...
'django.contrib.admin',
'uwsgi_admin',
# ...
)
Then modify your urls.py
accordingly. For example:
# ...
url(r'^admin/uwsgi/', include('mysite.uwsgi_admin.urls')),
url(r'^admin/', include(admin.site.urls)),
# ...
Be sure to place the URL pattern for uwsgi_admin before the one for the admin site, or it will never match.
/admin/uwsgi/
will then serve uWSGI statistics and has a button for graceful reloading of the server (when running under a Master). Note that memory usage is reported only when the memory-report
option is enabled.
The PyPy plugin¶
Requires uWSGI >= 2.0.9
Introduction¶
Idea/Design: Maciej Fijalkowski
Contributors: Alex Gaynor, Armin Rigo
A new PyPy plugin based on cffi is available since uWSGI 1.9.11. The old slow cpyext-based one has been removed from the tree.
The plugin is currently supported only on Linux systems. Following releases will support other platforms as well.
The plugin loads libpypy-c.so
on startup, sets the home of the PyPy installation and executes a special Python module
implementing the plugin logic. So yes, most of the plugin is implemented in Python, and theoretically this approach will allow
writing uWSGI plugins directly in Python in addition to C, C++ and Objective-C.
As of December 2014 all of the required patches to PyPy have been merged, so you can get an official nightly build (or a stable version released after december 2014) and use it with uWSGI.
Install uWSGI with PyPy support¶
As always with uWSGI, you have different ways to install uWSGI based on your needs.
If you have installed pip in your PyPy home, you can run
pip install uwsgi
The uwsgi setup.py file will recognize the PyPy environment and will build a PyPy-only uWSGI binary.
In the same way, you can execute the setup.py supplied in uWSGI sources:
pypy setup.py install
(this two approaches will hardcode the pypy home in the uWSGI binary, so you will not need to set pypy-home in your options)
Or you can compile manually:
UWSGI_PROFILE=pypy make
Or you can use the network installer:
curl http://uwsgi.it/install | bash -s pypy /tmp/uwsgi
This will build a uWSGI + PyPy binary in /tmp/uwsgi
.
Or you can build PyPy support as a plugin.
uwsgi --build-plugin plugins/pypy
or (old-style)
python uwsgiconfig.py --plugin plugins/pypy
The PyPy home¶
The uWSGI Python plugin (more exactly the CPython plugin) works by linking in libpython
. That means you need to rebuild the plugin for every different version of Python. The PyPy plugin is different, as libpypy-c is loaded on startup and its symbols are resolved at runtime. This allows you to migrate to a different PyPy version on the fly.
The “downside” of this approach is that you need to inform uWSGI where your PyPy installation is at runtime (unless you installed uwsgi via pip or with the setup.py script, in such a case the home will be found automatically)
Supposing your PyPy is in /opt/pypy
you can start uWSGI with:
uwsgi --http-socket :9090 --pypy-home /opt/pypy
With this command line uWSGI will search for /opt/pypy/bin/libpypy-c.so
and if found, it will set that path as the PyPy home.
If your libpypy-c.so
is outside of the PyPy home (and in a directory not reachable by the dynamic linker), you can use the ``–pypy-lib``option.
uwsgi --http-socket :9090 --pypy-home /opt/pypy --pypy-lib /opt/libs/libpypy-c.so
With this approach you are able to use the library from a specific PyPy build and the home from another one.
注解
Remember to prefix –pypy-lib with ./ if you want to point to a .so file in your current directory!
The PyPy setup file¶
As said before, most of the uWSGI PyPy plugin is written in Python. This code is loaded at runtime, and you can also customize it.
Yes, this does mean you can change the way the plugin works without rebuilding uWSGI.
A default version of the pypy_setup.py
file is embedded in the uWSGI binary, and it is automatically loaded on startup.
If you want to change it, just pass another filename via the --pypy-setup
option.
uwsgi --http-socket :9090 --pypy-home /opt/pypy --pypy-lib /opt/libs/libpypy-c.so --pypy-setup /home/foobar/foo.py
This Python module implements uWSGI hooks and the virtual uwsgi
python module for accessing the uWSGI API from your apps.
If you want to retrieve the contents of the embedded pypy_setup.py file you can read it from the binary symbols with the print-sym
convenience option.
uwsgi --print-sym uwsgi_pypy_setup
WSGI support¶
The plugin implements PEP 333 and PEP 3333. You can load both WSGI modules and mod_wsgi
style .wsgi
files.
To load a WSGI module (it must be in your Python path):
uwsgi --http-socket :9090 --pypy-home /opt/pypy --pypy-wsgi myapp
To load a WSGI file:
uwsgi --http-socket :9090 --pypy-home /opt/pypy --pypy-wsgi-file /var/www/myapp/myapp.wsgi
RPC support¶
You can register RPC functions using the uwsgi.register_rpc()
API function, like you would with the vanilla Python plugin.
import uwsgi
def hello():
return "Hello World"
uwsgi.register_rpc('hello', hello)
To call RPC functions, both uwsgi.rpc()
and uwsgi.call()
are available.
import uwsgi
uwsgi.rpc('192.168.173.100:3031', 'myfunc', 'myarg')
uwsgi.call('myfunc', 'myarg')
uwsgi.call('myfunc@192.168.173.100:3031', 'myarg')
Integration (with local RPC) has been tested between PyPy and PyPy, PyPy and JVM, and PyPy and Lua. All of these worked flawlessly... so that means you can call Java functions from PyPy.
IPython trick¶
Having a runtime shell for making tests is very nice to have. You can use IPython for this.
uwsgi --socket :3031 --pypy-home /opt/pypy --pypy-eval "import IPython; IPython.embed()" --honour-stdin
uWSGI API status¶
The following API functions, hooks and attributes are supported as of 20130526.
uwsgi.opt
uwsgi.post_fork_hook
uwsgi.add_cron()
uwsgi.setprocname()
uwsgi.alarm()
uwsgi.signal_registered()
uwsgi.mule_id()
uwsgi.worker_id()
uwsgi.masterpid()
uwsgi.lock()
uwsgi.unlock()
uwsgi.add_file_monitor()
uwsgi.add_timer()
uwsgi.add_rb_timer()
uwsgi.cache_get()
uwsgi.cache_set()
uwsgi.cache_update()
uwsgi.cache_del()
uwsgi.signal()
uwsgi.call()
uwsgi.rpc()
uwsgi.register_rpc()
uwsgi.register_signal()
Options¶
pypy-lib
- load the specified libpypy-s.sopypy-setup
- load the specified pypy_setup script filepypy-home
- set the pypy homepypy-wsgi
- load a WSGI modulepypy-wsgi-file
- load a mod_wsgi compatible .wsgi filepypy-eval
- execute the specified string beforefork()
pypy-eval-post-fork
- execute the specified string after eachfork()
pypy-exec
- execute the specified python script beforefork()
pypy-exec-post-fork
- execute the specified python script after eachfork()
pypy-pp/pypy-python-path/pypy-pythonpath
- add the specified item to the pythonpathpypy-paste
- load a paste.deploy .ini configurationpypy-ini-paste
- load a paste.deploy .ini configuration and use its [uwsgi] section
Notes¶
- Mixing libpython with libpypy-c is explicitly forbidden. A check in the pypy plugin prevents you from doing such a hellish thing.
- The PyPy plugin is generally somewhat more “orthodox” from a Python programmer point of view, while the CPython one may be a little blasphemous in many areas. We have been able to make that choice as we do not need backward compatibility with older uWSGI releases.
- The uWSGI API is still incomplete.
- The WSGI loader does not update the uWSGI internal application list, so things like
--need-app
will not work. The server will report “dynamic mode” on startup even if the app has been successfully loaded. This will be fixed soon.
Running PHP scripts in uWSGI¶
You can safely run PHP scripts using uWSGI’s CGI support. The downside of this approach is the latency caused by the spawn of a new PHP interpreter at each request.
To get far superior performance you will want to embed the PHP interpreter in the uWSGI core and use the PHP plugin.
Building¶
A bunch of distros (such as Fedora, Red Hat and CentOS) include a php-embedded
package.
Install it, along with php-devel
and you should be able to build the php plugin:
python uwsgiconfig.py --plugin plugins/php
# You can set the path of the php-config script with UWSGICONFIG_PHPPATH.
UWSGICONFIG_PHPPATH=/opt/php53/bin/php-config python uwsgiconfig.py --plugin plugins/php
# or directly specify the directory in which you have installed your php environment
UWSGICONFIG_PHPDIR=/opt/php53 python uwsgiconfig.py --plugin plugins/php
If you get linkage problems (such as libraries not found), install those missing packages (ncurses-devel
, gmp-devel
, pcre-devel
...) but be warned that if you add development packages modifying the uWSGI core behaviour (pcre
is one of these) you _need_ to recompile the uWSGI server too, or strange problems will arise.
For distros that do not supply a libphp package (all Debian-based distros, for instance), you have to rebuild PHP with the --enable-embed
flag to ./configure
:
./configure --prefix=/usr/local --with-mysql --with-mysqli --with-pdo-mysql --with-gd --enable-mbstring --enable-embed
# That's a good starting point
Ubuntu 10.04 (newer versions include official libphp-embed sapi)¶
# Add ppa with libphp5-embed package
sudo add-apt-repository ppa:l-mierzwa/lucid-php5
# Update to use package from ppa
sudo apt-get update
# Install needed dependencies
sudo apt-get install php5-dev libphp5-embed libonig-dev libqdbm-dev
# Compile uWSGI PHP plugin
python uwsgiconfig --plugin plugins/php
Multiple PHP versions¶
Sometimes (always, if you are an ISP) you might have multiple versions of PHP installed in the system. In such a case, you will need one uWSGI plugin for each version of PHP:
UWSGICONFIG_PHPDIR=/opt/php51 python uwsgiconfig.py --plugin plugins/php default php51
UWSGICONFIG_PHPDIR=/opt/php52 python uwsgiconfig.py --plugin plugins/php default php52
UWSGICONFIG_PHPDIR=/opt/php53 python uwsgiconfig.py --plugin plugins/php default php53
‘default’ is the build profile of your server core. If you build uWSGI without a specific profile, it will be ‘default’.
You can then load a specific plugin with plugins php51
, etc. You cannot load multiple PHP versions in the same uWSGI process.
Running PHP apps with nginx¶
If you have simple apps (based on file extensions) you can use something like this:
location ~ \.php$ {
root /your_document_root;
include uwsgi_params;
uwsgi_modifier1 14;
uwsgi_pass 127.0.0.1:3030;
}
You might want to check for all of URIs containing the string .php
:
location ~ \.php {
root /your_document_root;
include uwsgi_params;
uwsgi_modifier1 14;
uwsgi_pass 127.0.0.1:3030;
}
Now simply run the uWSGI server with a bunch of processes:
uwsgi -s :3030 --plugin php -M -p 4
# Or abuse the adaptive process spawning with the --cheaper option
uwsgi -s :3030 --plugin php -M -p 40 --cheaper 4
This will allow up to 40 concurrent php requests but will try to spawn (or destroy) workers only when needed, maintaining a minimal pool of 4 processes.
Advanced configuration¶
By default, the PHP plugin will happily execute whatever script you pass to it. You may want to limit it to only a subset of extensions with the php-allowed-ext
option.
uwsgi --plugin php --master --socket :3030 --processes 4 --php-allowed-ext .php --php-allowed-ext .inc
Run PHP apps without a frontend server¶
This is an example configuration with a “public” uWSGI instance running a PHP app and serving static files. It is somewhat complex for an example, but should be a good starting point for trickier configurations.
[uwsgi]
; load the required plugins, php is loaded as the default (0) modifier
plugins = http,0:php
; bind the http router to port 80
http = :80
; leave the master running as root (to allows bind on port 80)
master = true
master-as-root = true
; drop privileges
uid = serena
gid = serena
; our working dir
project_dir = /var/www
; chdir to it (just for fun)
chdir = %(project_dir)
; check for static files in it
check-static = %(project_dir)
; ...but skip .php and .inc extensions
static-skip-ext = .php
static-skip-ext = .inc
; search for index.html when a dir is requested
static-index = index.html
; jail our php environment to project_dir
php-docroot = %(project_dir)
; ... and to the .php and .inc extensions
php-allowed-ext = .php
php-allowed-ext = .inc
; and search for index.php and index.inc if required
php-index = index.php
php-index = index.inc
; set php timezone
php-set = date.timezone=Europe/Rome
; disable uWSGI request logging
disable-logging = true
; use a max of 17 processes
processes = 17
; ...but start with only 2 and spawn the others on demand
cheaper = 2
A more extreme example that mixes CGI with PHP using internal routing and a dash of configuration logic.
[uwsgi]
; load plugins
plugins-dir = /proc/unbit/uwsgi
plugins = cgi,php,router_uwsgi
; set the docroot as a config placeholder
docroot = /accounts/unbit/www/unbit.it
; reload whenever this config file changes
; %p is the full path of the current config file
touch-reload = %p
; set process names to something meaningful
auto-procname = true
procname-prefix-spaced = [unbit.it]
; run with at least 2 processes but increase upto 8 when needed
master = true
processes = 8
cheaper = 2
; check for static files in the docroot
check-static = %(docroot)
; check for cgi scripts in the docroot
cgi = %(docroot)
logto = /proc/unbit/unbit.log
;rotate logs when filesize is higher than 20 megs
log-maxsize = 20971520
; a funny cycle using 1.1 config file logic
for = .pl .py .cgi
static-skip-ext = %(_)
static-index = index%(_)
cgi-allowed-ext = %(_)
endfor =
; map cgi modifier and helpers
; with this trick we do not need to give specific permissions to cgi scripts
cgi-helper = .pl=perl
route = \.pl$ uwsgi:,9,0
cgi-helper = .cgi=perl
route = \.cgi$ uwsgi:,9,0
cgi-helper = .py=python
route = \.py$ uwsgi:,9,0
; map php modifier as the default
route = .* uwsgi:,14,0
static-skip-ext = .php
php-allowed-ext = .php
php-allowed-ext = .inc
php-index = index.php
; show config tree on startup, just to see
; how cool is 1.1 config logic
show-config = true
uWSGI API support¶
Preliminary support for some of the uWSGI API has been added in 1.1. This is the list of supported functions:
- uwsgi_version()
- uwsgi_setprocname($name)
- uwsgi_worker_id()
- uwsgi_masterpid()
- uwsgi_signal($signum)
- uwsgi_rpc($node, $func, ...)
- uwsgi_cache_get($key)
- uwsgi_cache_set($key, $value)
- uwsgi_cache_update($key, $value)
- uwsgi_cache_del($key)
Yes, this means you can call Python functions from PHP using RPC.
from uwsgidecorators import *
# define a python function exported via uwsgi rpc api
@rpc('hello')
def hello(arg1, arg2, arg3):
return "%s-%s-%s" (arg3, arg2, arg1)
Python says the value is <? echo uwsgi_rpc("", "hello", "foo", "bar", "test"); ?>
Setting the first argument of uwsgi_rpc
to empty, will trigger local rpc.
Or you can share the uWSGI cache...
uwsgi.cache_set("foo", "bar")
<? echo uwsgi_cache_get("foo"); ?>
Sessions over uWSGI caches (uWSGI >=2.0.4)¶
Starting from uWSGI 2.0.4, you can store PHP sessions in uWSGI caches.
[uwsgi]
plugins = php
http-socket = :9090
http-socket-modifier1 = 14
; create a cache with 1000 items named 'mysessions'
cache2 = name=mysessions,items=1000
; set the 'uwsgi' session handler
php-set = session.save_handler=uwsgi
; use the 'mysessions' cache for storing sessions
php-set = session.save_path=mysessions
; or to store sessions in remote caches...
; use the 'foobar@192.168.173.22:3030' cache for storing sessions
php-set = session.save_path=foobar@192.168.173.22:3030
Zend Opcode Cache (uWSGI >= 2.0.6)¶
For some mysterious reason, the opcode cache is disabled in the embed SAPI.
You can bypass the problem by telling the PHP engine that is running under the apache SAPI (using the php-sapi-name
option):
[uwsgi]
plugins = php
php-sapi-name = apache
http-socket = :9090
http-socket-modifier1 = 14
ForkServer (uWSGI >= 2.1)¶
The Fork Server (sponsored by Intellisurvey) is one of the main features of the 2.1 branch. It allows you to inherit your vassals from specific parents instead of the Emperor.
The PHP plugin has been extended to support a fork-server so you can have a pool of php base instances from which vassals can fork(). This means you can share the opcode cache and do other tricks.
Thanks to the vassal attributes in uWSGI 2.1 we can choose from wich parent a vassal will call fork().
注解
You need Linux kernel >= 3.4 (the feature requires PR_SET_CHILD_SUBREAPER
) for “solid” use. Otherwise your Emperor will not be able to correctly wait() on children (and this will slow-down your vassal’s respawns, and could lead to various form of race conditions).
In the following example we will spawn 3 vassals, one (called base.ini) will initialize a PHP engine, while the others two will fork() from it.
[uwsgi]
; base.ini
; force the sapi name to 'apache', this will enable the opcode cache
early-php-sapi-name = apache
; load a php engine as soon as possible
early-php = true
; ... and wait for fork() requests on /run/php_fork.socket
fork-server = /run/php_fork.socket
then the 2 vassals
[emperor]
; tell the emperor the address of the fork server
fork-server = /run/php_fork.socket
[uwsgi]
; bind to port :4001
socket = 127.0.0.1:4001
; force all requests to be mapped to php
socket-modifier1 = 14
; enforce a DOCUMENT_ROOT
php-docroot = /var/www/one
; drop privileges
uid = one
gid = one
[emperor]
; tell the emperor the address of the fork server
fork-server = /run/php_fork.socket
[uwsgi]
; bind to port :4002
socket = 127.0.0.1:4002
; force all requests to be mapped to php
socket-modifier1 = 14
; enforce a DOCUMENT_ROOT
php-docroot = /var/www/two
; drop privileges
uid = two
gid = two
The two vassals are completely unrelated (even if they fork from the same parent), so you can drop privileges, have different process policies and so on.
Now spawn the Emperor:
uwsgi --emperor phpvassals/ --emperor-collect-attr fork-server --emperor-fork-server-attr fork-server
The --emperor-collect-attr
forces the Emperor to search for the ‘fork-server’ attribute in the [emperor] section of the vassal file, while --emperor-fork-server-attr
tells it to use this parameter as the address of the fork server.
Obviously if a vassal does not expose such an attribute, it will normally fork() from the Emperor.
uWSGI Perl support (PSGI)¶
PSGI is the equivalent of WSGI in the Perl world.
The PSGI plugin is officially supported and has an officially assigned uwsgi modifier, 5
. So as usual, when you’re in the business of dispatching requests to Perl apps, set the modifier1
value to 5 in your web server configuration.
Compiling the PSGI plugin¶
You can build a PSGI-only uWSGI server using the supplied buildconf/psgi.ini
file. Make sure that
the ExtUtils::Embed
module and its prerequisites are installed before building the PSGI plugin.
python uwsgiconfig --build psgi
# or compile it as a plugin...
python uwsgiconfig --plugin plugins/psgi
# and if you have not used the default configuration
# to build the uWSGI core, you have to pass
# the configuration name you used while doing that:
python uwsgiconfig --plugin plugins/psgi core
or (as always) you can use the network installer:
curl http://uwsgi.it/install | bash -s psgi /tmp/uwsgi
to have a single-file uwsgi binary with perl support in /tmp/uwsgi
Usage¶
There is only one option exported by the plugin: psgi <app>
You can simply load applications using
./uwsgi -s :3031 -M -p 4 --psgi myapp.psgi -m
# or when compiled as a plugin,
./uwsgi --plugins psgi -s :3031 -M -p 4 --psgi myapp.psgi -m
Tested PSGI frameworks/applications¶
The following frameworks/apps have been tested with uWSGI:
- MojoMojo
- Mojolicious
- Mojolicious+perlbrew+uWSGI+nginx install bundle
Multi-app support¶
You can load multiple almost-isolated apps in the same uWSGI process using the mount
option or using the UWSGI_SCRIPT
/UWSGI_FILE
request variables.
[uwsgi]
mount = app1=foo1.pl
mount = app2=foo2.psgi
mount = app3=foo3.pl
server {
server_name example1.com;
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:3031;
uwsgi_param UWSGI_APPID app1;
uwsgi_param UWSGI_SCRIPT foo1.pl;
uwsgi_modifier1 5;
}
}
server {
server_name example2.com;
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:3031;
uwsgi_param UWSGI_APPID app2;
uwsgi_param UWSGI_SCRIPT foo2.psgi;
uwsgi_modifier1 5;
}
}
server {
server_name example3.com;
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:3031;
uwsgi_param UWSGI_APPID app3;
uwsgi_param UWSGI_SCRIPT foo3.pl;
uwsgi_modifier1 5;
}
}
The auto reloader (from uWSGI 1.9.18)¶
The option –perl-auto-reload <n> allows you to instruct uWSGI to monitor every single module imported by the perl vm.
Whenever one of the module changes, the whole instance will be (gracefully) reloaded.
The monitor works by iterating over %INC after a request is served and the specified number of seconds (from the last run) is elapsed (this number of seconds is the value of the option)
This could look sub-optimal (you wil get the new content starting from from the following request) but it is the more solid (and safe) approach for the way perl works.
If you want to skip specific files from the monitoring, just add them with –perl-auto-reload-ignore
Notes¶
- Async support should work out-of-the-box.
- Threads are supported on ithreads-enabled perl builds. For each app, a new interpreter will be created for each thread. This shouldn’t be too different from a simple multi-process fork()-based subsystem.
- There are currently no known memory leaks.
Real world example, HTML::Mason¶
Install the HTML::Mason PSGI handler from CPAN and create a directory for your site.
cpan install HTML::Mason::PSGIHandler mkdir mason
Create
mason/index.html
:% my $noun = 'World'; % my $ua = $r->headers_in; % foreach my $hh (keys %{$ua}) { <% $hh %><br/> % } Hello <% $noun %>!<br/> How are ya?<br/> Request <% $r->method %> <% $r->uri %><br/>
Create the PSGI file (
mason.psgi
):use HTML::Mason::PSGIHandler; my $h = HTML::Mason::PSGIHandler->new( comp_root => "/Users/serena/uwsgi/mason", # required ); my $handler = sub { my $env = shift; $h->handle_psgi($env); };
Pay attention to
comp_root
, it must be an absolute path!Now run uWSGI:
./uwsgi -s :3031 -M -p 8 --psgi mason.psgi -m
Then go to
/index.html
with your browser.
Ruby support¶
Ruby API support¶
Status¶
The uWSGI API for Ruby is still incomplete (QueueFramework, SharedArea, custom routing and SNMP being the most missing players). The DSL will be extended as soon as the various API calls are ready.
Currently available API functions and constants (available in the UWSGI ruby module) are
- UWSGI.suspend
- UWSGI.masterpid
- UWSGI.async_sleep
- UWSGI.wait_fd_read
- UWSGI.wait_fd_write
- UWSGI.async_connect
- UWSGI.signal
- UWSGI.register_signal
- UWSGI.register_rpc
- UWSGI.signal_registered
- UWSGI.signal_wait
- UWSGI.signal_received
- UWSGI.add_cron
- UWSGI.add_timer
- UWSGI.add_rb_timer
- UWSGI.add_file_monitor
- UWSGI.cache_get
- UWSGI.cache_get!
- UWSGI.cache_exists
- UWSGI.cache_exists?
- UWSGI.cache_del
- UWSGI.cache_set
- UWSGI.cache_set
- UWSGI.cache_set!
- UWSGI.cache_update
- UWSGI.cache_update!
- UWSGI.setprocname
- UWSGI.set_warning_message
- UWSGI.lock
- UWSGI.unlock
- UWSGI.mem
- UWSGI.mule_get_msg
- UWSGI.request_id
- UWSGI.mule_id
- UWSGI.mule_msg
- UWSGI.worker_id
- UWSGI.log
- UWSGI.logsize
- UWSGI.i_am_the_spooler
- UWSGI.send_to_spooler
- UWSGI.spool
- UWSGI::OPT
- UWSGI::VERSION
- UWSGI::HOSTNAME
- UWSGI::NUMPROC
- UWSGI::PIDFILE
- UWSGI::SPOOL_OK
- UWSGI::SPOOL_RETRY
- UWSGI::SPOLL_IGNORE
uWSGI DSL¶
In parallel to the uWSGI API Python decorators, a DSL for Ruby is available, allowing elegant access to the uWSGI API.
The module is available as uwsgidsl.rb
in the source distribution. You can put this code in your config.ru
file, or use the rbrequire
option to auto-include it.
timer(n, block)¶
Execute code at regular intervals.
timer 30 do |signum|
puts "30 seconds elapsed"
end
rbtimer(n, block)¶
As timer, but using a red-black tree timer.
rbtimer 30 do |signum|
puts "30 seconds elapsed"
end
filemon(path, block)¶
Execute code at file modifications.
filemon '/tmp' do |signum|
puts "/tmp has been modified"
end
cron(hours, mins, dom, mon, dow, block)¶
Execute a task periodically using the CronInterface.
cron 20,16,-1,-1,-1 do |signum|
puts "It's time for tea."
end
signal(signum, block)¶
Register code as a signal handler for the SignalFramework.
signal 17 do |signum|
puts "Signal #{signum} was invoked."
end
rpc(name, block)¶
Register code as a uWSGI RPC Stack function.
rpc 'helloworld' do
return "Hello World"
end
rpc 'advancedhelloworld' do |x,y|
return "x = #{x}, y = #{y}"
end
mule(id?, block)¶
Execute code as a Mule brain.
mule 1 do # Run in mule 1
puts "I am the mule #{UWSGI.mule_id}"
end
mule do # Run in first available mule
puts "I am the mule #{UWSGI.mule_id}"
end
After the function returns, the mule will be brainless. To avoid this, put the code in a loop, or use muleloop
.
muleloop(id?, block)¶
Execute code in a mule in looped context.
muleloop 3 do
puts "I am the mule #{UWSGI.mule_id}"
sleep(2)
end
SpoolProc¶
A subclass of Proc
, allowing you to define a task to be executed in the Spooler.
# define the function
my_long_running_task = SpoolProc.new {|args|
puts "I am a task"
UWSGI::SPOOL_OK
}
# spool it
my_long_running_task.call({'foo' => 'bar', 'one' => 'two'})
MuleFunc¶
Call a function from any process (such as a worker), but execute in a mule
i_am_a_long_running_function = MuleFunc.new do |pippo, pluto|
puts "i am mule #{UWSGI.mule_id} #{pippo}, #{pluto}"
end
i_am_a_long_running_function.call("serena", "alessandro")
The worker calls i_am_a_long_running_function()
but the function will be execute asynchronously in the first available mule.
If you want to run the function on a specific mule, add an ID parameter. The following would only use mule #5.
i_am_a_long_running_function = MuleFunc.new 5 do |pippo,pluto|
puts "i am mule #{UWSGI.mule_id} #{pippo}, #{pluto}"
end
i_am_a_long_running_function.call("serena", "alessandro")
Real world usage¶
A simple Sinatra app printing messages every 30 seconds:
# This is config.ru
require 'rubygems'
require 'sinatra'
require 'uwsgidsl'
timer 30 do |signum|
puts "30 seconds elapsed"
end
get '/hi' do
"Hello World!"
end
run Sinatra::Application
Or you can put your code in a dedicated file (mytasks.rb
here)
require 'uwsgidsl'
timer 30 do |signum|
puts "30 seconds elapsed"
end
timer 60 do |signum|
puts "60 seconds elapsed"
end
and then load it with
uwsgi --socket :3031 --rack config.ru --rbrequire mytasks.rb --master --processes 4
Starting from version 0.9.7-dev a Ruby (Rack/Rails) plugin is officially available. The official modifier number for Ruby apps is 7, so remember to set it in your web server configuration.
The plugin can be embedded in the uWSGI core or built as a dynamically loaded plugin.
Some uWSGI standard features still aren’t supported by the plugin, such as:
- UDP request management
- SharedArea – share memory pages between uWSGI components (support on the way)
- The uWSGI queue framework
See the Ruby API support page for a list of features currently supported.
Building uWSGI for Ruby support¶
You can find rack.ini
in the buildconf
directory. This configuration will build uWSGI with a Ruby interpreter embedded. To build uWSGI with this configuration, you’ll need the Ruby headers/development package.
python uwsgiconfig.py --build rack
The resulting uWSGI binary can run Ruby apps.
A rackp.ini
build configuration also exists; this will build uWSGI with Ruby support as a plugin; in this case remember to invoke uWSGI with the plugins=rack
option.
A note regarding memory consumption¶
By default the memory management of this plugin is very aggressive (as Ruby can easily devour memory like it was going out of fashion). The Ruby garbage collector is invoked after every request by default. This may hurt your performance if your app creates lots of objects on every request. You can tune the frequency of the collection with the OptionRubyGcFreq option. As usual, there is no one-value-fits-all setting for this, so experiment a bit.
If your app leaks memory without control, consider limiting the number of requests a worker can manage before being restarted with the max-requests
option. Using limit-as
can help too.
A note regarding threads and fibers¶
Adding threading support in Ruby 1.8 is out of discussion. Thread support in this versions is practically useless in a server like uWSGI. Ruby 1.9 has a threading mode very similar to the Python one, its support is available starting from uWSGI 1.9.14 using the “rbthreads” plugin.
Fibers are a new feature of Ruby 1.9. They are an implementation of coroutines/green threads/stop resume/cooperative multithreading, or whatever you’d like to call this class of funny technologies. See FiberLoop.
Running Rack applications on uWSGI¶
This example shows you how to run a Sinatra application on uWSGI.
config.ru
require 'rubygems'
require 'sinatra'
get '/hi' do
"Hello World!"
end
run Sinatra::Application
Then invoke uWSGI (with --plugins
if you built Ruby support as a plugin):
./uwsgi -s :3031 -M -p 4 -m --post-buffering 4096 --rack config.ru
./uwsgi --plugins rack -s :3031 -M -p 4 -m --post-buffering 4096 --rack config.ru
注解
post-buffering
is required by the Rack specification.
注解
As Sinatra has a built-in logging system, you may wish to disable uWSGI’s logging of requests with the disable-logging
option.
Running Ruby on Rails applications on uWSGI¶
As writing formal documentation isn’t very interesting, here’s a couple of examples of Rails apps on uWSGI.
Running Typo¶
sudo gem install typo
typo install /tmp/mytypo
./uwsgi -s :3031 --lazy-apps --master --processes 4 --memory-report --rails /tmp/mytypo --post-buffering 4096 --env RAILS_ENV=production
–lazy-apps is vital here as typo (like a lot of apps) is not fork-friendly (it does not expect is loaded in the master and then fork() is called). With this option the app is fully loaded one-time per-worker.
Nginx configuration:
location / {
root "/tmp/mytypo/public";
include "uwsgi_params";
uwsgi_modifier1 7;
if (!-f $request_filename) {
uwsgi_pass 127.0.0.1:3031;
}
}
Running Radiant¶
sudo gem install radiant
radiant /tmp/myradiant
cd /tmp/myradiant
# (edit config/database.yml to fit)
rake production db:bootstrap
./uwsgi -s :3031 -M -p 2 -m --rails /tmp/myradiant --post-buffering 4096 --env RAILS_ENV=production
Apache configuration (with static paths mapped directly):
DocumentRoot /tmp/myradiant/public
<Directory /tmp/myradiant/public>
Allow from all
</Directory>
<Location />
uWSGISocket 127.0.0.1:3032
SetHandler uwsgi-handler
uWSGIForceScriptName /
uWSGImodifier1 7
</Location>
<Location /images>
SetHandler default-handler
</Location>
<Location /stylesheets>
SetHandler default-handler
</Location>
<Location /javascripts>
SetHandler default-handler
</Location>
Rails and SSL¶
You may wish to use the HTTPS
/ UWSGI_SCHEME https
uwsgi protocol parameters to inform the app that it is running under HTTPS.
For Nginx:
uwsgi_param HTTPS on; # Rails 2.x apps
uwsgi_param UWSGI_SCHEME https; # Rails 3.x apps
Using Lua/WSAPI with uWSGI¶
Updated for uWSGI 2.0
Building the plugin¶
The lua plugin is part of the official uWSGI distribution (official modifier 6) and it is availale in the plugins/lua directory.
The plugin support lua 5.1, lua 5.2 and luajit.
By default lua 5.1 is assumed
As always there are various ways to build and install Lua support:
from sources directory:
make lua
with the installer (the resulting binary will be in /tmp/uwsgi)
curl http://uwsgi.it/install | bash -s lua /tmp/uwsgi
or you can build it as a plugin
python uwsgiconfig.py --plugin plugins/lua
or (if you already have a uwsgi binary)
uwsgi --build-plugin plugins/lua
The build system (check uwsgiplugin.py in plugins/lua directory for more details) uses pkg-config to find headers and libraries.
You can specify the pkg-config module to use with the UWSGICONFIG_LUAPC environment variable.
As an example
UWSGICONFIG_LUAPC=lua5.2 make lua
will build a uwsgi binary for lua 5.2
as well as
UWSGICONFIG_LUAPC=luajit make lua
will build a binary with luajit
If you do not want to rely on the pkg-config tool you can manually specify the includes and library directories as well as the lib name with the following environment vars:
UWSGICONFIG_LUAINC=<directory>
UWSGICONFIG_LUALIBPATH=<directory>
UWSGICONFIG_LUALIB=<name>
Why Lua ?¶
If you came from other object oriented languages, you may find lua for web development a strange choice.
Well, you have to consider one thing when exploring Lua: it is fast, really fast and consume very few resources.
The uWSGI plugin allows you to write web applications in lua, but another purpose (if not the main one) is using Lua to extend the uWSGI server (and your application) using the signals framework, the rpc subsystem or the simple hooks engine.
If you have slow-area in your code (independently by the language used) consider rewriting them in Lua (before dealing with C) and use uWSGI to safely call them.
Your first WSAPI application¶
We will use the official WSAPI example, let’s call it pippo.lua
:
function hello(wsapi_env)
local headers = { ["Content-type"] = "text/html" }
local function hello_text()
coroutine.yield("<html><body>")
coroutine.yield("<p>Hello Wsapi!</p>")
coroutine.yield("<p>PATH_INFO: " .. wsapi_env.PATH_INFO .. "</p>")
coroutine.yield("<p>SCRIPT_NAME: " .. wsapi_env.SCRIPT_NAME .. "</p>")
coroutine.yield("</body></html>")
end
return 200, headers, coroutine.wrap(hello_text)
end
return hello
Now run uWSGI with the lua
option (remember to add --plugins lua
as the
first command line option if you are using it as a plugin)
./uwsgi --http :8080 --http-modifier1 6 --lua pippo.lua
This command line starts an http router that forward requests to a single worker in which pippo.lua is loaded.
As you can see the modifier 6 is enforced.
Obviously you can directly attach uWSGI to your frontline webserver (like nginx) and bind it to a uwsgi socket:
./uwsgi --socket 127.0.0.1:3031 --lua pippo.lua
(remember to set modifier1 to 6 in your webserver of choice)
Concurrency¶
Basically Lua is available in all of the supported uWSGI concurrency models
you can go multiprocess:
./uwsgi --socket 127.0.0.1:3031 --lua pippo.lua --processes 8 --master
or multithread:
./uwsgi --socket 127.0.0.1:3031 --lua pippo.lua --threads 8 --master
or both
./uwsgi --socket 127.0.0.1:3031 --lua pippo.lua --processes 4 --threads 8 --master
you can run it in coroutine mode (see below) using uGreen – uWSGI Green Threads as the suspend engine
./uwsgi --socket 127.0.0.1:3031 --lua pippo.lua --async 1000 --ugreen
Both threading and async modes will initialize a lua state each (you can see it as a whole independent lua VM)
Abusing coroutines¶
One of the most exciting feature of Lua are coroutines (cooperative
multithreading) support. uWSGI can benefit from this using its async engine. The
Lua plugin will initialize a lua_State
for every async core. We will use a
CPU-bound version of our pippo.lua to test it:
function hello(wsapi_env)
local headers = { ["Content-type"] = "text/html" }
local function hello_text()
coroutine.yield("<html><body>")
coroutine.yield("<p>Hello Wsapi!</p>")
coroutine.yield("<p>PATH_INFO: " .. wsapi_env.PATH_INFO .. "</p>")
coroutine.yield("<p>SCRIPT_NAME: " .. wsapi_env.SCRIPT_NAME .. "</p>")
for i=0, 10000, 1 do
coroutine.yield(i .. "<br/>")
end
coroutine.yield("</body></html>")
end
return 200, headers, coroutine.wrap(hello_text)
end
return hello
and run uWSGI with 8 async cores...
./uwsgi --socket :3031 --lua pippo.lua --async 8
And just like that, you can manage 8 concurrent requests within a single worker!
Lua coroutines do not work over C stacks (meaning you cannot manage them with your C code), but thanks to uGreen – uWSGI Green Threads (the uWSGI official coroutine/greenthread engine) you can bypass this limit.
Thanks to uGreen you can use the uWSGI async API in your Lua apps and gain a very high level of concurrency.
uwsgi.async_connect
uwsgi.wait_fd_read
uwsgi.wait_fd_write
uwsgi.is_connected
uwsgi.send
uwsgi.recv
uwsgi.close
uwsgi.ready_fd
Threading example¶
The Lua plugin is “thread-safe” as uWSGI maps a lua_State to each internal
pthread. For example you can run the Sputnik wiki engine very easily. Use
LuaRocks to install Sputnik and versium-sqlite3
. A database-backed storage
is required as the default filesystem storage does not support being accessed
by multiple interpreters concurrently. Create a wsapi compliant file:
require('sputnik')
return sputnik.wsapi_app.new{
VERSIUM_STORAGE_MODULE = "versium.sqlite3",
VERSIUM_PARAMS = {'/tmp/sputnik.db'},
SHOW_STACK_TRACE = true,
TOKEN_SALT = 'xxx',
BASE_URL = '/',
}
And run your threaded uWSGI server
./uwsgi --plugins lua --lua sputnik.ws --threads 20 --socket :3031
A note on memory¶
As we all know, uWSGI is parsimonious with memory. Memory is a precious resource. Do not trust software that does not care for your memory! The Lua garbage collector is automatically called (by default) after each request.
You can tune the frequency of the GC call with the --lua-gc-freq <n>
option, where n
is the number of requests after the GC will be called:
[uwsgi]
plugins = lua
socket = 127.0.0.1:3031
processes = 4
master = true
lua = foobar.lua
; run the gc every 10 requests
lua-gc-freq = 10
RPC and signals¶
The Lua shell¶
Using Lua as ‘configurator’¶
uWSGI api status¶
JVM in the uWSGI server (updated to 1.9)¶
The JWSGI interface¶
注解
JWSGI is not a standard. Yet. If you like JWSGI, why not send an RFC to the uWSGI mailing list. We have no specific interest in a standard, but who knows...
JWSGI is a port of the WSGI/PSGI/Rack way of thinking for Java.
If, for some obscure reason, you’d feel like developing apps with JVM languages and you don’t feel like deploying a huge servlet stack, JWSGI should be up your alley.
It is a very simple protocol: you call a public method that takes a HashMap
as its sole argument. This HashMap contains CGI style variables and
jwsgi.input
containing a Java InputStream object.
The function has to returns an array of 3 Objects:
status
(java.lang.Integer) (example: 200)headers
(HashMap) (example: {“Content-type”: “text/html”, “Server”: “uWSGI”, “Foo”: [“one”,”two”]})body
(may be a String, an array of Strings, a File or an InputStream object)
Example¶
A simple JWSGI app looks like this:
import java.util.*;
public class MyApp {
public static Object[] application(HashMap env) {
int status = 200;
HashMap<String,Object> headers = new HashMap<String,Object>();
headers.put("Content-type", "text/html");
// a response header can have multiple values
String[] servers = {"uWSGI", "Unbit"};
headers.put("Server", servers);
String body = "<h1>Hello World</h1>" + env.get("REQUEST_URI");
Object[] response = { status, headers, body };
return response;
}
}
How to use it ?¶
You need both the ‘jvm’ plugin and the ‘jwsgi’ plugin. A build profile named ‘jwsgi’, is available in the project to allow a monolithic build with jvm+jwsgi:
UWSGI_PROFILE=jwsgi make
Compile your class with
javac
.javac MyApp.java
Run uWSGI and specify the method to run (in the form class:method)
./uwsgi --socket /tmp/uwsgi.socket --plugins jvm,jwsgi --jwsgi MyApp:application --threads 40
This will run a JWSGI application on UNIX socket /tmp/uwsgi.socket with 40 threads.
Reading request body¶
The jwsgi.input
item is an uwsgi.RequestBody
object (subclass of
java/io/InputStream). You it to access the request body.
import java.util.*;
public class MyApp {
public static Object[] application(HashMap env) {
int status = 200;
HashMap<String,Object> headers = new HashMap<String,Object>();
headers.put("Content-type", "text/plain");
int body_len = Integer.parseInt((String) env.get("CONTENT_LENGTH"));
byte[] chunk = new byte[body_len];
uwsgi.RequestBody input = (uwsgi.RequestBody) env.get("jwsgi.input");
int len = input.read(chunk);
System.out.println("read " + len + " bytes");
String body = new String(chunk, 0, len);
Object[] response = { status, headers, body };
return response;
}
}
Pay attention to the use of read(byte[])
instead of the classical
read()
. The latter inefficiently reads one byte at time, while the former
reads a larger chunk at a time.
JWSGI and Groovy¶
Being low-level, the JWSGI standard can be used as-is in other languages running on the JVM. As an example this is a “Hello World” Groovy example:
static def Object[] application(java.util.HashMap env) {
def headers = ["Content-Type":"text/html", "Server":"uWSGI"]
return [200, headers, "<h1>Hello World</h1"]
}
One serving a static file:
static def Object[] application(java.util.HashMap env) {
def headers = ["Content-Type":"text/plain", "Server":"uWSGI"]
return [200, headers, new File("/etc/services")]
}
The second approach is very efficient as it will abuse uWSGI internal facilities. For example if you have offloading enabled, your worker thread will be suddenly freed. To load Groovy code, remember to compile it:
groovyc Foobar.groovy
Then run it:
./uwsgi --socket /tmp/uwsgi.socket --plugins jvm,jwsgi --jwsgi Foobar:application --threads 40
JWSGI and Scala¶
Like Groovy, you can write JWSGI apps with Scala. You only need the entry point function to use native Java objects:
object HelloWorld {
def application(env:java.util.HashMap[String, Object]): Array[Object] = {
var headers = new java.util.HashMap[String, Object]()
headers.put("Content-Type", "text/html")
headers.put("Server", "uWSGI")
return Array(200:java.lang.Integer, headers , "Hello World")
}
}
Or in a more Scala-ish way:
object HelloWorld {
def application(env:java.util.HashMap[String, Object]): Array[Object] = {
val headers = new java.util.HashMap[String, Object]() {
put("Content-Type", "text/html")
put("Server", Array("uWSGI", "Unbit"))
}
return Array(200:java.lang.Integer, headers , "Hello World")
}
}
Once compiled with scalac <filename>
you run like this:
./uwsgi --socket /tmp/uwsgi.socket --plugins jvm,jwsgi --jwsgi HelloWorld:application --threads 40
The Clojure/Ring JVM request handler¶
Thanks to the JVM in the uWSGI server (updated to 1.9) plugin available from 1.9, Clojure web apps can be run on uWSGI.
The supported gateway standard is Ring, https://github.com/ring-clojure/ring . Its full specification is available here: https://github.com/ring-clojure/ring/blob/master/SPEC
A uWSGI build profile named “ring” is available for generating a monolithic build with both the JVM and Ring plugins.
From the uWSGI sources:
UWSGI_PROFILE=ring make
The build system will try to detect your JDK installation based on various presets (for example on CentOS you can yum install
java-1.6.0-openjdk.x86_64-devel
or java-1.7.0-openjdk-devel.x86_64
or on Debian/Ubuntu openjdk-6-jdk
and so on...).
OSX/Xcode default paths are searched too.
After a successful build you will have the uwsgi binary and a uwsgi.jar file that you should copy in your CLASSPATH (or just remember to set it in the uwsgi configuration every time).
参见
For more information on the JVM plugin check JVM in the uWSGI server (updated to 1.9)
Our first Ring app¶
A basic Clojure/Ring app could be the following (save it as myapp.clj):
(ns myapp)
(defn handler [req]
{:status 200
:headers { "Content-Type" "text/plain" , "Server" "uWSGI" }
:body (str "<h1>The requested uri is " (get req :uri) "</h1>")
}
)
The code defines a new namespace called ‘myapp’, in which the ‘handler’ function is the Ring entry point (the function called at each web request)
We can now build a configuration serving that app on the HTTP router on port 9090 (call it config.ini):
[uwsgi]
http = :9090
http-modifier1 = 8
http-modifier2 = 1
jvm-classpath = plugins/jvm/uwsgi.jar
jvm-classpath = ../.lein/self-installs/leiningen-2.0.0-standalone.jar
clojure-load = myapp.clj
ring-app = myapp:handler
Run uWSGI:
./uwsgi config.ini
Now connect to port 9090 and you should see the app response.
As you can note we have manually added uwsgi.jar and the Leiningen standalone jar (it includes the whole Clojure distribution) to our classpath.
Obviously if you do not want to use Leiningen, just add the Clojure jar to your classpath.
The clojure-load
option loads a Clojure script in the JVM (very similar to what jvm-class
do with the basic jvm plugin).
The ring-app
option specify the class/namespace in which to search for the ring function entry point.
In our case the function is in the ‘myapp’ namespace and it is called ‘handler’ (you can understand that the syntax is namespace:function)
Pay attention to the modifier configuration. The JVM plugin registers itself as 8, while Ring registers itself as modifier 2 #1, yielding an effective configuration of “modifier1 8, modifier2 1”.
Using Leiningen¶
Leiningen is a great tool for managing Clojure projects. If you use Clojure, you are very probably a Leiningen user.
One of the great advantages of Leiningen is the easy generation of a single JAR distribution. That means you can deploy a whole app with a single file.
Let’s create a new “helloworld” Ring application with the lein
command.
lein new helloworld
Move it to the just created ‘helloworld’ directory and edit the project.clj file
(defproject helloworld "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.Clojure/Clojure "1.4.0"]])
We want to add the ring-core
package to our dependencies (it contains a set of classes/modules to simplify the writing of ring apps) and obviously we need to change the description and URL:
(defproject helloworld "0.1.0-SNAPSHOT"
:description "My second uWSGI ring app"
:url "https://uwsgi-docs.readthedocs.org/en/latest/Ring.html"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.Clojure/Clojure "1.4.0"] [ring/ring-core "1.2.0-beta1"]])
Now save it and run...
lein repl
This will install all of the jars we need and move us to the Clojure console (just exit from it for now).
Now we want to write our Ring app, just edit the file src/helloworld/core.clj and place the following content in it:
(ns helloworld.core
(:use ring.util.response))
(defn handler [request]
(-> (response "Hello World")
(content-type "text/plain")))
Then re-edit project.clj to instruct Leiningen on which namespaces to build:
(defproject helloworld "0.1.0-SNAPSHOT"
:description "FIXME: write description"
:url "http://example.com/FIXME"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:aot [helloworld.core]
:dependencies [[org.Clojure/Clojure "1.4.0"] [ring/ring-core "1.2.0-beta1"]])
As you can see we have added helloworld.core in the :aot
keyword.
Now let’s compile our code:
lein compile
And build the full jar (the uberjar):
lein uberjar
If all goes well you should see a message like this at the end of the procedure:
Created /home/unbit/helloworld/target/helloworld-0.1.0-SNAPSHOT-standalone.jar
Take a note of the path so we can configure uWSGI to run our application.
[uwsgi]
http = :9090
http-modifier1 = 8
http-modifier2 = 1
jvm-classpath = plugins/jvm/uwsgi.jar
jvm-classpath = /home/unbit/helloworld/target/helloworld-0.1.0-SNAPSHOT-standalone.jar
jvm-class = helloworld/core__init
ring-app = helloworld.core:handler
This time we do not load Clojure code, but directly a JVM class.
Pay attention: when you specify a JVM class you have to use the ‘/’ form, not the usual dotted form.
The __init suffix is automatically added by the Clojure system when your app is compiled.
The ring-app
set the entry point to the helloworld.core namespace and the function ‘handler’.
We can access that namespace as we have loaded it with jvm-class
Concurrency¶
As all of the JVM plugin request handlers, multi-threading is the best way to achieve concurrency.
Threads in the JVM are really solid, do not be afraid to use them (even if you can spawn multiple processes too)
[uwsgi]
http = :9090
http-modifier1 = 8
http-modifier2 = 1
jvm-classpath = plugins/jvm/uwsgi.jar
jvm-classpath = /home/unbit/helloworld/target/helloworld-0.1.0-SNAPSHOT-standalone.jar
jvm-class = helloworld/core__init
ring-app = helloworld.core:handler
master = true
processes = 4
threads = 8
This setup will spawn 4 uWSGI processes (workers) with 8 threads each (for a total of 32 threads).
Accessing the uWSGI api¶
Clojure can call native Java classes too, so it is able to access the uWSGI API exposed by the JVM plugin.
The following example shows how to call a function (written in python) via Clojure:
(ns myapp
(import uwsgi)
)
(defn handler [req]
{:status 200
:headers { "Content-Type" "text/html" , "Server" "uWSGI" }
:body (str "<h1>The requested uri is " (get req :uri) "</h1>" "<h2>reverse is " (uwsgi/rpc (into-array ["" "reverse" (get req :uri)])) "</h2>" )
}
)
The “reverse” function has been registered from a Python module:
from uwsgidecorators import *
@rpc('reverse')
def contrario(arg):
return arg[::-1]
This is the used configuration:
[uwsgi]
http = :9090
http-modifier1 = 8
http-modifier2 = 1
jvm-classpath = plugins/jvm/uwsgi.jar
jvm-classpath = /usr/share/java/Clojure-1.4.jar
Clojure-load = myapp.clj
plugin = python
import = pyrpc.py
ring-app = myapp:handler
master = true
Another useful feature is accessing the uwsgi cache. Remember that cache keys are string while values are bytes.
The uWSGI Ring implementation supports byte array in addition to string for the response. This is obviously a violation of the standard but avoids you to re-encode bytes every time (but obviously you are free to do it if you like).
Notes and status¶
- A shortcut option allowing to load compiled code and specifying the ring app would be cool.
- As with the The JWSGI interface handler, all of the uWSGI performance features are automatically used (like when sending static files or buffering input)
- The plugin has been developed with the cooperation and ideas of Mingli Yuan. Thanks!
Introduction¶
As of uWSGI 1.9, you can have a full, thread-safe and versatile JVM embedded in the core. All of the plugins can call JVM functions (written in Java, JRuby, Jython, Clojure, whatever new fancy language the JVM can run) via the RPC subsystem or using uWSGI The uWSGI Signal Framework The JVM plugin itself can implement request handlers to host JVM-based web applications. Currently The JWSGI interface and The Clojure/Ring JVM request handler (Clojure) apps are supported. A long-term goal is supporting servlets, but it will require heavy sponsorship and funding (feel free to ask for more information about the project at info@unbit.it).
Building the JVM support¶
First of all, be sure to have a full JDK distribution installed. The uWSGI build system will try to detect common JDK setups (Debian, Ubuntu, Centos, OSX...), but if it is not able to find a JDK installation it will need some information from the user (see below). To build the JVM plugin simply run:
python uwsgiconfig.py --plugin plugins/jvm default
Change ‘default’, if needed, to your alternative build profile. For example if you have a Perl/PSGI monolithic build just run
python uwsgiconfig.py --plugin plugins/jvm psgi
or for a fully-modular build
python uwsgiconfig.py --plugin plugins/jvm core
If all goes well the jvm_plugin will be built. If the build system cannot find a JDK installation you will ned to specify the path of the headers directory (the directory containing the jni.h file) and the lib directory (the directory containing libjvm.so). As an example, if jni.h is in /opt/java/includes and libjvm.so is in /opt/java/lib/jvm/i386, run the build system in that way:
UWSGICONFIG_JVM_INCPATH=/opt/java/includes UWSGICONFIG_JVM_LIBPATH=/opt/java/lib/jvm/i386 python uwsgiconfig --plugin plugins/jvm
After a successful build, you will get the path of the uwsgi.jar file. That jarball containes classes to access the uWSGI API, and you should copy it into your CLASSPATH or at the very least manually load it from uWSGI’s configuration.
Exposing functions via the RPC subsystem¶
In this example we will export a “hello” Java function (returning a string) and we will call it from a Python WSGI application. This is our base configuration (we assume a modular build).
[uwsgi]
plugins = python,jvm
http = :9090
wsgi-file = myapp.py
jvm-classpath = /opt/uwsgi/lib/uwsgi.jar
The jvm-classpath
is an option exported by the JVM plugin that allows you
to add directories or jarfiles to your classpath. You can specify as many
jvm-classpath
options you need. Here we are manually adding uwsgi.jar
as we did not copy it into our CLASSPATH. This is our WSGI example script.
import uwsgi
def application(environ, start_response):
start_response('200 OK', [('Content-Type','text/html')])
yield "<h1>"
yield uwsgi.call('hello')
yield "</h1>"
Here we use uwsgi.call()
instead of uwsgi.rpc()
as a shortcut (little
performance gain in options parsing). We now create our Foobar.java class. Its
static void main()
function will be run by uWSGI on startup.
public class Foobar {
static void main() {
// create an anonymous function
uwsgi.RpcFunction rpc_func = new uwsgi.RpcFunction() {
public String function(String... args) {
return "Hello World";
}
};
// register it in the uWSGI RPC subsystem
uwsgi.register_rpc("hello", rpc_func);
}
}
The uwsgi.RpcFunction
interface allows you to easily write uWSGI-compliant
RPC functions. Now compile the Foobar.java file:
javac Foobar.java
(eventually fix the classpath or pass the uwsgi.jar path with the -cp option) You now have a Foobar.class that can be loaded by uWSGI. Let’s complete the configuration...
[uwsgi]
plugins = python,jvm
http = :9090
wsgi-file = myapp.py
jvm-classpath = /opt/uwsgi/lib/uwsgi.jar
jvm-main-class = Foobar
The last option (jvm-main-class
) will load a java class and will execute
its main()
method. We can now visit localhost:9090 and we should see the
Hello World message.
Registering signal handlers¶
In the same way as the RPC subsystem you can register signal handlers. You will be able to call Java functions on time events, file modifications, cron... Our Sigbar.java:
public class Sigbar {
static void main() {
// create an anonymous function
uwsgi.SignalHandler sh = new uwsgi.SignalHandler() {
public void function(int signum) {
System.out.println("Hi, i am the signal " + signum);
}
};
// register it in the uWSGI signal subsystem
uwsgi.register_signal(17, "", sh);
}
}
uwsgi.SignalHandler
is the interface for signal handlers.
Whenever signal 17 is rased, the corresponding JVM function will be run. Remember to compile the file, load it in uWSGI and to enable to master process (without it the signal subsystem will not work).
The fork() problem and multithreading¶
The JVM is not fork()
friendly. If you load a virtual machine in the master
and then you fork() (like generally you do in other languages) the children JVM
will be broken (this is mainly because threads required by the JVM are not
inherited). For that reason a JVM for each worker, mule and spooler is
spawned. Fortunately enough, differently from the vast majority of other
platforms, the JVM has truly powerful multithreading support. uWSGI supports
it, so if you want to run one of the request handlers (JWSGI, Clojure/Ring)
just remember to spawn a number of threads with the --threads
option.
How does it work?¶
uWSGI embeds the JVM using the JNI interface. Unfortunately we cannot rely on JVM’s automatic garbage collector, so we have to manually unreference all of the allocated objects. This is not a problem from a performance and usage point of view, but makes the development of plugins a bit more difficult compared to other JNI-based products. Fortunately the current API simplifies that task.
Passing options to the JVM¶
You can pass specific options to the JVM using the --jvm-opt
option.
For example to limit heap usage to 10 megabytes:
[uwsgi]
...
jvm-opt = -Xmx10m
Loading classes (without main method)¶
We have already seen how to load classes and run their main()
method on
startup. Often you will want to load classes only to add them to the JVM
(allowing access to external modules needing them) To load a class you can use
--jvm-class
.
[uwsgi]
...
jvm-class = Foobar
jvm-class = org/unbit/Unbit
Remember class names must use the ‘/’ format instead of dots! This rule applies
to --jvm-main-class
too.
Request handlers¶
Although the Java(TM) world has its J2EE environment for deploying web applications, you may want to follow a different approach. The uWSGI project implements lot of features that are not part of J2EE (and does not implement lot of features that are a strong part of J2EE), so you may find its approach more suited for your setup (or taste, or skills).
The JVM plugin exports an API to allow hooking web requests. This approach differs a bit from “classic” way uWSGI works. The JVM plugin registers itself as a handler for modifier1==8, but will look at the modifier2 value to know which of its request handlers has to manage it. For example the The Clojure/Ring JVM request handler plugin registers itself in the JVM plugin as the modifier2 number ‘1’. So to pass requests to it you need something like that:
[uwsgi]
http = :9090
http-modifier1 = 8
http-modifier2 = 1
or with nginx:
location / {
include uwsgi_params;
uwsgi_modifier1 8;
uwsgi_modifier2 1;
uwsgi_pass /tmp/uwsgi.socket;
}
Currently there are 2 JVM request handlers available:
- The JWSGI interface
- The Clojure/Ring JVM request handler (for Clojure)
As already said, the idea of developing a servlet request handler is there, but it will require a sponsorship (aka. money) as it’ll be a really big effort.
Notes¶
- You do not need special jar files to use UNIX sockets – the JVM plugin has access to all of the uWSGI features.
- You may be addicted to the log4j module. There is nothing wrong with it, but do take a look at uWSGI’s logging capabilities (less resources needed, less configuration, and more NoEnterprise)
- The uWSGI API access is still incomplete (will be updated after 1.9)
- The JVM does not play well in environments with limited address space. Avoid
using
--limit-as
if you load the JVM in your instances.
The Mono ASP.NET plugin¶
uWSGI 1.9 added support for the Mono platform, especially for the ASP.NET infrastructure.
The most common way to deploy Mono ASP.NET applications is with the XSP project, a simple web server gateway implementing HTTP and FastCGI protocols.
With the Mono plugin you will be able to host ASP.net applications directly in uWSGI, gaining all of its features in your application for free.
As all of the other uWSGI plugin you can call functions exported from the other languages using the uWSGI RPC Stack subsystem.
Building uWSGI + Mono¶
You can build Mono support as a plugin or in a monolithic build.
A build profile named “mono” is available, making the task pretty simple.
Be sure to have mono installed in your system. You need the Mono headers, the mcs
compiler and the System.Web assembly. They are available in standard mono distributions.
On recent Debian/Ubuntu systems you can use
apt-get install build-essential python mono-xsp4 asp.net-examples
mono-xsp4
is a trick to install all we need in a single shot, as ASP.net examples will be used for testing our setup.
We can build a monolithic uWSGI distribution with Mono embedded:
UWSGI_PROFILE=mono make
At the end of the procedure (if all goes well) you will get the path to the uwsgi.dll
assembly.
You may want to install it in your GAC (with gacutil -i <path>) to avoid specifying its path every time. This library allows access to the uWSGI api from Mono applications.
Starting the server¶
The Mono plugin has an official modifier1
, 15.
[uwsgi]
http = :9090
http-modifier1 = 15
mono-app = /usr/share/asp.net-demos
mono-index = index.asp
The previous setup assumes uwsgi.dll has been installed in the GAC, if it is not your case you can force its path with:
[uwsgi]
http = :9090
http-modifier1 = 15
mono-app = /usr/share/asp.net-demos
mono-index = index.asp
mono-assembly = /usr/lib/uwsgi/uwsgi.dll
/usr/share/asp.net-demos
is the directory containing Mono’s example ASP.net applications.
If starting uWSGI you get an error about not being able to find uwsgi.dll, you can enforce a specific search path with
[uwsgi]
http = :9090
http-modifier1 = 15
mono-app = /usr/share/asp.net-demos
mono-index = index.asp
mono-assembly = /usr/lib/uwsgi/uwsgi.dll
env = MONO_PATH=/usr/lib/uwsgi/
Or you can simply copy uwsgi.dll into the /bin
directory of your site directory (/usr/share/asp.net-demos
in this case).
The mono-index
option is used to set the file to search when a directory is requested. You can specify it multiple times.
Under the hood: the mono key¶
The previous example should have worked flawlessly, but internally lot of assumptions have been made.
The whole mono plugin relies on the “key” concept. A key is a unique identifier for a .net application. In the example case the key for the application is “/usr/share/asp.net-demos”. This is a case where the key maps 1:1 with the virtualhost map. To map a virtualhost path to a specific key you can use the form
[uwsgi]
http = :9090
http-modifier1 = 15
mono-app = /foobar=/usr/share/asp.net-demos
now the /foobar key maps to the /usr/share/asp.net-demos .net app.
By default the requested key is mapped to the DOCUMENT_ROOT variable. So in this new case /foobar should be the DOCUMENT_ROOT value.
But the uWSGI http router has no concept of DOCUMENT_ROOT so how the previous example could work ? This is because the first loaded app is generally the default one, so the mono plugin, being not able to find an app returned the default one.
Using DOCUMENT_ROOT as the key could be quite limiting. So the –mono-key option is available. Let’s build a massive virtualhosting stack using uWSGI internal routing
[uwsgi]
http = :9090
http-modifier1 = 15
mono-key = MONO_APP
route-run = addvar:MONO_APP=/var/www/asp/${HTTP_HOST}
MONO_APP is not the variable the mono plugin will search for applications (instead of DOCUMENT_ROOT).
Thanks to internal routing we set it (dynamically) to the path of host-specific application root, so a request to example.com will map to /var/www/asp/example.com
Concurrency and fork() unfriendliness¶
As the Mono VM is not fork()
friendly, a new VM is spawned for each worker. This ensures you can run your application in multiprocessing mode.
Mono has really solid multithreading support and it works great with uWSGI’s thread support.
[uwsgi]
http = :9090
http-modifier1 = 15
mono-app = /usr/share/asp.net-demos
mono-index = index.asp
mono-assembly = /usr/lib/uwsgi/uwsgi.dll
env = MONO_PATH=/usr/lib/uwsgi/
master = true
processes = 4
threads = 20
With this setup you will spawn 4 processes each with 20 threads. Try to not rely on a single process. Albeit it is a common setup in the so-called “Enterprise environments”, having multiple processes ensures you greater availability (thanks to the master work). This rule (as an example) applies even to the JVM in the uWSGI server (updated to 1.9) plugin.
API access¶
This is a work in progress. Currently only a couple of functions are exported. High precedence will be given to the uWSGI RPC Stack and Signal subsystem and to the The uWSGI caching framework framework.
Tricks¶
As always uWSGI tries to optimize (where possible) the “common” operations of your applications. Serving static files is automatically accelerated (or offloaded if offloading is enabled) and all of the path resolutions are cached.
Running CGI scripts on uWSGI¶
The CGI plugin provides the ability to run CGI scripts using the uWSGI server.
Web servers/clients/load balancers send requests to the uWSGI server using modifier 9
. uWSGI then uses the variables passed from the client as CGI variables (on occasion fixing them) and calls the corresponding script/executable, re-forwarding its output to the client.
The plugin tries to resemble Apache’s behavior, allowing you to run CGI scripts even on webservers that do not support CGI natively, such as Nginx.
Enabling the plugin¶
The CGI plugin is by default not built in to the core. You need to build a binary with cgi embedded or build the cgi plugin.
To build a single binary with CGI support:
curl http://uwsgi.it/install | bash -s cgi /tmp/uwsgi
To compile it as a plugin,
python uwsgiconfig.py --plugin plugins/cgi
or, from sources directory:
make PROFILE=cgi
Configuring CGI mode¶
The cgi <[mountpoint=]path>
option is the main entry point for configuring your CGI environment.
path
may be a directory or an executable file.
In the case of a directory, the CGI plugin will use the URI to find the path of the script. If an executable is passed, it will be run, with SCRIPT_NAME
, SCRIPT_FILENAME
and PATH_INFO
set in its environment.
The mountpoint
is optional. You can use it to map different URIs to different CGI directories/scripts.
Notes¶
- Remember to use uWSGI’s resource limiting and jailing techniques (namespaces, chroot, capability, unshare....) with your CGI apps to limit the damage they might cause.
- Asynchronous mode is not at all supported with CGI applications. Each CGI application will block the worker running it.
- If not mapped to a helper, each CGI script must have read and execution permissions.
Examples¶
Example 1: Dumb CGI-enabled directory¶
[uwsgi]
plugins = cgi
socket = uwsgi.sock
cgi = /var/www/cgi-bin
Each request will search for the specified file in /var/www/cgi-bin
and execute it.
A request to http://example.com/foo.cgi
would run /var/www/cgi-bin/foo.cgi
.
Example 2: old-style cgi-bin directory¶
[uwsgi]
plugins = cgi
socket = uwsgi.sock
cgi = /cgi-bin=/var/lib/cgi-bin
A call to http://example.com/cgi-bin/foo
will run /var/lib/cgi-bin/foo
.
Example 3: restricting usage to certain extensions¶
We want only .cgi and .pl files to be executed:
[uwsgi]
plugins = cgi
socket = uwsgi.sock
cgi = /cgi-bin=/var/lib/cgi-bin
cgi-allowed-ext = .cgi
cgi-allowed-ext = .pl
Example 4: mapping scripts to interpreters using their extension¶
We want to run files ending with .php
in the directory /var/www
via the php5-cgi
binary:
[uwsgi]
plugins = cgi
socket = uwsgi.sock
cgi = /var/www
cgi-allowed-ext = .php
cgi-helper = .php=php5-cgi
If a file is run with an helper, the file to be run will not require the execute permission bit. The helper of course does.
Extension comparison is not case sensitive.
Example 5: running PHP scripts as CGI via Nginx¶
Configure Nginx to pass .php requests to uWSGI, with /var/www/foo
as the document root.
location ~ .php$ {
include uwsgi_params;
uwsgi_param REDIRECT_STATUS 200; # required by php 5.3
uwsgi_modifier1 9;
uwsgi_pass 127.0.0.1:3031;
}
And configure uWSGI like this:
[uwsgi]
plugins = cgi
socket = 127.0.0.1:3031
cgi = /var/www/foo
cgi-allowed-ext = .php
cgi-helper = .php=php5-cgi
Example 6: Concurrency¶
By default each uWSGI worker will be able to run a single CGI script. This mean that using one process, will block your incoming requests until the first request has been ended.
Adding more workers will mitigate the problem, but will consume a lot of memory.
Threads are a better choice. Let’s configure each worker process to run 20 worker threads and thus run 20 CGI scripts concurrently.
[uwsgi]
plugins = cgi
threads = 20
socket = 127.0.0.1:3031
cgi = /var/www/foo
cgi-allowed-ext = .php
cgi-helper = .php=php5-cgi
Starting from uWSGI 2.0.2 you can have even more cheap concurrency thanks to async mode support:
[uwsgi]
plugins = cgi
async = 200
ugreen = true
socket = 127.0.0.1:3031
cgi = /var/www/foo
cgi-allowed-ext = .php
cgi-helper = .php=php5-cgi
this will spawn 200 coroutines, each able to manage a CGI script (with few K of memory)
Example 7: Mailman web interface behind Nginx¶
location /cgi-bin/mailman {
include uwsgi_params;
uwsgi_modifier1 9;
uwsgi_pass 127.0.0.1:3031;
}
[uwsgi]
plugins = cgi
threads = 20
socket = 127.0.0.1:3031
cgi = /cgi-bin/mailman=/usr/lib/cgi-bin/mailman
cgi-index = listinfo
The cgi-index
directive specifies which script is run when a path ending with a slash is requested. This way /cgi-bin/mailman/
will be mapped to the /cgi-bin/mailman/listinfo
script.
Example 8: Viewvc as CGI in a subdir¶
Using the Mountpoint option.
[uwsgi]
plugins = cgi
threads = 20
socket = 127.0.0.1:3031
cgi = /viewvc=/usr/lib/cgi-bin/viewvc.cgi
Example 9: using the uWSGI HTTP router and the check-static
option¶
This is pretty much a full-stack solution using only uWSGI running on port 8080.
[uwsgi]
plugins = http, cgi
; bind on port 8080 and use the modifier 9
http = :8080
http-modifier1 = 9
; set the document_root as a placeholder
my_document_root = /var/www
; serve static files, skipping .pl and .cgi files
check-static = %(my_document_root)
static-skip-ext = .pl
static-skip-ext = .cgi
; run cgi (ending in .pl or .cgi) in the document_root
cgi = %(my_document_root)
cgi-index = index.pl
cgi-index = index.cgi
cgi-allowed-ext = .pl
cgi-allowed-ext = .cgi
Example 10: optimizing CGIs (advanced)¶
You can avoid the overhead of re-running interpreters at each request, loading the interpreter(s) on startup and calling a function in them instead of execve()
ing the interpreter itself.
The contrib/cgi_python.c
file in the source distribution is a tiny example on how to optimize Python CGI scripts.
The Python interpreter is loaded on startup, and after each fork()
, uwsgi_cgi_run_python
is called.
To compile the library you can use something like this:
gcc -shared -o cgi_python.so -fPIC -I /usr/include/python2.7/ cgi_python.c -lpython2.7
And then map .py
files to the uwsgi_cgi_run_python
function.
[uwsgi]
plugins = cgi
cgi = /var/www
cgi-loadlib = ./cgi_python.so:uwsgi_cgi_load_python
cgi-helper = .py=sym://uwsgi_cgi_run_python
}}}
Remember to prefix the symbol in the helper with sym://
to enable uWSGI to find it as a loaded symbol instead of a disk file.
The GCCGO plugin¶
uWSGI 1.9.20 officially substituted the old uWSGI Go support (1.4 only) plugin with a new one based on GCCGO.
The usage of GCCGO allows more features and better integration with the uWSGI deployment styles.
GCC suite >= 4.8 is expected (and strongly suggested).
How it works¶
When the plugin is enabled, a new go runtime is initialized after each fork()
.
If a main
Go function is available in the process address space it will be executed in the Go runtime, otherwise the control goes back to the uWSGI loop engine.
Why not use plain Go?¶
Unfortunately the standard Go runtime is currently not embeddable and does not support compiling code as shared libraries.
Both are requisite for meaningful uWSGI integration.
Starting from GCC 4.8.2, its libgo
has been improved a lot and building shared libraries as well as initializing the Go runtime works like a charm (even if it required a bit of... not very elegant hacks).
Building the plugin¶
A build profile is available allowing you to build a uWSGI+gccgo binary ready to load Go shared libraries:
make gccgo
The first app¶
You do not need to change the way you write webapps in Go. The net/http
package can be used flawlessly:
package main
import "uwsgi"
import "net/http"
import "fmt"
func viewHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Hello World</h1>")
}
func main() {
http.HandleFunc("/view/", viewHandler)
uwsgi.Run()
}
The only difference is in calling uwsgi.Run()
instead of initializing the Go HTTP server.
To build the code as shared library simply run:
gcc -fPIC -shared -o myapp.so myapp.go
If you get an error about gcc not able to resolve uWSGI symbols, just add -I<path_to_uwsgi_binary>
to the command line (see below):
gcc -fPIC -shared -I/usr/bin -o myapp.so myapp.go
Now let’s run it under uWSGI:
uwsgi --http-socket :9090 --http-socket-modifier1 11 --go-load ./myapp.so
The gccgo plugin registers itself as modifier1
11, so remember to set it to run Go code.
uwsgi.gox¶
By default when building the gccgo profile, a uwsgi.gox file is created. This can be used when building go apps using the uWSGI API, to resolve symbols.
Remember that if you add the directory containing the uwsgi binary (as seen before) to
the includes (-I path
) path of gcc, the binary itself will be used for resolving symbols.
Goroutines¶
Thanks to the new GCC split stack feature, goroutines are sanely (i.e. they do not require a full pthread) implemented in gccgo.
A loop engine mapping every uWSGI core to a goroutine is available in the plugin itself.
To start uWSGI in goroutine mode just add --goroutines <n>
where <n> is the maximum number of concurrent goroutines to spawn.
Like The Gevent loop engine, uWSGI signal handlers are executed in a dedicated goroutine.
In addition to this, all blocking calls make use of the netpoll
Go api. This means you can run internal routing actions, rpc included, in a goroutine.
Options¶
--go-load <path>
load the specified go shared library in the process address space--gccgo-load <path>
alias for go-load--go-args <arg1> <arg2> <argN>
set arguments passed to the virtual go command line--gccgo-args <arg1> <arg2> <argN>
alias for go-args--goroutines <n>
enable goroutines loop engine with the specified number of async cores
uWSGI API¶
注解
This section may, or may not, be out of date. Who knows!
Unfortunately only few pieces of the uWSGI API have been ported to the gccgo plugin. More features will be added in time for uWSGI 2.0.
Currently exposed API functions:
uwsgi.CacheGet(key string, cache string) string
uwsgi.RegisterSignal(signum uint8, receiver string, handler func(uint8)) bool
Notes¶
- Please, please do not enable multithreading, it will not work and probably will never work.
- All uWSGI native features (like internal routing) work in goroutines mode. However do not expect languages like Python or Perl to work over them anytime soon.
The Symcall plugin¶
The symcall plugin (modifier 18) is a convenience plugin allowing you to write native uWSGI request handlers without the need of developing a full uWSGI plugin.
You tell it which symbol to load on startup and then it will run it at every request.
注解
The “symcall” plugin is built-in by default in standard build profiles.
Step 1: preparing the environment¶
The uWSGI binary by itself allows you to develop plugins and libraries without the need of external development packages or headers.
The first step is getting the uwsgi.h
C/C++ header:
uwsgi --dot-h > uwsgi.h
Now, in the current directory, we have a fresh uwsgi.h ready to be included.
Step 2: our first request handler:¶
Our C handler will print the REMOTE_ADDR value with a couple of HTTP headers.
(call it mysym.c or whatever you want/need)
#include "uwsgi.h"
int mysym_function(struct wsgi_request *wsgi_req) {
// read request variables
if (uwsgi_parse_vars(wsgi_req)) {
return -1;
}
// get REMOTE_ADDR
uint16_t vlen = 0;
char *v = uwsgi_get_var(wsgi_req, "REMOTE_ADDR", 11, &vlen);
// send status
if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) return -1;
// send content_type
if (uwsgi_response_add_content_type(wsgi_req, "text/plain", 10)) return -1;
// send a custom header
if (uwsgi_response_add_header(wsgi_req, "Foo", 3, "Bar", 3)) return -1;
// send the body
if (uwsgi_response_write_body_do(wsgi_req, v, vlen)) return -1;
return UWSGI_OK;
}
Final step: map the symcall plugin to the mysym_function
symbol¶
uwsgi --dlopen ./mysym.so --symcall mysym_function --http-socket :9090 --http-socket-modifier1 18
With --dlopen
we load a shared library in the uWSGI process address space.
The --symcall
option allows us to specify which symbol to call when modifier1 18 is in place
We bind the instance to HTTP socket 9090 forcing modifier1 18.
Hooks and symcall unleashed: a TCL handler¶
We want to write a request handler running the following TCL script (foo.tcl) every time:
# call it foo.tcl
proc request_handler { remote_addr path_info query_string } {
set upper_pathinfo [string toupper $path_info]
return "Hello $remote_addr $upper_pathinfo $query_string"
}
We will define a function for initializing the TCL interpreter and parsing the script. This function will be called on startup soon after privileges drop.
Finally we define the request handler invoking the TCL proc and passign args to it
#include <tcl.h>
#include "uwsgi.h"
// global interpreter
static Tcl_Interp *tcl_interp;
// the init function
void ourtcl_init() {
// create the TCL interpreter
tcl_interp = Tcl_CreateInterp() ;
if (!tcl_interp) {
uwsgi_log("unable to initialize TCL interpreter\n");
exit(1);
}
// initialize the interpreter
if (Tcl_Init(tcl_interp) != TCL_OK) {
uwsgi_log("Tcl_Init error: %s\n", Tcl_GetStringResult(tcl_interp));
exit(1);
}
// parse foo.tcl
if (Tcl_EvalFile(tcl_interp, "foo.tcl") != TCL_OK) {
uwsgi_log("Tcl_EvalFile error: %s\n", Tcl_GetStringResult(tcl_interp));
exit(1);
}
uwsgi_log("TCL engine initialized");
}
// the request handler
int ourtcl_handler(struct wsgi_request *wsgi_req) {
// get request vars
if (uwsgi_parse_vars(wsgi_req)) return -1;
Tcl_Obj *objv[4];
// the proc name
objv[0] = Tcl_NewStringObj("request_handler", -1);
// REMOTE_ADDR
objv[1] = Tcl_NewStringObj(wsgi_req->remote_addr, wsgi_req->remote_addr_len);
// PATH_INFO
objv[2] = Tcl_NewStringObj(wsgi_req->path_info, wsgi_req->path_info_len);
// QUERY_STRING
objv[3] = Tcl_NewStringObj(wsgi_req->query_string, wsgi_req->query_string_len);
// call the proc
if (Tcl_EvalObjv(tcl_interp, 4, objv, TCL_EVAL_GLOBAL) != TCL_OK) {
// ERROR, report it to the browser
if (uwsgi_response_prepare_headers(wsgi_req, "500 Internal Server Error", 25)) return -1;
if (uwsgi_response_add_content_type(wsgi_req, "text/plain", 10)) return -1;
char *body = (char *) Tcl_GetStringResult(tcl_interp);
if (uwsgi_response_write_body_do(wsgi_req, body, strlen(body))) return -1;
return UWSGI_OK;
}
// all fine
if (uwsgi_response_prepare_headers(wsgi_req, "200 OK", 6)) return -1;
if (uwsgi_response_add_content_type(wsgi_req, "text/plain", 10)) return -1;
// write the result
char *body = (char *) Tcl_GetStringResult(tcl_interp);
if (uwsgi_response_write_body_do(wsgi_req, body, strlen(body))) return -1;
return UWSGI_OK;
}
You can build it with:
gcc -fPIC -shared -o ourtcl.so `./uwsgi/uwsgi --cflags` -I/usr/include/tcl ourtcl.c -ltcl
The only differences from the previous example are the -I and -l for adding the TCL headers and library.
So, let’s run it with:
uwsgi --dlopen ./ourtcl.so --hook-as-user call:ourtcl_init --http-socket :9090 --symcall ourtcl_handler --http-socket-modifier1 18
Here the only new player is --hook-as-user call:ourtcl_init
invoking the specified function after privileges drop.
注解
This code is not thread safe! If you want to improve this tcl library to support multithreading, best approach will be having a TCL interpreter for each pthread instead of a global one.
Considerations¶
Since uWSGI 1.9.21, thanks to the --build-plugin
option, developing uWSGI plugins has become really easy.
The symcall plugin is for tiny libraries/pieces of code, for bigger needs consider developing a full plugin.
The tcl example we have seen before is maybe the right example of “wrong” usage ;)
The XSLT plugin¶
Since uWSGI 1.9.1 a new plugin named “xslt” is available, implementing XML Stylesheet Transformation both as request handler and routing instruction.
To successfully apply a transformation you need a ‘doc’ (an XML document) and a stylesheet (the XSLT file).
Additionally you can apply global params and set a specific content type (by default the generated output is set as text/html).
The request handler¶
Modifier1 23 has been assigned to the XSLT request handler.
The document path is created appending the PATH_INFO
to the DOCUMENT_ROOT
.
The stylesheet path is created following these steps:
- If a specific CGI variable is set (via
--xslt-var
) it will be used as the stylesheet path. - If a file named like the document plus a specific extension (by default
.xsl
and.xslt
are searched) exists it will be used as the stylesheet path. - Finally a series of static XSLT files (specified with
--xslt-stylesheet
) is tried.
Examples:
uwsgi --http-socket :9090 --http-socket-modifier1 23 --xslt-ext .bar
If /foo.xml is requested (and the file exists) DOCUMENT_ROOT``+``foo.xml.bar
will be searched as the xslt file.
uwsgi --http-socket :9090 --http-socket-modifier1 23 --xslt-stylesheet /var/www/myfile1.xslt --xslt-stylesheet /var/www/myfile2.xslt
If /foo.xml is requested (and the file exists) /var/www/myfile1.xslt
will be tried. If it does not exist, /var/www/myfile2.xslt
will be tried instead.
uwsgi --http-socket :9090 --http-socket-modifier1 23 --xslt-var UWSGI_XSLT
If /foo.xml is requested (and the file exists), the content of the UWSGI_XSLT
variable (you can set it from your webserver) is used as the stylesheet path.
If a QUERY_STRING
is available, its items will be passed as global parameters to the stylesheet.
As an example if you request /foo.xml?foo=bar&test=uwsgi
, “foo” (as “bar” and “test” (as “uwsgi”) will be passed as global variables:
<xsl:value-of select="$foo"/>
<xsl:value-of select="$test"/>
The routing instruction¶
The plugin registers itself as internal routing instruction named “xslt”. It is probably a lot more versatile then the request plugin.
Its syntax is pretty simple:
[uwsgi]
plugin = xslt
route = ^/foo xslt:doc=${DOCUMENT_ROOT}/${PATH_INFO}.xml,stylesheet=/var/www/myfunction.xslt,content_type=text/html,params=foo=bar&test=unbit
This will apply the /var/www/myfunction.xslt
transformation to foo.xml
and will return it as text/html
.
The only required parameters for the routing instruction are doc
and stylesheet
.
SSI (Server Side Includes) plugin¶
Server Side Includes are an “old-fashioned” way to write dynamic web pages.
It is generally recognized as a templating system instead of a full featured language.
The main purpose of the uWSGI SSI plugin is to have a fast templating system that has access to the uWSGI API.
At the time of writing, March 2013, the plugin is beta quality and implements less than 30% of the SSI standard, the focus being in exposing uWSGI API as SSI commands.
Using it as a request handler¶
The plugin has an official modifier1, number 19.
[uwsgi]
plugin = ssi
http = :9090
http-modifier1 = 19
http-var = DOCUMENT_ROOT=/var/www
The plugin builds the filename as DOCUMENT_ROOT``+``PATH_INFO
. This file is then parsed as a server side include document.
Both DOCUMENT_ROOT
and PATH_INFO
are required, otherwise a 500 error will be returned.
An example configuration for Nginx would be:
location ~ \.shtml$ {
root /var/www;
include uwsgi_params;
uwsgi_pass 127.0.0.1:3031;
uwsgi_modifier1 19;
}
with something like this for uWSGI...
[uwsgi]
plugin = ssi
socket = 127.0.0.1:3031
Using SSI as a routing action¶
A more versatile approach is using the SSI parser as a routing action.
[uwsgi]
plugin = ssi
http-socket = :9090
route = ^/(.*) ssi:/var/www/$1.shtml
警告
As with all of the routing actions, no check on file paths is made to allow a higher level of customization. If you pass untrusted paths to the SSI action, you should sanitize them (you can use routing again, checking for the presence of .. or other dangerous symbols).
And with the above admonition in mind, when used as a routing action, DOCUMENT_ROOT
or PATH_INFO
are not required, as the parameter passed contains the full filesystem path.
Supported SSI commands¶
This is the list of supported commands (and their arguments). If a command is not part of the SSI standard (that is, it’s uWSGI specific) it will be reported.
printenv¶
Print a list of all request variables.
cache¶
注解
This is uWSGI specific/non-standard.
Arguments: key
name
Print the value of the specified cache key in the named cache.
Status¶
- The plugin is fully thread safe and very fast.
- Very few commands are available, more will be added soon.
uWSGI V8 support¶
Building¶
You will need the libv8
headers to build the plugin. The official modifier1 value for V8 is ‘24’.
RPC¶
function part1(request_uri, remote_addr) {
return '<h1>i am part1 for ' + request_uri + ' ' + remote_addr + "</h1>" ;
}
function part2(request_uri, remote_addr) {
return '<h2>i am part2 for ' + request_uri + ' ' + remote_addr + "</h2>" ;
}
function part3(request_uri, remote_addr) {
return '<h3>i am part3 for ' + request_uri + ' ' + remote_addr + "</h3>" ;
}
uwsgi.register_rpc('part1', part1);
uwsgi.register_rpc('part2', part2);
uwsgi.register_rpc('part3', part3);
ciao = function(saluta) {
uwsgi.log("I have no idea what's going on.");
return "Ciao Ciao";
}
uwsgi.register_rpc('hello', ciao);
Signal handlers¶
function tempo(signum) {
uwsgi.log("e' passato 1 secondo");
}
uwsgi.register_signal(17, '', tempo);
Multitheading and multiprocess¶
Mules¶
The uWSGI API¶
JSGI 3.0¶
exports.app = function (request) {
uwsgi.log("Hello! I am the app.\n");
uwsgi.log(request.scheme + ' ' + request.method + ' ' + request.scriptName + ' ' + request.pathInfo + ' ' + request.queryString + ' ' + request.host);
uwsgi.log(request.serverSoftware);
return {
status: 200,
headers: {"Content-Type": "text/plain", "Server": ["uWSGI", "v8/plugin"]},
body: ["Hello World!", "I am V8"]
};
}
uwsgi --plugin v8 --v8-jsgi myapp.js --http-socket :8080 --http-socket-modifier1 24
CommonJS¶
- Require: OK
- Binary/B: NO
- System/1.0: in progress
- IO/A: NO
- Filesystem/A: NO
The GridFS plugin¶
Beginning in uWSGI 1.9.5 a “GridFS” plugin is available. It exports both a request handler and an internal routing function. Its official modifier is ‘25’. The routing instruction is “gridfs” The plugin is written in C++.
Requirements and install¶
To build the plugin you need the libmongoclient
headers (and a functioning
C++ compiler). On a Debian-like system you can do the following.
apt-get install mongodb-dev g++
A build profile for gridfs is available:
UWSGI_PROFILE=gridfs make
Or you can build it as plugin:
python uwsgiconfig.py --plugin plugins/gridfs
For a fast installation of a monolithic build you can use the network installer:
curl http://uwsgi.it/install | bash -s gridfs /tmp/uwsgi
This will install a gridfs enabled uwsgi binary.
Standalone quickstart¶
This is a standalone config that blindly maps the incoming PATH_INFO
to
items in the GridFS db named “test”:
[uwsgi]
; you can remove the plugin directive if you are using a uWSGI gridfs monolithic build
plugin = gridfs
; bind to http port 9090
http-socket = :9090
; force the modifier to be the 25th
http-socket-modifier1 = 25
; map gridfs requests to the "test" db
gridfs-mount = db=test
Assuming you have the myfile.txt file stored in your GridFS as “/myfile.txt”, run the following:
curl -D /dev/stdout http://localhost:9090/myfile.txt
and you should be able to get it.
The initial slash problem¶
Generally PATH_INFO
is prefixed with a ‘/’. This could cause problems in
GridFS path resolution if you are not storing the items with absolute path
names. To counteract this, you can make the gridfs
plugin to skip the
initial slash:
[uwsgi]
; you can remove the plugin directive if you are using a uWSGI gridfs monolithic build
plugin = gridfs
; bind to http port 9090
http-socket = :9090
; force the modifier to be the 25th
http-socket-modifier1 = 25
; map gridfs requests to the "test" db
gridfs-mount = db=test,skip_slash=1
Now instead of searching for /myfile.txt it will search for “myfile.txt”.
Multiple mountpoints (and servers)¶
You can mount different GridFS databases under different SCRIPT_NAME (or
UWSGI_APPID). If your web server is able to correctly manage the
SCRIPT_NAME
variable you do not need any additional setup (other than
–gridfs-mount). Otherwise don’t forget to add the –manage-script-name option
[uwsgi]
; you can remove the plugin directive if you are using a uWSGI gridfs monolithic build
plugin = gridfs
; bind to http port 9090
http-socket = :9090
; force the modifier to be the 25th
http-socket-modifier1 = 25
; map gridfs requests to the "test" db
gridfs-mount = db=test,skip_slash=1
; map /foo to db "wolverine" on server 192.168.173.17:4040
gridfs-mount = mountpoint=/foo,server=192.168.173.17:4040,db=wolverine
; map /bar to db "storm" on server 192.168.173.30:4040
gridfs-mount = mountpoint=/bar,server=192.168.173.30:4040,db=storm
; force management of the SCRIPT_NAME variable
manage-script-name = true
curl -D /dev/stdout http://localhost:9090/myfile.txt
curl -D /dev/stdout http://localhost:9090/foo/myfile.txt
curl -D /dev/stdout http://localhost:9090/bar/myfile.txt
This way each request will map to a different GridFS server.
Replica sets¶
If you are using a replica set, you can use it in your uWSGI config with this syntax: <replica>server1,server2,serverN...
[uwsgi]
http-socket = :9090
http-socket-modifier1 = 25
gridfs-mount = server=rs0/ubuntu64.local\,raring64.local\,mrspurr-2.local,db=test
Pay attention to the backslashes used to escape the server list.
Prefixes¶
As well as removing the initial slash, you may need to prefix each item name:
[uwsgi]
http-socket = :9090
http-socket-modifier1 = 25
gridfs-mount = server=rs0/ubuntu64.local\,raring64.local\,mrspurr-2.local,db=test,prefix=/foobar___
A request for /test.txt will be mapped to /foobar___/test.txt
while
[uwsgi]
http-socket = :9090
http-socket-modifier1 = 25
gridfs-mount = server=rs0/ubuntu64.local\,raring64.local\,mrspurr-2.local,db=test,prefix=/foobar___,skip_slash=1
will map to /foobar___test.txt
MIME types and filenames¶
By default the MIME type of the file is derived from the filename stored in
GridFS. This filename might not map to the effectively requested URI or you may
not want to set a content_type
for your response. Or you may want to allow
some other system to set it. If you want to disable MIME type generation just
add no_mime=1
to the mount options.
[uwsgi]
http-socket = :9090
http-socket-modifier1 = 25
gridfs-mount = server=ubuntu64.local,db=test,skip_slash=1,no_mime=1
If you want your response to set the filename using the original value (the one
stored in GridFS) add orig_filename=1
[uwsgi]
http-socket = :9090
http-socket-modifier1 = 25
gridfs-mount = server=ubuntu64.local,db=test,skip_slash=1,no_mime=1,orig_filename=1
Timeouts¶
You can set the timeout of the low-level MongoDB operations by adding
timeout=N
to the options:
[uwsgi]
http-socket = :9090
http-socket-modifier1 = 25
; set a 3 seconds timeout
gridfs-mount = server=ubuntu64.local,db=test,skip_slash=1,timeout=3
MD5 and ETag headers¶
GridFS stores an MD5 hash of each file. You can add this info to your response
headers both as ETag (MD5 in hex format) or Content-MD5 (in Base64). Use
etag=1
for adding ETag header and md5=1
for adding Content-MD5. There’s
nothing stopping you from adding both headers to the response.
[uwsgi]
http-socket = :9090
http-socket-modifier1 = 25
; set a 3 seconds timeout
gridfs-mount = server=ubuntu64.local,db=test,skip_slash=1,timeout=3,etag=1,md5=1
Multithreading¶
The plugin is fully thread-safe, so consider using multiple threads for improving concurrency:
[uwsgi]
http-socket = :9090
http-socket-modifier1 = 25
; set a 3 seconds timeout
gridfs-mount = server=ubuntu64.local,db=test,skip_slash=1,timeout=3,etag=1,md5=1
master = true
processes = 2
threads = 8
This will spawn 2 processes monitored by the master with 8 threads each for a total of 16 threads.
Combining with Nginx¶
This is not different from the other plugins:
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:3031;
uwsgi_modifier1 25;
}
Just be sure to set the uwsgi_modifier1
value to ensure all requests get
routed to GridFS.
[uwsgi]
socket = 127.0.0.1:3031
gridfs-mount = server=ubuntu64.local,db=test,skip_slash=1,timeout=3,etag=1,md5=1
master = true
processes = 2
threads = 8
The ‘gridfs’ internal routing action¶
The plugin exports a ‘gridfs’ action simply returning an item:
[uwsgi]
socket = 127.0.0.1:3031
route = ^/foo/(.+).jpg gridfs:server=192.168.173.17,db=test,itemname=$1.jpg
The options are the same as the request plugin’s, with “itemname” being the only addition. It specifies the name of the object in the GridFS db.
Notes¶
- If you do not specify a server address, 127.0.0.1:27017 is assumed.
- The use of the plugin in async modes is not officially supported, but may work.
- If you do not get why a request is not serving your GridFS item, consider
adding the
--gridfs-debug
option. It will print the requested item in uWSGI logs.
The GlusterFS plugin¶
Available from uWSGI 1.9.15
official modifier1: 27
The ‘glusterfs’ plugin allows you to serve files stored in glusterfs filesystems directly using the glusterfs api available starting from GlusterFS 3.4
This approach (compared to serving via fuse or nfs) has various advantages in terms of performances and ease of deployment.
Step1: glusterfs installation¶
we build glusterfs from official sources, installing it in /opt/glusterfs on 3 nodes (192.168.173.1, 192.168.173.2, 192.168.173.3).
./configure --prefix=/opt/glusterfs
make
make install
now start the configuration/control daemon with:
/opt/glusterfs/sbin/glusterd
from now on we can start configuring our cluster
Step2: the first cluster¶
run the control client to access the glusterfs shell:
/opt/glusterfs/sbin/gluster
the first step is “discovering” the other nodes:
# do not run on node1 !!!
peer probe 192.168.173.1
# do not run on node2 !!!
peer probe 192.168.173.2
# do not run on node3 !!!
peer probe 192.168.173.3
remember, you do not need to run “peer probe” for the same address of the machine on which you are running the glusterfs console. You have to repeat the procedure on each node of the cluser.
Now we can create a replica volume (/exports/brick001 dir has to exist in every node):
volume create unbit001 replica 3 192.168.173.1:/exports/brick001 192.168.173.2:/exports/brick001 192.168.173.3:/exports/brick001
and start it:
volume start unbit001
Now you should be able to mount your glusterfs filesystem and start writing files in it (you can use nfs or fuse)
Step3: uWSGI¶
a build profile, named ‘glusterfs’ is already available, so you can simply do:
PKG_CONFIG_PATH=/opt/glusterfs/lib/pkgconfig/ UWSGI_PROFILE=glusterfs make
The profile currently disable ‘matheval’ support as the glusterfs libraries use bison/yacc with the same function prefixes (causing nameclash).
You can now start your HTTP serving fastly serving glusterfs files (remember no nfs or fuse are involved):
[uwsgi]
; bind on port 9090
http-socket = :9090
; set the default modifier1 to the glusterfs one
http-socket-modifier1 = 27
; mount our glusterfs filesystem
glusterfs-mount = mountpoint=/,volume=unbit001,server=192.168.173.1:0
; spawn 30 threads
threads = 30
High availability¶
The main GlusterFS selling point is high availability. With the prevopus setup we introduced a SPOF with the control daemon.
The ‘server’ option allows you to specify multiple control daemons (they are tried until one responds)
[uwsgi]
; bind on port 9090
http-socket = :9090
; set the default modifier1 to the glusterfs one
http-socket-modifier1 = 27
; mount our glusterfs filesystem
glusterfs-mount = mountpoint=/,volume=unbit001,server=192.168.173.1:0;192.168.173.2:0;192.168.173.3:0
; spawn 30 threads
threads = 30
The ‘0’ port is a glusterfs convention, it means ‘the default port’ (generally 24007). You can specify whatever port you need/want
Multiple mountpoints¶
If your webserver (like nginx or the uWSGI http router) is capable of setting protocol vars (like SCRIPT_NAME or UWSGI_APPID) you can mount multiple glusterfs filesystems in the same instance:
[uwsgi]
; bind on port 9090
http-socket = :9090
; set the default modifier1 to the glusterfs one
http-socket-modifier1 = 27
; mount our glusterfs filesystem
glusterfs-mount = mountpoint=/,volume=unbit001,server=192.168.173.1:0;192.168.173.2:0;192.168.173.3:0
glusterfs-mount = mountpoint=/foo,volume=unbit002,server=192.168.173.1:0;192.168.173.2:0;192.168.173.3:0
glusterfs-mount = mountpoint=/bar,volume=unbit003,server=192.168.173.1:0;192.168.173.2:0;192.168.173.3:0
; spawn 30 threads
threads = 30
Multiprocess VS multithread¶
Currently a mix of the both will offers you best performance and availability.
Async support is on work
Internal routing¶
The uWSGI internal routing allows you to rewrite requests to change the requested files. Currently the glusterfs plugin only uses the PATH_INFO, so you can change it via the ‘setpathinfo’ directive
Caching is supported too. Check the tutorial (linked in the homepage) for some cool idea
Using capabilities (on Linux)¶
If your cluster requires clients to bind on privileged ports (<1024) and you do not want to change that thing (and obviously you do not want to run uWSGI as root) you may want to give your uWSGI instance the NET_BIND_SERVICE capability. Just ensure you have a capabilities-enabled uWSGI and add
... --cap net_bind_service ...
to all of the instances you want to connect to glusterfs
The RADOS plugin¶
Available from uWSGI 1.9.16, stable from uWSGI 2.0.6
official modifier1: 28
Authors: Javier Guerra, Marcin Deranek, Roberto De Ioris
The ‘rados’ plugin allows you to serve objects stored in a Ceph cluster directly using the librados API.
Note that it’s not the CephFS filesystem, nor the ‘radosgw’ S3/Swift-compatible layer; RADOS is the bare object-storage layer.
Step1: Ceph cluster and content¶
If you want to try a minimal Ceph instalation, you can follow this guide: http://ceph.com/docs/master/start/. note that you only need the OSD and MON daemons, the MDS are needed only for CephFS filesystems.
Once you get it running, you should have a configuration file (by default on /etc/ceph/ceph.con), and should be able to use the rados utility.
rados lspools
by default, you should have at least the ‘data’, ‘metadata’ and ‘rbd’ pools. Now add some content to the ‘data’ pool. For example, if you have a ‘list.html’ file and images ‘first.jpeg’, ‘second.jpeg’ on a subdirectory ‘imgs/’:
rados -p data put list.html list.html
rados -p data put imgs/first.jpeg imgs/first.jpeg
rados -p data put imgs/second.jpeg imgs/second.jpeg
rados -p data ls -
note that RADOS doesn’t have a concept of directories, but the object names can contain slashes.
Step2: uWSGI¶
A build profile, named ‘rados’ is already available, so you can simply do:
make PROFILE=rados
or
python uwsgiconfig.py --build rados
or use the installer
# this will create a binary called /tmp/radosuwsgi that you will use instead of 'uwsgi'
curl http://uwsgi.it/install | bash -s rados /tmp/radosuwsgi
Obviously you can build rados support as plugin
uwsgi --build-plugin plugins/rados/
or the old style:
python uwsgiconfig.py --plugin plugins/rados/
You can now start an HTTP server to serve RADOS objects:
[uwsgi]
; bind on port 9090
http-socket = :9090
; set the default modifier1 to the rados one
http-socket-modifier1 = 28
; mount our rados pool
rados-mount = mountpoint=/rad/,pool=data,config=/etc/ceph/ceph.conf
; spawn 30 threads
threads = 30
the ‘rados-mount’ parameter takes various subparameters:
- mountpoint: required, the URL prefix on which the RADOS objects will appear.
- pool: required, the RADOS pool to serve.
- config: optional, the path to the ceph config file.
- timeout: optional, set the timeout for operations, in seconds
- allow_put: allow calling the
PUT
HTTP method to store new objects- allow_delete: allow calling the
DELETE
HTTP method to remove objects- allow_mkcol: allow calling
MKCOL
HTTP method to create new pools- allow_propfind: (requires uWSGI 2.1) allow calling the WebDAV
PROPFIND
method
In this example, your content will be served at http://localhost:9090/rad/list.html, http://localhost:9090/rad/imgs/first.jpeg and http://localhost:9090/rad/imgs/second.jpeg.
High availability¶
The RADOS storage system is fully distributed, just starting several uWSGI workers on several machines with the same ‘ceph.conf’, all will see the same pools. If they all serve on the same mountpoint, you get a failure-resistant RADOS-HTTP gateway.
Multiple mountpoints¶
You can issue several ‘rados-mount’ entries, each one will define a new mountpoint. This way you can expose different RADOS pools at different URLs.
HTTP methods¶
The following methods are supported:
- GET -> retrieve a resource
- HEAD -> like GET but without body
- OPTIONS -> (requires uWSGI 2.1) returns the list of allowed HTTP methods and WebDAV support
- PUT -> requires allow_put in mountpoint options, store a resource in ceph: curl -T /etc/services http://localhost:8080/services
- MKCOL -> requires allow_mkcol in mountpoint options, creates a new pool: curl -X MKCOL http://localhost:8080/anewpool (the pool ‘anewpool’ will be created)
- DELETE -> requires allow_delete in mountpoint options, removes an object
- PROPFIND -> requires allow_propfind in mountpoint options (uWSGI 2.1+), implements WebDAV PROPFIND method
Features¶
- multiprocessing is supported
- async support is fully functional, the ugreen suspend engine is the only supported one:
[uwsgi]
; bind on port 9090
http-socket = :9090
; set the default modifier1 to the rados one
http-socket-modifier1 = 28
; mount our rados pool
rados-mount = mountpoint=/rad/,pool=data,config=/etc/ceph/ceph.conf
; spawn 1000 async cores
async = 1000
; required !!!
ugreen = true
Caching example¶
Caching is highly recommended to improve performance and reduce the load on the Ceph cluster. This is a good example:
[uwsgi]
; create a bitmap cache with max 1000 items storable in 10000 4k blocks
cache2 = name=radoscache,items=1000,blocks=10000,blocksize=4096,bitmap=1
; check every object ending with .html in the 'radoscache' cache
route = \.html$ cache:key=${PATH_INFO},name=radoscache,content_type=text/html
; if not found, store it at the end of the request for 3600 seconds (this will automatically enable Expires header)
route = \.html$ cachestore:key=${PATH_INFO},name=radoscache,expires=3600
; general options
; master is always a good idea
master = true
; bind on http port 9090 (better to use a uwsgi socket behind a proxy like nginx)
http-socket = :9090
; set the default modifier1 to the rados one
http-socket-modifier1 = 28
; mount our rados 'htmlpages' pool
rados-mount = mountpoint=/,pool=htmlpages
; spawn multiple processes and threads
processes = 4
threads = 8
To test the caching behaviour, a tool like uwsgicachetop (https://pypi.python.org/pypi/uwsgicachetop) will be very useful.
More information about caching here: CachingCookbook
Security note¶
Enabling MKCOL, PUT and DELETE may be high security risks.
Combine them with the internal routing framework for adding authentication/authorization policies:
[uwsgi]
master = true
; bind on http port 9090 (better to use a uwsgi socket behind a proxy like nginx)
http-socket = :9090
; set the default modifier1 to the rados one
http-socket-modifier1 = 28
; mount our rados 'htmlpages' pool
rados-mount = mountpoint=/,pool=htmlpages,allow_put=1,allow_mkcol=1
; spawn multiple processes and threads
processes = 4
threads = 8
; permit PUT only to authenticated 'foo' user
route-if = equal:${REQUEST_METHOD};PUT basicauth:my secret area,foo:bar
; allow MKCOL only from 127.0.0.1
route-if = equal:${REQUEST_METHOD};MKCOL goto:check_localhost
; end of the chain
route-run = last:
route-label = check_localhost
; if REMOTE_ADDR = 127.0.0.1 -> continue to rados plugin
route-remote-addr = ^127\.0\.0\.1$ continue:
; otherwise break with 403
route-run = break:403 Forbidden
Notes¶
- The plugin automatically enables the MIME type engine.
- There is no directory index support. It makes no sense in rados/ceph context.
- You should drop privileges in your uWSGI instances, so be sure you give the right permissions to the ceph keyring.
其他插件¶
The Pty plugin¶
- Available since uWSGI 1.9.15, supported on Linux, OpenBSD, FreeBSD and OSX
This plugin allows you to attach pseudo terminals to your applications.
Currently the pseudoterminal server can be attached (and exposed over network) only on the first worker (this limit will be removed in the future).
The plugin exposes a client mode too (avoiding you to mess with netcat, telnet or screen settings)
Building it¶
The plugin is not included in the default build profiles, so you have to build it manually:
python uwsgiconfig.py --plugin plugins/pty [profile]
(remember to specify the build profile if you are not using the default one)
Example 2: IPython control thread¶
import IPython
from uwsgidecorators import *
# only worker 1 has the pty attached
@postfork(1)
@thread
def tshell():
while True:
IPython.embed()
SPNEGO authentication¶
Configuring uWSGI with LDAP¶
uWSGI can be configured using LDAP. LDAP is a flexible way to centralize configuration of large clusters of uWSGI servers.
注解
LDAP support must be enabled while building uWSGI. The libldap library is required.
Importing the uWSGIConfig schema¶
Running uWSGI with the –ldap-schema or –ldap-schema-ldif parameter will make it output a standard LDAP schema (or an LDIF file) that you can import into your server.
An example LDIF dump¶
This is an LDIF dump of an OpenLDAP server with a uWSGIConfig entry, running a Trac instance.
dn: dc=projects,dc=unbit,dc=it
objectclass: uWSGIConfig
objectclass: domain
dc: projects
uWSGIsocket: /var/run/uwsgi/projects.unbit.it.sock
uWSGIhome: /accounts/unbit/tracvenv
uWSGImodule: trac.web.main:dispatch_request
uWSGImaster: TRUE
uWSGIprocesses: 4
uWSGIenv: TRAC_ENV=/accounts/unbit/trac/uwsgi
Usage¶
You only need to pass a valid LDAP url to the –ldap option. Only the first entry returned will be used as configuration.
uwsgi –ldap ldap://ldap.unbit.it/dc=projects,dc=unbit,dc=it
If you want a filter with sub scope (this will return the first record under the tree dc=projects,dc=unbit,dc=it with ou=Unbit):
uwsgi –ldap ldap://ldap.unbit.it/dc=projects,dc=unbit,dc=it?sub?ou=Unbit
弃用(Broken/deprecated)特性¶
Integrating uWSGI with Erlang¶
警告
Erlang support is broken as of 1.9.20. A new solution is being worked on.
The uWSGI server can act as an Erlang C-Node and exchange messages and RPC with Erlang nodes.
Building¶
First of all you need the ei
libraries and headers. They are included in
the official erlang tarball. If you are on Debian/Ubuntu, install the
erlang-dev
package. Erlang support can be embedded or built as a plugin.
For embedding, add the erlang
and pyerl
plugins to your buildconf.
embedded_plugins = python, ping, nagios, rpc, fastrouter, http, ugreen, erlang, pyerl
or build both as plugins
python uwsgiconfig --plugin plugins/erlang
python uwsgiconfig --plugin plugins/pyerl
The Erlang plugin will allow uWSGI to became a Erlang C-Node. The pyerl
plugin will add Erlang functions to the Python plugin.
Activating Erlang support¶
You only need to set two options to enable Erlang support in your
Erlang-enabled uWSGI build. The erlang
option sets the Erlang node name of
your uWSGI server. It may be specified in simple or extended format:
nodename@ip
nodename@address
nodename
The erlang-cookie
option sets the cookie for inter-node communications. If
you do not specify it, the value is taken from the ~/.erlang.cookie
file.
To run uWSGI with Erlang enabled:
uwsgi --socket :3031 --erlang testnode@192.168.173.15 --erlang-cookie UUWSGIUWSGIU -p 2
A simple RPC hello world example¶
Define a new erlang module that exports only a simple function.
-module(uwsgitest). -export([hello/0]). hello() -> 'hello world !'.
Launch the
erl
shell specifying the nodename and (eventually) the cookie:erl -name testnode@192.168.173.1
Compile the uwsgitest Erlang module
c(uwsgitest). {ok,uwsgitest}
... and try to run the
hello
function:uwsgitest:hello(). 'hello world !'
Great - now that our Erlang module is working, we are ready for RPC! Return to
your uWSGI server machine and define a new WSGI module – let’s call it
erhello.py
.
import uwsgi
def application(env, start_response):
testnode = uwsgi.erlang_connect("testnode@192.168.173.1")
start_response('200 OK', [('Content-Type', 'text/plain')])
yield uwsgi.erlang_rpc(testnode, "uwsgitest", "hello", [])
uwsgi.erlang_close(testnode)
or the fast-style
import uwsgi
def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
yield uwsgi.erlang_rpc("testnode@192.168.173.1", "uwsgitest", "hello", [])
Now relaunch the uWSGI server with this new module:
uwsgi --socket :3031 --erlang testnode@192.168.173.15 --erlang-cookie UUWSGIUWSGIU -p 2 -w erhello
Point your browser to your uWSGI enabled webserver and you should see the output of your erlang RPC call.
Python-Erlang mappings¶
The uWSGI server tries to translate Erlang types to Python objects according to the table below.
Python | Erlang | note |
---|---|---|
str | binary | |
unicode | atom | limited by internal atom size |
int/long | int | |
list | list | |
tuple | tuple | |
3-tuple | pid |
Sending messages to Erlang nodes¶
One of the most powerful features of Erlang is the inter-node message passing system. uWSGI can communicate with Erlang nodes as well. Lets define a new Erlang module that simply will echo back whatever we send to it.
-module(uwsgiecho).
-export([start/0, loop/0, echo/1]).
echo(Message) ->
{i_am_echo , Message}.
loop() ->
receive
Message1 ->
io:format("received a message~n"),
{ useless, 'testnode@192.168.173.15' } ! echo(Message1)
end,
loop().
start() ->
register(echoer, spawn(uwsgiecho, loop, [])).
Remember to register your process with the Erlang register
function. Using
pids to identify processes is problematic. Now you can send messages with
uwsgi.erlang_send_message()
.
uwsgi.erlang_send_message(node, "echoer", "Hello echo server !!!" )
The second argument is the registered process name. If you do not specify the
name, pass a 3-tuple of Python elements to be interpreted as a Pid. If your
Erlang server returns messages to your requests you can receive them with
uwsgi.erlang_recv_message()
. Remember that even if Erlang needs a
process name/pid to send messages, they will be blissfully ignored by uWSGI.
Receiving erlang messages¶
Sometimes you want to directly send messages from an Erlang node to the uWSGI server. To receive Erlang messages you have to register “Erlang processes” in your Python code.
import uwsgi
def erman(arg):
print "received an erlang message:", arg
uwsgi.erlang_register_process("myprocess", erman)
Now from Erlang you can send messages to the “myprocess” process you registered:
{ myprocess, 'testnode@192.168.173.15' } ! "Hello".
RPC¶
You can call uWSGI uWSGI RPC Stack functions directly from Erlang.
rpc:call('testnode@192.168.173.15', useless, myfunction, []).
this will call the “myfunction” uWSGI RPC function on a uWSGI server configured as an Erlang node.
Connection persistence¶
On high-loaded sites opening and closing connections for every Erlang
interaction is overkill. Open a connection on your app initialization with
uwsgi.erlang_connect()
and hold on to the file descriptor.
What about Mnesia?¶
We suggest you to use Mnesia when you need a high-availability site. Build an
Erlang module to expose all the database interaction you need and use
uwsgi.erlang_rpc()
to interact with it.
Can I run EWGI applications on top of uWSGI?¶
For now, no. The best way to do this would be to develop a plugin and assign a special modifier for EWGI apps.
But before that happens, you can wrap the incoming request into EWGI form in
Python code and use uwsgi.erlang_rpc()
to call your Erlang app.
Management Flags¶
警告
This feature may be currently broken or deprecated.
You can modify the behavior of some aspects of the uWSGI stack remotely, without taking the server offline using the Management Flag system.
注解
A more comprehensive re-setup system may be in the works.
All the flags take an unsigned 32-bit value (so the block size is always 4) that contains the value to set for the flag. If you do not specify this value, only sending the uWSGI header, the server will count it as a read request.
Flag | Action | Description |
---|---|---|
0 | logging | enable/disable logging |
1 | max_requests | set maximum number of requests per worker |
2 | socket_timeout | modify the internal socket timeout |
3 | memory_debug | enable/disable memory debug/report |
4 | master_interval | set the master process check interval |
5 | harakiri | set/unset the harakiri timeout |
6 | cgi_mode | enable/disable cgi mode |
7 | threads | enable/disable threads (currently unimplemented) |
8 | reaper | enable/disable process reaper |
9 | log-zero | enable/disable logging of request with zero response size |
10 | log-slow | set/unset logging of slow requests |
11 | log-4xx | enable/disable logging of request with 4xx response status |
12 | log-5xx | enable/disable logging of request with 5xx response status |
13 | log-big | set/unset logging of request with big response size |
14 | log-sendfile | set/unset logging of sendfile requests |
15 | backlog-status | report the current size of the backlog queue (linux on tcp only) |
16 | backlog-errors | report the number of errors in the backlog queue (linux on tcp only) |
myadmin tool¶
A simple (and ugly) script, myadmin
, is included to remotely change management flags:
# disable logging on the uWSGI server listening on 192.168.173.17 port 3031
./uwsgi --no-server -w myadmin --pyargv "192.168.173.17:3031 0 0"
# re-enable logging
./uwsgi --no-server -w myadmin --pyargv "192.168.173.17:3031 0 1"
# read a value:
./uwsgi --no-server -w myadmin --pyargv "192.168.173.17:3031 15"
uWSGI Go support (1.4 only)¶
警告
Starting from 1.9.20, the Go plugin has been superseded by the The GCCGO plugin plugin.
Starting from uWSGI 1.4-dev you can host Go web applications in your uWSGI stack. You can download Go from http://golang.org/ . Currently Linux i386/x86_64, FreeBSD i386/x86_64 and OSX are supported. For OSX support, you need Go 1.0.3+ or you will need to apply the patch available at http://code.google.com/p/go/source/detail?r=62b7ebe62958 Goroutines are currently supported only on Linux i386/x86_64.
Building uWSGI with Go support¶
Go support can be built as an embedded component or plugin. The main difference with the setup of other languages is this time we will build a uwsgi library and not a uwsgi binary. This library will be used by a Go package named uwsgi.go you can link with your apps. Do not be afraid as in the uWSGI distribution there is already a build profile to make a completely (monolithic) distribution with Go support embedded. At the end of the build procedure you will have a libuwsgi.so shared library and a uwsgi.a Go package.
To build uWSGI+go just run (from uWSGI sources directory)
UWSGI_PROFILE=go make
or if Python is not in your system path, or you need to use a specific python version:
/usr/local/bin/python uwsgiconfig.py --build go
(or wherever your custom Python is)
At the end of the build procedure you will have a libuwsgi.so file (copy or link it to a library directory like /usr/local/lib or /usr/lib and eventually run ldconfig if needed) and a uwsgi.a file in a subdirectory (based on your arch/os) in plugins/go/pkg.
重要
The last message from the build procedure reports the GOPATH
you should
use when building uWSGI Go apps (copy/remember/annotate that value
somewhere).
If you already know how the Go import system works, feel free to copy uwsgi.a in your system-wide GOPATH.
Writing the first Go application¶
By default the uWSGI Go plugin supports the http.DefaultServeMux
handler,
so if your app is already based on it, running it in uWSGI should be extremely
simple.
package main
import (
"uwsgi"
"net/http"
"fmt"
)
func oneHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>One</h1>")
}
func twoHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h2>Two</h2>")
}
func main() {
http.HandleFunc("/one/", oneHandler)
http.HandleFunc("/two/", twoHandler)
uwsgi.Run()
}
As you can see, the only differences from a standard net/http
-based
application are the need to import "uwsgi"
need and calling uwsgi.Run()
function, which will run the whole uWSGI server. If you want to use your
personal request handler instead of http.DefaultServeMux
, use
uwsgi.Handler(http.Handler)
or
uwsgi.RequestHandler(func(http.ResponseWriter, *http.Request))
to set it.
func myHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h2>Two</h2>")
}
func main() {
uwsgi.RequestHandler(myHandler)
uwsgi.Run()
}
Building your first app¶
Assuming you saved your app as helloworld.go, just run the following.
GOPATH=/home/foobar/uwsgi/plugins/go go build helloworld.go
change GOPATH to the value you got from the build procedure, or to the dir you have installed/copied uwsgi.a If all goes well you will end with a ‘helloworld’ executable. That executable is a full uWSGI server (yes, really).
./helloworld --http :8080 --http-modifier1 11
Just point your browser to the port 8080 and check /one/ and /two/ You can start adding processes and a master:
./helloworld --http :8080 --http-modifier1 11 --master --processes 8
Note: modifier1 11 is officially assigned to Go.
Going in production¶
In a production environment you will probably put a webserver/proxy in front of your app. Thus your nginx config will look like this:
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:3031;
uwsgi_modifier1 11;
}
while your uWSGI config will be something like this...
[uwsgi]
socket = 127.0.0.1:3031
master = true
processes = 4
Finally simply run your app:
./helloworld config.ini
Goroutines (currently Linux/FreeBSD-only)¶
Goroutines are very probably the most interesting feature of the Go platform.
A uWSGI loop engine for goroutines is automatically embedded in the uWSGI
library when you build it with the go plugin. To spawn goroutines in each
uWSGI process just add the goroutines = N
option, where N is the number of
goroutines to spawn.
[uwsgi]
socket = 127.0.0.1:3031
master = true
processes = 4
goroutines = 100
With this config you will spawn 100 goroutines for each uWSGI process, yielding a grand total of 400 goroutines (!) As far as uWSGI is concerned, goroutines map to pthreads, but you will be able to spawn coroutine-based tasks from your application as well.
uWSGI api¶
It is fairly simple to access the uWSGI API from your Go app. To do so invoke the functions exported by the uwsgi package:
package main
import (
"fmt"
"uwsgi"
)
func hello2(signum int) {
fmt.Println("I am an rb_timer running on mule", uwsgi.MuleId())
}
func hello(signum int) {
fmt.Println("Ciao, 3 seconds elapsed")
}
func postinit() {
uwsgi.RegisterSignal(17, "", hello)
uwsgi.AddTimer(17, 3)
uwsgi.RegisterSignal(30, "mule1", hello2)
uwsgi.AddRbTimer(30, 5)
}
func foofork() {
fmt.Println("fork() has been called")
}
func main() {
uwsgi.PostInit(postinit)
uwsgi.PostFork(foofork)
uwsgi.Run()
}
The PostInit() function set the ‘hook’ to be called after the Go initialization is complete. The PostFork() function set the ‘hook’ to be called after each fork() In postinit hook we register two uwsgi signals, with the second one being run in a mule (the mule1) To run the code just build your new app as above and execute it
[uwsgi]
socket = 127.0.0.1:3031
master = true
processes = 2
goroutines = 20
mules = 2
memory-report = true
This time we have added memory-report, try it to see how memory-cheap Go apps can be.
Running from the Emperor¶
If you are running in Emperor mode, you can run uWSGI-Go apps by using the
privileged-binary-patch
option. Your vassal configuration should be
something like this.
[uwsgi]
socket = 127.0.0.1:3031
master = true
processes = 2
goroutines = 20
mules = 2
memory-report = true
uid = foobar
gid = foobar
privileged-binary-patch = /tmp/bin/helloworld
(Obviously change /tmp/bin/helloworld
to wherever your app lives...)
Notes¶
- A series of interesting go examples can be found in the
t/go
directory of the uWSGI source distribution. - Changing process names is currently not possible without modifying the go core
- You cannot use uWSGI native threads with Go (just use –goroutines)
- Only a little part of the uWSGI API has been exposed so far. If you want to hack on it or need more, just edit the uwsgi.go file in the plugins/go/src/uwsgi directory
- Goroutines require the async mode (if you are customizing your uWSGI library remember to always include it)
- It looks like it is possible to load the Python, Lua and PSGI plugins without problems even in goroutines mode (more tests needed)
发布说明¶
稳定版¶
uWSGI 2.0.9¶
[20141230]
Bugfixes¶
- fixed mod_proxy_uwsgi for non-blocking mode (many thanks to Joe cuchac)
- fixed master-fifo + cheaper
- fixed leak on error in bind_to_unix (Riccardo Magliocchetti)
- atexit hooks works in cheaped workers too
- atexit hooks works in gevent mode too during shutdown
- fixed carbon command line option value after reload
- do not honour Emperor throttling on the first run
- fixed Mono plugin
- fixed peer name in corerouters
- fixed stop signal for daemons
- varios ssl/tls fixes in https/spdy router
- fixed python3 –py-auto-reload-ignore
- fixed modifiers in corerouters
- support for yajl from homebrew (OSX)
- psgi: Ensure that we call any DESTROY hooks on psgix.harakiri.commit (Ævar Arnfjörð Bjarmason)
- systemdlogger: fix compilation with -Werror=format-security (Riccardo Magliocchetti)
- fixed unmasked websockets
- perl fixed latent refcounting bug (Mattia Barbon)
New Features¶
Improved PyPy support for Linux¶
The PyPy team have started building libpypy-c.so in their official releases. Now using pypy with uWSGI should be way easier:
Fastrouter post-buffering¶
The fastrouter got post-buffering:
http://uwsgi-docs.readthedocs.org/en/latest/Fastrouter.html#post-buffering-mode-uwsgi-2-0-9
Perl uwsgi::opt¶
The psgi/perl plugin exposes the uwsgi::opt hash, reporting the whole instance key-value configuration
–pull-header¶
This is like –collect-header but the collected header is not returned to the client
active-workers signal target¶
This is like the ‘workers’ target, but forward the signal only to non-cheaper workers
httpdumb routing action¶
The http internal router exposes a new mode called ‘httpdumb’ that does not change headers before forwarding the request
uWSGI 2.0.8¶
Note: this is the first version with disabled-by-default SSL3, if you need it, you can re-enable with --ssl-enable3
option
Bugfixes¶
- fixed PHP SCRIPT_NAME usage when
--php-app
is in place - allow “appendn” hook without second argument
- fix heap corruption in the Carbon plugin (credits: Nigel Heron)
- fix getifaddrs() memory management
- fixed tcsetattr() usage
- fixed kevent usage of return value (credits: Adriano Di Luzio)
- ensure PSGI response headers are in the right format
- fixed reloading of attached daemons
- fixed SSL/TLS shutdown
- fixed mountpoint logic for paths not ending with / (credits: Adriano Di Luzio)
- fixed Python3 support in spooler decorators (credits: Adriano Di Luzio)
New Features¶
RTSP and chunked input backports from 2.1 for the HTTP router¶
The --http-manage-rtsp
and ``–http-chunked-input` have been backported from 2.1 allowing the HTTP router
to detect RTSP and chunked requests automatically. This is useful for the upcoming https://github.com/unbit/uwsgi-realtime plugin.
–hook-post-fork¶
This custom hook allows you to call actions after each fork().
fallback to trollius for asyncio plugin¶
If you build the asyncio plugin for python2, a fallback to the trollius module will be tried.
This feature has gotten basically zero test coverage, so every report (bug or success alike) is welcome.
added sweep_on_full, clear_on_full and no_expire to --cache2
¶
Three new options for --cache2
have been added for improving the caching expire strategies:
sweep_on_full
will call a sweep (delete all of the expired items) as soon as the cache became fullclear_on_full
will completely clear the cache as soon as it is fullno_expire
forces the cache to not generate a cache sweeper thread, delegating items removal to the two previous options
backported wait-for-fs/mountpoints from 2.1¶
--wait-for-fs <path>
suspend the uWSGI startup until a file/directory is available--wait-for-file <path>
suspend the uWSGI startup until a file is available--wait-for-dir <path>
suspend the uWSGI startup until a directory is available--wait-for-mountpoint <path>
suspend the uWSGI startup until a mountpoint is available
improved the offload api (backport from 2.1)¶
uWSGI 2.0.8 is compatible with the upcoming https://github.com/unbit/uwsgi-realtime plugin that allows the use of realtime features (like websockets or audio/video streaming) using the uWSGI offload engine + Redis publish/subscribe.
Allows building plugins from remote sources as embedded¶
The UWSGI_EMBED_PLUGINS environment variable has been extended to support remote plugins. As an example you can build a monolithic uwsgi binary with the Avahi and realtime plugins as:
UWSGI_EMBED_PLUGINS="avahi=https://github.com/20tab/uwsgi-avahi,realtime=https://github.com/unbit/uwsgi-realtime" make
Automatically manage HTTP_X_FORWARDED_PROTO¶
Albeit a new standard is avavailble in the HTTP world for forwarded sessions (http://tools.ietf.org/html/rfc7239) this release adds support for the X-Forwarded-Proto header, automatically setting the request scheme accordingly.
Availability¶
uWSGI 2.0.8 has been released on 20141026. Download it from:
uWSGI 2.0.7¶
Changelog [20140905]
Bugfixes¶
- fixed counters in Statsd plugin (Joshua C. Forest)
- fixed caching in PHP plugin (Andrew Bevitt)
- fixed management of system users starting with a number
- fixed request body readline using memmove instead of memcpy (Andrew Wason)
- ignore “user” namespace in setns (still a source of problems)
- fixed Python3 RPC bytes/string mess (result: we support both)
- do not destroy the Emperor on failed mount hooks
- fixed symbol lookup error in the Mono plugin on OS X (Ventero)
- fixed FastCGI and SCGI protocols error when out of buffer happens
- fixed Solaris/SmartOS I/O management
- fixed two memory leaks in the RPC subsystem (Riccardo Magliocchetti)
- fixed the Rados plugin’s PUT method (Martin Mlynář)
- fixed multiple Python mountpoints with multiple threads in cow mode
- stats UNIX socket is now deleted by vacuum
- fixed off-by-one corruption in cache LRU mode
- force single-CPU build in Cygwin (Guido Notari)
New Features and improvements¶
Allow calling the spooler from every CPython context¶
At Europython 2014, Ultrabug (an uWSGI contributor and packager) asked for the possibility to spool tasks directly from a greenlet.
Done.
store_delete cache2 option¶
Author: goir
The store_delete flag of the –cache2 option allows you to force the cache engine to automatically remove invalid backing store files instead of steadfastly refusing to launch.
file logger rotation¶
Author: Riccardo Magliocchetti
The file logger has been extended to allow the use of rotation (the same system used by the non-pluggable –logto).
https://github.com/unbit/uwsgi/commit/0324e5965c360dccfb873ffe351dec88ddab59c5
Vassal plugin hooks¶
The plugin hook API has been extended with two new hooks: vassal and vassal_before_exec.
They allow customizing a vassal soon after its process has been created.
The first third-party plugin using it is ‘apparmor’: https://github.com/unbit/uwsgi-apparmor
This allows you to apply an Apparmor profile to a vassal.
Broodlord improvements¶
The Broodlord subsystem has been improved with a new option: –vassal-sos that automatically ask for reinforcement when all of the workers of an instance are busy.
In addition to this a sysadmin can now manually ask for reinforcement sending the ‘B’ command to the master FIFO of an instance.
Availability¶
uWSGI 2.0.7 has been released on 20140905, and you can download it from
uWSGI 2.0.6¶
Changelog [20140701]
Bugfixes¶
- fixed a memory leak in the subscription system
- fixed shortcut for ssl-socket
- fixed Apache2 mod_proxy_uwsgi. It is now considered stable with all Apache MPM engines.
- fixed SCRIPT_NAME and PATH_TRANSLATED generation in the PHP plugin (thanks Matthijs Kooijman)
- remove the old FIFO socket from the event queue when recreating it (thanks Marko Tiikkaja)
New features¶
The new Rados plugins¶
Credits: Marcin Deranek
The Rados plugin has been improved and stabilized, and now it is considered stable and usable in production.
Async modes and multithreading correctly work.
Support for uploading objects (via PUT) and creating new pools (MKCOL) has been added.
Expect WebDAV support in uWSGI 2.1.
Docs have been updated: http://uwsgi-docs.readthedocs.org/en/latest/Rados.html
–if-hostname¶
This is configuration logic for including options only when the system’s hostname matches a given value.
[uwsgi]
if-hostname = node1.local
socket = /tmp/socket1.socket
endif =
if-hostname = node2.local
socket = /var/run/foo.socket
endif =
Apache2 mod_proxy_uwsgi stabilization¶
After literally years of bug reports and corrupted data and other general badness, mod_proxy_uwsgi is finally stable.
On modern Apache2 releases it supports UNIX sockets too.
Updated docs: http://uwsgi-docs.readthedocs.org/en/latest/Apache.html#mod-proxy-uwsgi
uwsgi[rsize] routing var¶
The new uwsgi[rsize] routing variable (meaningful only in the ‘final’ chain) exposes the response size of the request.
the callint scheme¶
This scheme allows you to generate blobs from functions exposed by your uWSGI instance:
[uwsgi]
uid = @(callint://get_my_uid)
gid = @(callint://get_my_gid)
–fastrouter-fallback-on-no-key¶
The corerouter’s fallback procedure requires that a valid key (domain name) has been requested.
This option forces the various routers to trigger the fallback procedure even if a key has not been found.
PHP 5.5 opcode caching via –php-sapi-name¶
For mysterious reasons the PHP 5.5+’s opcode caching is not enabled in the “embed” SAPI. This option allows you to fake the SAPI name – apache is a good option – to force the opcode caching engine to turn on.
Improved chain-reloading¶
Thanks to Marko Tiikkaja, the chain reloading procedure correctly works in cheaper modes and is more verbose.
added ‘chdir’ keyval to –attach-daemon2¶
You can now set where attached daemons need to chdir().
uWSGI 2.0.5¶
Changelog [20140601]
Bugfixes¶
- fixed support for repeated headers in the Lua plugin (Credits: tizoc)
- fixed support for embedding configuration in OpenBSD and NetBSD
- various fixes in the cURL-based plugins (Credits: Yu Zhao)
- fixed milliseconds-based waits
- fixed sharedarea’s poller
- fixed the JSON encoder in the stats server
- fixed FastCGI parser and implemented EOF management (Credits: Jeff Trawick)
- improved fast on-demand mode
- avg_rt computation is excluded for static files
- fixed variable support in uwsgi internal router
- fixed websockets + keepalive ordering
- disable SIGPIPE management in coroutine-based loop engines
- fixed 64-bit sharedarea management in 32-bit systems
- honor chmod/chown-socket in fd0 mode
- hack to avoid Safari on iOS making a mess with keepalive
- fixed log setup when both –logto and –log2 are used (Credits: Łukasz Mierzwa)
- fixed mule_get_msg EAGAIN
- signal_pidfile returns the right error code
- fixed asyncio on OSX
New features¶
graceful reload of mule processes (Credits: Paul Egan)¶
SIGHUP is now sent to mules instead of directly killing them. You are free to trap/catch the signal in your code. If a mule does not die in the allowed “mercy time” (–mule-reload-mercy, default 60 seconds), SIGKILL will be sent.
return routing action (Credits: Yu Zhao)¶
This new action will allow users to write simplified “break” clauses.
For example, “return:403” is equivalent to “break:403 Forbidden”, with response body “Forbidden”.
The response body is quite useful for telling end users what’s going wrong.
–emperor-no-blacklist¶
This new option completely disables the Emperor’s blacklisting subsystem.
Icecast2 protocol helpers¶
One of the upcoming unbit.com projects is a uWSGI based audio/video streaming server.
The plugin (should be released during Europython 2014) already supports the Icecast2 protocol.
A bunch of patches have been added to the HTTP router to support the Icecast2 protocol.
For example the --http-manage-source
option allows the HTTP router to honor SOURCE method requests, automatically placing them in raw mode.
–metrics-no-cores, –stats-no-cores, –stats-no-metrics¶
When you have hundreds (or thousands) of async cores, exposing metrics for them may get really slow.
Three new options have been added allowing you to disable the generation of core-related metrics and consequently their usage in the stats server.
UWSGI_GO_CHEAP_CODE¶
This exit code (15) can be raised by a worker to tell the master to not respawn it.
PROXY1 support for the http router (Credits: bgglenn)¶
The option --http-enable-proxy-protocol
allows the HTTP router to understand PROXY1 protocol requests, such as those made by Haproxy or Amazon Elastic Load Balancer (ELB).
reset_after_push for metrics (Credits: Babacar Tall)¶
This metric attribute ensures that the metric value is reset to 0 (or its hardcoded initial_value) after the metric is pushed to external systems (such as Carbon or StatsD).
setremoteaddr¶
This new routing action allows you to completely override the REMOTE_ADDR detected by protocol handlers:
[uwsgi]
; treat all requests as local
route-run = setremoteaddr:127.0.0.1
the resolve option¶
There are uWSGI options (or plugins) that do not automatically resolve DNS names to IP addresses. This option allows you to map a placeholder to the DNS resolution result of a string:
[uwsgi]
; place the dns resolution of 'example.com' in the 'myserver' placeholder
resolve = myserver=example.com
; %(myserver) would now be 93.184.216.119
subscribe2 = server=%(myserver),key=foobar
Availability¶
uWSGI 2.0.5 has been released on [20140601] and can be downloaded from:
uWSGI 2.0.4¶
Changelog [20140422]
Bugfixes¶
- fixed “mime” routing var (Steve Stagg)
- allow duplicate headers in http parsers
- faster on_demand Emperor management
- fixed UWSGI_ADDITIONAL_SOURCES build option
- merge duplicated headers when SPDY is enabled (Łukasz Mierzwa)
- fixed segfault for unnamed loggers
- –need-app works in lazy-apps mode
- fixed fatal hooks management
New features¶
The experimental asyncio loop engine (CPython >= 3.4)¶
asyncio (also known as ‘tulip’) is the new infrastructure for writing non-blocking/async/callback-based code with Python 3.
This (experimental) plugin allows you to use asyncio as the uWSGI loop engine.
Docs: http://uwsgi-docs.readthedocs.org/en/latest/asyncio.html
httprouter advanced timeout management¶
The HTTP router learned 2 new specific timeouts:
- –http-headers-timeout <n>: defines the timeout while waiting for http headers
- –http-connect-timeout <n>: defines the timeout when connecting to backend instances
These should help sysadmins to improve security and availability.
Credits: Łukasz Mierzwa
allow disabling cache warnings in –cache2¶
Author: Łukasz Mierzwa
The ‘ignore_full’ keyval option has been added to cache2. This will disable warnings when a cache is full.
purge LRU cache feature by Yu Zhao (getcwd)¶
This new mode allows you to configure a cache to automatically expire the least recently used (LRU) items to make space when it’s running out.
Just add purge_lru=1 into your cache2 directive.
support embedded config on FreeBSD¶
You can now embed configuration files into the binary also on FreeBSD systems:
http://uwsgi-docs.readthedocs.org/en/latest/Embed.html#step-2-embedding-the-config-file
RPC hook¶
Two new hooks have been added:
- ‘rpc’ -> call the specified RPC function (fails on error)
- ‘rpcretry’ -> call the specified RPC function (retries on error)
setmodifier1 and setmodifier2 routing actions¶
Having to load the ‘uwsgi’ routing plugin to simply set modifiers was really annoying.
These two new routing options allow you to dynamically set request modifiers.
no_headers option for static router¶
keyval based static routing actions can now avoid rewriting response headers (useful for X-Sendfile), just add no_headers=1 to your keyval options.
Availability¶
uWSGI 2.0.4 has been released on 20140422, you can download it from:
uWSGI 2.0.3¶
Changelog 20140317
Bugfixes¶
- fixed spooler ‘at’ key usage
- fixed a memory and fd leak with on-demand Emperor sokets
- on __APPLE__ use LOG_NOTICE for syslog plugin
- fixed mongrel2 support
- hack for avoiding libmongoclient to crash on broken cursor
- log alarm is now a uwsgi_log_verbose() wrapper
- fixed tuntap router memory corruption
- Set ECDHE curve independently from DHE parameters (Hynek Schlawack)
- do not wait for a whole Emperor cycle before checking for each waitpid
- fix a regression with caller() not indicating the starting *.psgi program (Ævar Arnfjörð Bjarmason)
New features¶
Emperor SIGWINCH and SIGURG¶
The Emperor now responds to two new signals:
SIGWINCH: force an emperor rescan of vassals
SIGURG: cleanup the Emperor states (for now it only clears its blacklist)
Building plugins on-the-fly from git repositories¶
You can now build plugins stored on git servers:
uwsgi --build-plugin https://github.com/unbit/uwsgi-bonjour
or
UWSGI_EMBED_PLUGINS="bonjour=https://github.com/unbit/uwsgi-bonjour" pip install uwsgi
uwsgi.add_var(key, value)¶
You can now set request variables direcly from your app, for better integration with the internal routing subsystem
my $app = sub {
uwsgi::add_var("newvar","newvalue");
return [200, ['Content-Type' => 'text/html'], ["Hello"]];
}
uwsgi --http-socket :9090 --psgi hello.pl --response-route-run "log:\${newvar}"
add_var has been implemented in the CPython and Perl plugins
‘disableheaders’ routing action¶
This new action disables the sending of response headers, independently by the current request state
Smarter Emperor on bad conditions¶
Now the Emperor completely destroys internal vassal-related structures when it is impossible to correctly kill a broken vassal (both for inconsistent Emperor state or for internal system problems)
Availability¶
You can download uWSGI 2.0.3 from: http://projects.unbit.it/downloads/uwsgi-2.0.3.tar.gz
uWSGI 2.0.2¶
Changelog 20140226
Bugfixes¶
- fixed python3 support on older compilers/libc
- allow starting in spooler-only mode
- fixed cache bitmap support and added test suite (credits: Danila Shtan)
- fixed ftime log var
- added async remote signal management
- fixed end-for and end-if
- fixed loop in internal-routing response chain
- fixed pypy execute_source usage
- logpipe: Don’t setsid() twice (credits: INADA Naoki)
New features and improvements¶
CGI plugin¶
The plugin has been improved to support streaming.
In addition to this the long-awaited async support is finally ready. Now you can have CGI concurrency without spawning a gazillion of expensive threads/processes
Check: Running CGI scripts on uWSGI
PSGI loading improvements¶
The PSGI loader now tries to use Plack::Util::load_psgi() function instead of simple eval. This addresses various inconsistences in the environment (like the double parsing/compilation/execution of psgi scripts).
If the Plack module is not available, a simple do-based code is used (very similar to load_psgi)
Many thanks to Ævar Arnfjörð Bjarmason of booking.com for having discovered the problem
Availability¶
uWSGI 2.0.2 can be downloaded from: http://projects.unbit.it/downloads/uwsgi-2.0.2.tar.gz
uWSGI 2.0.1¶
Changelog [20140209]
Bugfixes and improvements¶
- due to a wrong prototype declaration, building uWSGI without SSL resulted in a compilation bug. The issue has been fixed.
- a race condition preventing usage of a massive number of threads in the PyPy plugin has been fixed
- check for heartbeat status only if heartbeat subsystem has been enabled
- improved heartbeat code to support various corner cases
- improved psgi.input to support offset in read()
- fixed (and simplified) perl stacktrace usage
- fixed sni secured subscription
- CGI plugin does not require anymore that Status header is the first one (Andjelko Horvat)
- fixed CPython mule_msg_get timeout parsing
- allows embedding of config files via absolute paths
- fixed symcall rpc
- fixed a memory leak in CPython spooler api (xiaost)
- The –no-orphans hardening has been brought back (currently Linux-only)
- improved dotsplit router mode to reduce DOS risk
- sub-Emperor are now loyal by default
- fixed non-shared ruby 1.8.7 support
- fixed harakiri CPython tracebacker
- request vars are now correctly exposed by the stats server
- support log-master for logfile-chown
- improved legion reload
- fixed tuntap netmask
- fixed busyness plugin without metrics subsystem
New features¶
uWSGI 2.0 is a LTS branch, so do not expect too much new features. 2.0.1 is the first maintainance release, so you still get a bunch of them (mainly features not complete in 2.0)
Perl native Spooler support¶
Perl finally got full support for the Spooler subsystem. In 2.0 we added server support, in 2.0.1 we completed client support too.
use Data::Dumper;
uwsgi::spooler(sub {
my $env = shift;
print Dumper($env);
return uwsgi::SPOOL_OK;
});
uwsgi::spool({'foo' => 'bar', 'arg2' => 'test2'})
–alarm-backlog¶
Raise the specified alarm when the listen queue is full
[uwsgi]
alarm = myalarm cmd:mail -s 'ALARM ON QUEUE' admin@example.com
alarm-backlog = myalarm
–close-on-exec2¶
Credits: Kaarle Ritvanen
this flag applies CLOSE_ON_EXEC socket flag on all of the server socket. Use it if you do not want you request-generated processes to inherit the server file descriptor.
Note: –close-on-exec applies the flag on all of the sockets (client and server)
simple notifications subsystem¶
An annoying problem with subscriptions is that the client does not know if it has been correctly subscribed to the server.
The notification subsystem allows you to add to the subscription packet a datagram address (udp or unix) on which the server will send back messages (like successful subscription)
[uwsgi]
; enable the notification socket
notify-socket = /tmp/notify.socket
; pass it in subscriptions
subscription-notify-socket = /tmp/notify.socket
...
the notification subsystem is really generic. Expect more subsystem to use it in the future.
pid namespace for daemons (Linux only)¶
This is a Linux-only, epxerimental feature allowing you to spawn a daemon in a new pid namespace. This feature requires the master running as root.
Resubscriptions¶
The fastrouter and the http/https/spdy router now support “resubscription”.
You can specify a dgram address (udp or unix) on which all of the subscriptions request will be forwarded to (obviously changing the node address to the router one)
The system could be useful to build ‘federated’ setup:
[uwsgi]
fastrouter = 192.168.0.1:3031
fastrouter-subscription-server = 127.0.0.1:5000
fastrouter-resubscribe = 192.168.0.2:5000
with this setup the fastrouter on 192.168.0.2 will have all of the records of 192.168.0.1 with the destination set to 192.168.0.1:3031.
filesystem monitor api¶
The real-time filesystem notification api has been standardized and it is now usable by plugins. The prototype to register a monitor is:
struct uwsgi_fsmon *uwsgi_register_fsmon(char *path, void (*func) (struct uwsgi_fsmon *), void *data) {
it will register a monitor on “path” triggering the function “func” passing “data” as argument.
Remember, this is different from the “touch” api, that is poll-based and can only monitor files. (while fsmon can monitor directories too)
support for yajl 1.0¶
2.0 added support yajl JSON parser (version 2). 2.0.1 added support for 1.0 too
for-readline¶
a config-logic iterator that yield file lines:
[uwsgi]
for-readline = /etc/myenvs
env = %(_)
end-for =
%i and %j magic vars¶
%i -> returns the inode of the currently parsed file
%j -> returns hex representation of 32bit djb33x hashing of the currently parsed absolute filename
–inject-before and –inject-after¶
This two new options should make the config templating system complete for everyone.
They basically prepend and append ‘blobs’ to a config file.
Yeah, it sound a bit nonsense.
Check the following example:
header.xml:
<uwsgi>
<socket>:3031</socket>
footer.xml:
<master/>
</uwsgi>
and body.xml:
<processes>8</processes>
you can build a single config tree with:
uwsgi --show-config --inject-before header.xml --inject-after footer.xml --xml body.xml
this approach, albeit raw, allows you to use magic-vars in more advanced ways (as you have control on the context of the file using them)
Note: ordering is important, –inject-before and –inject-after must be specified before the relevant config option.
–http-server-name-as-http-host¶
Some Ruby/Rack middleware make a questionable check on SERVER_NAME/HTTP_HOST matching.
This flag allow the http router to map SERVER_NAME to HTTP_HOST automatically instead of instructing your uWSGI instances to do it.
better Emperor’s Ragnarok (shutdown procedure)¶
The ‘Ragnarok’ is the Emperor phase executed when you ask him to shutdown.
Before 2.0.1, this procedure simply send KILL to vassals to brutally destroy them.
The new Ragnarok is way more benevolent, asking vassals to gracefully shutdown.
The Emperor tolerance for vassals not shutting down can be tuned with –reload-mercy (default 30 seconds)
PyPy paste support¶
Two new options for PyPy plugin have been added for paste support:
–pypy-paste <config>
–pypy-ini-paste <ini>
they both maps 1:1 to the CPython variants, but contrary to it they automatically fix logging
Availability¶
You can download uWSGI 2.0.1 from: http://projects.unbit.it/downloads/uwsgi-2.0.1.tar.gz
uWSGI 2.0¶
Changelog [20131230]
Important changes¶
Dynamic options have been definitely removed as well as the broken_plugins directory
Bugfixes and improvements¶
- improved log rotation
- do not rely on unix signals to print request status during harakiri
- added magic vars for uid and gid
- various Lua fixes
- a tons of coverity-governed bugfixes made by Riccardo Magliocchetti
New features¶
–attach-daemon2¶
this is a keyval based option for configuring external daemons.
Updated docs are: Managing external daemons/services
Linux setns() support¶
One of the biggest improvements in uWSGI 1.9-2.0 has been the total support for Linux namespaces.
This last patch adds support for the setns() syscall.
This syscall allows a process to “attach” to a running namespace.
uWSGI instances can exposes their namespaces file descriptors (basically they are the files in /proc/self/ns) via a unix socket.
External instances connects to that unix socket and automatically enters the mapped namespace.
to spawn an instance in “namespace server mode”, you use the --setns-socket <addr>
option
uwsgi --setns-socket /var/run/ns.socket --unshare net,ipc,uts ...
to attach you simply use --setns <addr>
uwsgi --setns /var/run/ns.socket ...
Updated docs: Jailing your apps using Linux Namespaces
“private” hooks¶
When uWSGI runs your hooks, it verbosely print the whole hook action line. This could be a security problem in some scenario (for example when you run initial phases as root user but allows unprivileged access to logs).
Prefixing your action with a ‘!’ will suppress full logging:
[uwsgi]
hook-asap = !exec:my_secret_command
Support for yajl library (JSON parser)¶
Til now uWSGI only supported jansson as the json parser required for managing .js config files.
You can now use the yajl library (available in centos) as alternative JSON parser (will be automatically detected)
Perl spooler support¶
The perl/PSGI plugin can now be used as a spooler server:
uwsgi::spooler(sub {
my $args = shift;
print Dumper($args);
return -2;
});
The client part is still missing as we need to fix some internal api problem.
Expect it in 2.0.1 ;)
Gateways can drop privileges¶
Gateways (like http router, sslrouter, rawrouter, forkptyrouter ...) can now drop privileges independently by the master.
Currently only the http/https/spdy router exposes the new option (--http-uid/--http-gid
)
Subscriptions-governed SNI contexts¶
The subscription subsystem now supports 3 additional keys (you can set them with the –subscribe2 option):
sni_key
sni_cert
sni_ca
all of the takes a path to the relevant ssl files.
Check: SNI - Server Name Identification (virtual hosting for SSL nodes)
Availability¶
uWSGI 2.0 has been released on 20131230 and can be downloaded from:
uWSGI 1.9.21¶
Latest 1.9 before 2.0 (scheduled at December 30th 2013)
From now on, all of the releases will be -rc’s (no new features will be added)
A document describing notes for upgrades from the (extremely obsolete) 1.2 and 1.4 versions is on work.
This release includes a new simplified plugins builder subsystem directly embedded in the uWSGI binary.
A page reporting third plugins is available: uWSGI third party plugins (feel free to add yours)
And now....
Changelog [20131211]
Bugfixes¶
- croak if the psgi streamer fails
- allows building coroae on raspberrypi
- do not wait for write availability until strictly required
- avoid segfault when async mode api is called without async mode
- fixed plain (without suspend engine) async mode
- do not spit errors on non x86 timerfd_create
- support timerfd_create/timerfd_settime on __arm__
Optimizations¶
writev() for the first chunk¶
Inernally when the first response body is sent, uWSGI check if response headers have been sent too, and eventually send them with an additional write() call.
This new optimizations allows uWSGI to send both headers and the first body chunk with single writev() syscall.
If the writev() returns with an incomplete write on the second vector, the system will fallback to simple write().
use a single buffer for websockets outgoing packets¶
Before this patch every single websocket packet required to allocate a memory chunk.
This patch forces the reuse of a single dynamic buffer. For games this should result in a pretty good improvement in responsiveness.
New features¶
removed zeromq api¶
The zeromq api (a single function indeed) has been removed. Each plugin rquiring zeromq cam simply call zmq_init() insteadd of uwsgi_zeromq_init().
The mongrel2 support has been moved to a ‘mongrel2’ plugin.
To pair uWSGI with mongrel2 the same options as before can be used, just remember to load (and build) the mongrel2 plugin
report request data in writers and readers¶
every error when reading and writing to/from the client will report current request’s data.
This should simplify debugging a lot.
Modular logchunks management¶
The uWSGI api has been extended to allow plugins to define their log-request vars.
tmsecs and tmicros, werr, rerr, ioerr, var.XXX¶
6 new request logging variables are available:
tmsecs: report the current unix time in milliseconds
tmicros: report the current unix time in microseconds
werr: report the number of write errors for the current request
rerr: report the number of read errors for the current request
ioerr: the sum of werr and rerr
var.XXX: report the context of the request var XXX (like var.PATH_INFO)
mountpoints and mules support for symcall¶
The symcall plugin has been improved to support mules and mountpoints.
To run a C function in a mule just specify it as --mule=foobar()
when the mule finds an argument ending
with () it will consider it a function symbol.
read2 and wait_milliseconds async hooks¶
This two non-blocking hooks adds new capabilities to the non-blocking system.
The first one allows to wait on two file descriptors with the same call (currently implemented only in plain async mode)
The second one is used to have a millisecond resolution sleep. (this is currently used only by the sharedarea waiting system)
websockets binary messages¶
You can now send websocket binary message. Just use uwsgi.websocket_send_binary()
instead of uwsgi.websocket_send()
the ‘S’ master fifo command¶
Sending ‘S’ to the master fifo, enable/disable the sending of subscription packets
as-mule hook¶
this new custom hooks allows you to execute custom code in every mule:
[uwsgi]
hook-as-mule = exec:myscript.sh
...
accepting hook and improved chain reloading¶
The chain reloading subsystem has been improved to take in account when a worker is really ready to accept() requests.
This specific state is announced to the Emperor too.
Check this article for more infos: http://uwsgi-docs.readthedocs.org/en/latest/articles/TheArtOfGracefulReloading.html
–after-request-call¶
this option allows you to call specific C functions (in chains) after each request. While you should use the framework/interface features for this kind of job, sometimes it is not possibile to execute code after the logging phase. In such a case feel free to abuse this option.
error pages¶
Three new options allow the definition of custom error pages (html only):
--error-page-403 <file>
add an error page (html) for managed 403 response
--error-page-404 <file>
add an error page (html) for managed 404 response
--error-page-500 <file>
add an error page (html) for managed 500 response
Simplified plugins builder¶
Building uWSGI plugins is now super easy:
uwsgi --build-plugin <directory>
this option will create a sane environment based on the current binary (no need to fight with build profiles and #ifdef) and will build the plugin.
No external files (included uwsgi.h) are needed as the uWSGI binary embeds them.
TODO for 2.0¶
- implement websockets and sharedarea support in Lua
- complete sharedarea api for CPython, Perl, Ruby and PyPy
- implement read2 and wait_milliseconds hook in all of the available loop engines
Availability¶
uWSGI 1.9.21 has been released on December 11th 2013 and can be downloaded at:
uWSGI 1.9.20¶
Changelog [20131117]
First round of deprecations and removals for 2.0¶
- The Go plugin is now considered “broken” and has been moved away from the
plugins
directory. The new blessed way for running Go apps in uWSGI is using The GCCGO plugin plugin. - The
--auto-snapshot
option has been removed, advanced management of instances now happens via The Master FIFO. - The matheval support has been removed, while a generic “matheval” plugin (for internal routing) is available (but not compiled in by default). See below for the new way for making “math” in config files.
- The “erlang” and “pyerl” plugins are broken and has been moved out of the
plugins
directory. Erlang support will be completely rewritten after 2.0 release.
Next scheduled deprecations and removals¶
The ZeroMQ API (a single function indeed) will be removed. Each plugin using ZeroMQ will create its own zmq
context (no need to share it). This means libzmq will no more be linked in the uWSGI core binary.
Mongrel2 protocol support will be moved to a “mongrel2” plugin instead of being embedded in the core.
Bugfixes¶
- Fixed master hang when gracefully reloading in lazy mode.
- Fixed
default_app
usage. - Another round of coverity fixes by Riccardo Magliocchetti.
- Fixed
EAGAIN
management when reading the body.
New features¶
64bit return values for the RPC subsystem¶
Before this release every RPC response was limited to a size of 64k (16bit).
Now the RPC protocol automatically detects if more space is needed and can scale up to 64bit.
Another advantage of this approach is that only the required amount of memory per-response is allocated instead of blindly creating a 64k chunk every time.
The new GCCGO plugin¶
Check official docs: The GCCGO plugin
The plugin is in early stage of development but it’s already quite solid.
Simple math in configuration files¶
As seen before, we have removed matheval support in favor of a simplified interface:
For example, now you can automatically set the number of threads to:
[uwsgi]
; %k is a magic var translated to the number of cpu cores
threads = %(%k * 3)
...
(%k * 3
is number_of_cpu_cores * 3
).
New magic vars¶
%t
- Unix time (in seconds, gathered at instance startup).
%T
- Unix time (in microseconds, gathered at instance startup).
%k
- Number of detected CPU cores.
Perl/PSGI improvements¶
- The Chunked input API.
psgix.io
is aSocket::IO
object mapped to the connection file descriptor (you need to enable it with--psgi-enable-psgix-io
).uwsgi::rpc
anduwsgi::connection_fd
from the API.--plshell
will invoke an interactive shell (based onDevel::REPL
).
New native protocols: --https-socket
and --ssl-socket
¶
When built with SSL support, uWSGI exposes two new native socket protocols: HTTPS and uwsgi over SSL.
Both options take the following value: <addr>,<cert>,<key>[,ciphers,ca]
.
[uwsgi]
https-socket = :8443,foobar.crt,foobar.key
...
Currently none of the mainstream webservers support uwsgi over SSL, a patch for nginx will be sent for approval in the next few hours.
PROXY (version1) protocol support¶
Recently Amazon ELB added support for HAProxy PROXY (version 1) protocol support. This simple protocol allows the frontend to pass the real IP of the client to the backend.
Adding --enable-proxy-protocol
will force the --http-socket
to check for a PROXY protocol request for setting the REMOTE_ADDR
and REMOTE_PORT
fields.
New metrics collectors¶
avg
- Compute the math average of children:
--metric name=foobar,collector=avg,children=metric1;metric2
. accumulator
- Always add the value of the specified children to the final value.
multiplier
- Multiply the sum of the specified children for the value specified in
arg1n
.
Check The Metrics subsystem.
Availability¶
uWSGI 1.9.20 has been released on 20131117 and can be downloaded from http://projects.unbit.it/downloads/uwsgi-1.9.20.tar.gz.
uWSGI 1.9.19¶
Changelog [20131109]
This release starts the ‘hardening’ cycle for uWSGI 2.0 (scheduled for the end of december 2013).
The metrics subsystem was the last piece missing and this version (after 1 year of analysis) finally includes it.
During the following 2 months we will start deprecating features or plugins that got no-interest, are known to be broken or are simply superseed by more modern/advanced ones.
Currently the following plugin and features are scheduled for removal:
- The Go plugin, superseeded by the gccgo one. (eventually the Go plugin will be brought back if something changes in the fork() support)
- Auto-snapshotting, was never documented, it has tons of corner case bugs and it is huber-complex. The features added by the MasterFifo allows for better implementations of snapshotting.
Waiting for decision:
- the erlang plugin is extremely old, was badly engineered and should be completely rewritten. If you are a user of it, please contact the staff. Very probably we will not be able to maintain it without sponsorship.
- the matheval support could be removed soon (unless we find some specific use that could require it), substituted by some form of simple math directly implemented in the option parser
- the admin plugin should be substituted with something more advanced. An api for defining dynamic options is on-work
Bugfixes¶
- completely skip cgroups initialization when non-root
- tons of post-static_analysis fixes by Riccardo Magliocchetti
- fixed the greenlet plugin reference counting
- avoid kevent storm for stats pusher thread
- fixed rbtimers math
- both ‘cache’ and ‘file’ routers got a ‘no_content_length’ key option to avoid settign the Content-Length header
- the PyPy plugin automatically enables threads/GIL
- manage dot_segments in HTTP parser
- improved srand() usage
New features¶
The Metrics subsystem¶
This was the last piece missing before uWSGI 2.0. The Metrics subsystem allows you to store “numbers” related to monitoring, graphing and quality checks and exports them in various ways.
Official docs: The Metrics subsystem
The Tornado loop engine¶
While working on nodejs integration we realized that contrary to what we used to believe, Tornado (an asynchronous, callback based module for python) is usable in uWSGI.
Note: The plugin is not built-in by default
Official docs: The Tornado loop engine
The ‘puwsgi’ protocol¶
A “persistent” (keep-alive) version of the ‘uwsgi’ parser has been added named ‘puwsgi’ (persistent uwsgi).
This protocol works only for request without a body and requires support from the frontend. Its use is currently for custom clients/apps, there is no webserver handler supporting it.
The --puwsgi-socket <addr>
will bind a puwsgi socket to the specified address
–vassal-set¶
You can tell the Emperor to pass specific options to every vassal using the –set facility:
[uwsgi]
emperor = /etc/uwsgi/vassals
vassal-set = processes=8
vassal-set = enable-metrics=1
this will add --set processes=8
and --set enable-metrics=1
to each vassal
The ‘template’ transformation¶
This is a transformation allowing you to apply all of the internal routing patterns to your responses.
Take the following file (foo.html)
<html>
<head>
<title>Running on ${SERVER_NAME}</title>
</head>
<body>
Your ip address is: ${REMOTE_ADDR}<br/>
Served requests: ${metric[worker.0.requests]}<br/>
Pid: ${uwsgi[pid]}<br/>
A random UUID: ${uwsgi[uuid]}
</body>
</html>
we will apply the ‘template’ transformation to it:
[uwsgi]
http-socket = :9090
; enable the metrics subsystem
enable-metrics = true
; inject the route transformation
route-run = template:
; return a file (transformation will be applied to it)
route-run = file:filename=foo.html,no_content_length=1
everything available in the internal routing subsystem can be used into the template transformation.
Performance are stellar, so instead of old Server Side Includes, you may want to try it.
Not enough ? combine it with caching:
[uwsgi]
http-socket = :9090
; enable the metrics subsystem
enable-metrics = true
; load foo.html in the cache
cache2 = name=mycache,items=10
load-file-in-cache = foo.html
; inject the route transformation
route-run = template:
; return the cache item (transformation will be applied to it)
route-run = cache:key=foo.html,no_content_length=1
Again ?
what about chunked encoding ?
[uwsgi]
http-socket = :9090
; enable the metrics subsystem
enable-metrics = true
; load foo.html in the cache
cache2 = name=mycache,items=10
load-file-in-cache = foo.html
; inject the route transformation
route-run = template:
; inject chunked encoding
route-run = chunked:
; return the cache item (transformation will be applied to it)
route-run = cache:key=foo.html,no_content_length=1
or gzip ?
[uwsgi]
http-socket = :9090
; enable the metrics subsystem
enable-metrics = true
; load foo.html in the cache
cache2 = name=mycache,items=10
load-file-in-cache = foo.html
; inject the route transformation
route-run = template:
; inject gzip
route-run = gzip:
; return the cache item (transformation will be applied to it)
route-run = cache:key=foo.html,no_content_length=1
Availability¶
uWSGI 1.9.19 has been released on 20131109, you can download it from:
uWSGI 1.9.18¶
Changelog [20131011]
License change¶
This version of uWSGI is the first of the 1.9 tree using GPL2 + linking exception instead of plain GPL2.
This new license should avoid any problems when using uWSGI as a shared library (or when linking it with non-GPL2 compatible libraries)
Remember: if you need to make closed-source modifications to uWSGI you can buy a commercial license.
Bugfixes¶
- fixed uwsgi native protocol support on big endian machines
- fixed jvm build system for arm (Jorge Gallegos)
- fixed a memleak spotted by cppcheck in zlib management
- chdir() at every emperor glob iteration
- correctly honour –force-cwd
- fixed ia64/Linux compilation (Jonas Smedegaard/Riccardo Magliocchetti)
- fixed ruby rvm paths parsing order
- added waitpid() after daemon’s SIGTERM (Łukasz Mierzwa)
- fixed pid numbering after –idle (Łukasz Mierzwa)
- fixed/improved cheaper memory limits (Łukasz Mierzwa)
- correctly close inherited sockets in gateways
- fix checks for MAP_FAILED in mmap() (instead of NULL)
- fixed FastCGI non-blocking body read() (patch by Arkaitz Jimenez)
- fixed attach.py script
- avoid crashing on non-conformant PSGI response headers
- run the python autoreloader even in non-apps mode when non-lazy
New Features¶
Minimal build profiles¶
Albeit the memory usage of the uWSGI core is generally between 1.8 and 2.5 megs, there are use cases in which you want an even minimal core and set of embedded plugins.
Examples are users not making use of uWSGI specific features, or cases in which the libraries used by uWSGI nameclash with others (like openssl or zeromq).
A bunch of ‘minimal’ build profiles have been added:
- pyonly (build a minimal CPython WSGI server)
- pypyonly (build a minimal PyPy WSGI server)
- plonly (build a minimal PSGI server)
- rbonly (build a minimal Rack server)
the only supported configuration format is .ini and internal routing and legion subsystem are not builtin.
For example if you want to install a minimal uWSGI binary via pip:
UWSGI_PROFILE=pyonly pip install uwsgi
IMPORTANT: minimal build profiles do not improve performance, for the way uWSGI is designed, unused features do not waste CPU. Minimal build profiles impact on final binary size only
Auto-fix modifier1¶
Setting the modifier1 for non-python plugin is pretty annoying (read: you always forget about it).
Now if the modifier1 of the request is zero, but the python plugin is not loaded (or there are no python apps loaded) the first configured app will be set instead (unless you disable with feature with –no-default-app).
This means you can now run:
uwsgi --http-socket :9090 --psgi myapp.pl
instead of
uwsgi --http-socket :9090 --http-socket-modifier1 5 --psgi myapp.pl
obviously try to always set the modifier1, this is only a handy hack
Perl auto reloader¶
The –perl-auto-reload option allows the psgi plugin to check for changed modules after every request. It takes the frequency (in seconds) of the scan.
The scan happens after a request has been served. It is suboptimal, but it is the safest choice too.
The “raw” mode (preview technology, only for CPython)¶
While working on a new server-side project in Unbit we had the need to expose our web application using a very specific protocol (none of the ones supported by uWSGI).
Our first way was adding the new protocol as a plugin, but soon we realize that is was too-specific. So we decided to introduce the RAW mode.
Raw mode allows you to directly parse the request in your application callable. Instead of getting a list of CGI vars/headers in your callable you only get the file descriptor soon after accept().
You can then read()/write() to that file descriptor in full freedom.
import os
def application(fd):
os.write(fd, "Hello World")
uwsgi --raw-socket :7070 --python-raw yourapp.py
Raw mode disables request logging. We currently support it only for CPython, if we get reports (or interest) about it for the other languages we will add support for sure.
IMPORTANT: raw mode is not a standard, so do not expect any middleware or common usage patterns will apply. Use it as a low-level socket wrapper.
Optional NON-standard support for CPython buffer protocol for WSGI responses¶
Authors: yihuang with help of INADA Naoki (methane)
The WSGI (PEP333/3333) is pretty clear about the type of valid objects for responses: str for python2, bytes for python3
uWSGI (heavily using mod_wsgi as a reference) always enforce such behaviour, so “exotic” patterns like returning bytearray where not supported. Such uses are somewhat involuntary supported on pure-python application servers, just because they simply call write() over them or because they cast them to string before returning (very inefficient)
The patch proposed by yihuang suggests the use of the low-level buffer protocol exposed by the CPython C api. Strings (in python2) and bytes (in python3) support the buffer protocol, so its use is transparent and backward compatibility is granted too. (for the CPython C api experts: yes we support both old and new buffer protocol)
This is a NON-standard behaviour you have to voluntary enable with –wsgi-accept-buffer.
Use with care as it could mask errors and/or wrong behaviours.
Note: if you tried 1.9.18-dev you may note this option was enabled by default. It was an error. Thanks to Graham Dumpleton (mod_wsgi author) for pointing it out.
Emperor and config improvements¶
Credits: Matthijs Kooijman
The config system has been improved to be even more consistent in respect to strict mode (remainder: with –strict you basically check your config files for unknown options avoiding headaches caused by typos).
New magic vars have been added exposing the name of the original config file (this simplify templating when in Emperor mode), check them at https://github.com/unbit/uwsgi-docs/blob/master/Configuration.rst#magic-variables
The Emperor got support for Linux capabilities using the –emperor-cap option. The option takes the list of capability you want to maintain for your vassals when they start as root:
[uwsgi]
emperor = /etc/uwsgi/vassals
emperor-cap = setuid,net_bind_service
with this setup your vassal will be only able to drop privileges and bind to ports < 1024
Its best friend is the CLONE_NEWUSER flag of linux namespaces that is now fully supported on uWSGI:
[uwsgi]
emperor = /etc/uwsgi/vassals
emperor-use-clone = user
emperor-cap = setuid,net_bind_service
this will create a new root user for the vassal with fewer privileges (CLONE_NEWUSER is pretty hard to understand, but the best thing to catch it is seeing it as a new root user with dedicated capabilities)
Build system improvements¶
The build system has been improved to link custom sources on the fly. This works great for low-level hooks:
// embed_me.c
#include <stdio.h>
void hello_i_am_foobar() {
printf("I Am foobar");
}
Now we can link this file to the main uWSGI binary in one shot:
UWSGI_ADDITIONAL_SOURCES=embed_me.c make
and you will automatically get access for your hooks:
uwsgi --http-socket :9090 --call-asap hello_i_am_foobar
Finally, Riccardo Magliocchetti rewrote the build script to use optparse instead of raw/old-fashioned sys.argv parsing
Pluginized the ‘schemes’ management¶
schemes are the prefix part of uWSGI uri’s. When you do
uwsgi --ini http://foobar.local:9090/test.ini
the http:// is the scheme, signalling uWSGI it has to download the config file via http.
Til now those ‘schemes’ were hardcoded. Now they are exposed as plugins, so you can add more of them (or override the default one).
The new system has been applied to the PSGI plugin too (sorry we are sure only perl developers will understand that kind of poetry :P) so you can do things like:
uwsgi --http-socket :1717 --psgi http://yourapps.local/dancer.pl
or
./uwsgi --binary-append-data yourapp.pl > blob001
cat blob001 >> ./uwsgi
./uwsgi --http-socket :1717 --psgi data://0
mountpoints checks¶
It could be hard to understand why an application server should check for mountpoints.
In the same way understanding how writing filesystem in userspace was silly few years ago.
So, check the article about managing Fuse filesystem with uWSGI: http://uwsgi-docs.readthedocs.org/en/latest/tutorials/ReliableFuse.html
Preliminary libffi plugin¶
As embedding c libraries for exposing hooks is becoming more common, we have started working on libffi integration, allowing safe (and sane) argument passing to hooks. More to came soon.
Official support for kFreeBSD¶
Debian/kFreeBSD is officially supported.
You can even use FreeBSD jails too !!!
Availability¶
uWSGI 1.9.18 has been released on October 11th 2013 and can be downloaded from:
uWSGI 1.9.17¶
Changelog [20130917]
Bugfixes¶
- the ‘pty’ client is now blocking (safer approach)
- removed strtok() usage (substituted by a new uwsgi api function on top of strtok_r() )
- fixed –pty-exec (Credits: C Anthony Risinger)
- listen_queue/somaxconn linux check is now done even for UNIX sockets
New features¶
The Master FIFO¶
This is a new management way in addition to UNIX signals. As we have no more free signals to use (and generally dealing with signals and pidfiles is not very funny), all of the new management features of uWSGI will be based on the master fifo.
Docs are already available: The Master FIFO
The asap hook¶
Credits: Matthijs Kooijman
a new hook, named ‘asap’ has been added. It will be run soon after the options are parsed.
Check: Hooks
The TCC (libtcc) plugin¶
TCC is an embeddable c compilers. It includes a shared library (libtcc) you can use to compile strings of c code on the fly.
The libtcc uWSGI plugins allows compiling strings of c to process symbols. CUrrently the “tcc” hook engine has been implemented:
[uwsgi]
hook-asap = tcc:mkdir("/var/run/sockets");printf("directory created\n");
hook-as-user = tcc:printf("i am process with pid %d\n", getpid());
hook-post-app = tcc:if (getenv("DESTROY_THE_WORLD")) exit(1);
http-socket = /var/run/sockets/foobar.sock
The forkptyrouter gateway¶
While work on Linux containers/namespaces continues to improve we have added this special router/gateway allowing dynamic allocation of pseodoterminals in uWSGI instances. To access the sockets created by the forkptyrouter you can use the –pty-connect option exposed by the ‘pty’ plugin.
Documention is being worked on.
added a new magic var for ANSI escaping¶
The %[ magic var has been added, it allows you to define ANSI sequences in your logs.
If you like coloured logs:
log-encoder = format %[[33m${msgnl}%[[0m
Routable log encoders¶
You can now attach log encoders to specific log routes:
[uwsgi]
logger = stderr file:/dev/tty
log-route = stderr ubuntu
log-route = stderr clock
print = %[[34mHELLO%[[0m
; add an encoder to the 'stderr' logger
log-encoder = format:stderr %[[33m${msgnl}%[[0m
http-socket = :9090
–vassals-include¶
Credits: Matthijs Kooijman
This is like –vassal-inherit but the parsing will be “immediate” (so you can use placeholders)
The Emperor heartbeat system is now mercyless...¶
The old approach for the heartbeat Emperor subsystem was asking for “gentle” reload to bad vassals.
Now vassals not sending heartbeat (after being registered with the heartbeat subsystem) are killed with -9
The result of this patch will be more robust bad vassals management
logpipe¶
Author: INADA Naoki
You can now send loglines to the stdin of an external command:
req-logger = pipe:/usr/local/bin/mylogger
added “fd” logger to “logfile” plugin¶
you can directly send logs to a file descriptors:
req-logger = fd:17
uWSGI 1.9.16¶
Changelog [20130914]
Important change in the gevent plugin shutdown/reload procedure !!!¶
The shutdown/reload phase when in gevent mode has been changed to better integrate with multithreaded (and multigreenlet) environments (most notably the newrelic agent).
Instead of “joining” the gevent hub, a new “dummy” greenlet is spawned and “joined”.
During shutdown only the greenlets spawned by uWSGI are taken in account, and after all of them are destroyed the process will exit. This is different from the old approach where the process wait for ALL the currently available greenlets (and monkeypatched threads).
If you prefer the old behaviout just specify the option –gevent-wait-for-hub
Bugfixes/Improvements¶
- fixed CPython reference counting bug in rpc and signal handlers
- improved smart-attach-daemon for slow processes
- follow Rack specifications for QUERY_STRING,SCRIPT_NAME,SERVER_NAME and SERVER_PORT
- report missing internal routing support (it is only a warning when libpcre is missing)
- better ipcsem support during shutdown and zerg mode (added –persistent-ipcsem as special case)
- fixed fastcgi bug exposed by apache mod_fastcgi
- do not call pre-jail hook on reload
- force linking with -lrt on solaris
- report thunder lock status
- allow custom priority in rsyslog plugin
New features¶
FreeBSD jails native support¶
uWSGI got nativr FreeBSD jails support. Official documentation is here FreeBSD Jails
The Rados plugin¶
Author: Javier Guerra
Based on the The GlusterFS plugin plugin, a new one allowing access to Rados object storage is available:
The TunTap router¶
This new gateway is the result of tons of headaches while trying to build better (read: solid) infrastructures with Linux namespaces.
While dealing with uts, ipc, pid and filesystem namespaces is pretty handy, managing networking is a real pain.
We introduced lot of workaroud in uWSGI 1.9.15 (expecially for simplify the veth management) but finally we realized that those systems do not scale in terms of management.
The TunTap router tries to solve the issue moving the networking part of jailed vassals in user space.
Basically each vassal create one or more tuntap devices. This devices are connected (via a unix socket) to the “tuntap router” allowing access from the vassal to the external network.
That means a single network interface in the main namespace and one for each vassal.
The performance are already quite good (we are only losing about 10% in respect of kernel-level routing) but can be optimized.
In addition to this the tuntap router has a simple userspace firewall you can use to manage complex routing rules.
Documentation is still in progress, but you can configure a tuntap router following the big comment on top of this file:
https://github.com/unbit/uwsgi/blob/master/plugins/tuntap/tuntap.c
while you can connect to it with --tuntap-device <dev> <socket>
where <dev> is the tuntap device to create in the vassal/client and <socket> is the unix address
of the tuntap router
An Example Emperor
[uwsgi]
tuntap-router = emperor0 /tmp/tuntap.socket
exec-as-root = ifconfig emperor0 192.168.0.1 netmask 255.255.255.0 up
exec-as-root = iptables -t nat -F
exec-as-root = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
exec-as-root = echo 1 >/proc/sys/net/ipv4/ip_forward
emperor-use-clone = ipc,uts,fs,pid,net
emperor = /etc/vassals
and one of its vassals:
[uwsgi]
tuntap-device = uwsgi0 /tmp/tuntap.socket
exec-as-root = ifconfig lo up
exec-as-root = ifconfig uwsgi0 192.168.0.2 netmask 255.255.255.0 up
exec-as-root = route add default gw 192.168.0.1
exec-as-root = hostname foobar
socket = /var/www/foobar.socket
psgi-file = foobar.pl
Linux O_TMPFILE¶
Latest Linux kernel support a new operational mode for opening files: O_TMPFILE
this flag open a temporary file (read: unlinked) without any kind of race conditions.
This mode is automatically used if available (no options needed)
Linux pivot-root¶
When dealing with Linux namespaces, changing the root filesystem is one of the main task.
chroot() is generally too simple, while pivot-root allows you more advanced setup
The syntax is --pivot-root <new_root> <old_root>
Cheaper memlimit¶
Author: Łukasz Mierzwa
This new check allows control of dynamic process spawning based on the RSS usage:
http://uwsgi-docs.readthedocs.org/en/latest/Cheaper.html#setting-memory-limits
Log encoders¶
There are dozens of log engines and storage system nowadays. The original uWSGI approach was developing a plugin for every engine.
While working with logstash and fluentd we realized that most of the logging pluging are reimplementations of teh same concept over and over again.
We followed an even more modular approach introducing log encoders:
They are basically patterns you can apply to each logline
New “advanced” Hooks¶
A new series of hooks for developers needing little modifications to the uWSGI cores are available.
Documention about the whole hooks subsystem is now available (it is a work in progress):
Availability¶
uWSGI 1.9.16 has been released on September 14th 2013. You can download it from:
uWSGI 1.9.15¶
Changelog [20130829]
Bugfixes¶
- fixed jvm options hashmap (#364)
- fixed python3 wsgi.file_wrapper
- fixed python3 –catch-exceptions
- fixed type in pypy wsgi.input.read
- better symbol detection for pypy
- improved ruby libraries management on heroku
- fixed http-keepalive memleak
- fixed spooler body management under CPython
- fixed unshare() usage of ‘fs’
- fixed UWSGI_PROFILE usage when building plugins with –plugin
- improved SmartOS support and added OmniOS support
New features¶
The PTY plugin¶
This new plugin allows you to generate pseudoterminals and attach them to your workers.
Pseudoterminals are then reachable via network (UNIX or TCP sockets).
You can use them for shared debugging or to have input channels on your webapps.
The plugin is in early stage of development (very few features) and it is not built in by default, but you can already make funny things like:
[uwsgi]
plugin = pty,rack
; generate a new pseudoterminal on port 5000 and map it to the first worker
pty-socket = 127.0.0.1:5000
; classic options
master = true
processes = 2
rack = myapp.ru
socket = /tmp/uwsgi.socket
; run a ruby interactive console (will use the pseudoterminal)
; we use pry as it kick asses
rbshell = require 'pry';binding.pry
now you can access the pseudoterminal with
uwsgi --plugin pty --pty-connect 127.0.0.1:5000
you can run the client in various windows, it will be shared by all of the peers (all will access the same pseudoterminal).
We are sure new funny uses for it will popup pretty soon
preliminary documentation is available at The Pty plugin
strict mode¶
One of the most common error when writing uWSGI config files, are typos in option names.
As you can add any option in uWSGI config files, the system will accept anythyng you will write even if it is not a real uWSGI option.
While this approach is very powerful and allow lot of funny hacks, it can causes lot of headaches too.
If you want to check all of your options in one step, you can now add the –strict option. Unknown options will trigger a fatal error.
fallback configs¶
Being very cheap (in term of resources) and supporting lot of operating systems and architectures, uWSGI is heavily used in embedded systems.
One of the common feature in such devices is the “reset to factory defaults”.
uWSGI now natively support this kind of operation, thanks to the –fallback-config option.
If a uWSGI instance dies with exit(1) and a fallback-config is specified, the binary will be re-exec()’d with the new config as the only argument.
Let’s see an example of a configuration with unbindable address (unprivileged user trying to bind to privileged port)
[uwsgi]
uid = 1000
gid = 1000
socket = :80
and a fallback one (bind to unprivileged port 8080)
[uwsgi]
uid = 1000
gid = 1000
socket = :8080
run it (as root, as we want to drop privileges):
sudo uwsgi --ini wrong.ini --fallback-config right.ini
you will get in your logs:
...
bind(): Permission denied [core/socket.c line 755]
Thu Aug 29 07:26:26 2013 - !!! /Users/roberta/uwsgi/uwsgi (pid: 12833) exited with status 1 !!!
Thu Aug 29 07:26:26 2013 - !!! Fallback config to right.ini !!!
[uWSGI] getting INI configuration from right.ini
*** Starting uWSGI 1.9.15-dev-4046f76 (64bit) on [Thu Aug 29 07:26:26 2013] ***
...
–perl-exec and –perl-exec-post-fork¶
You can now run custom perl code before and after the fork() calls.
Both options simply take the perl script as the argument
uwsgi.cache_keys([cache])¶
This api function has been added to the python and pypy plugins. It allows you to iterate the keys of a local uWSGI cache.
It returns a list.
added %(ftime) to logformat¶
this is like ‘ltime’ but honouring the –log-date format
protect destruction of UNIX sockets when another instance binds them¶
on startup uWSGI now get the inode of the just created unix socket.
On vacuum if the inode is changed the unlink of the socket is skipped.
This should help avoiding sysadmin destructive race conditions or misconfigurations
–worker-exec2¶
this is like –worker-exec but happens after post_fork hooks
allow post_fork hook on general plugins¶
general plugins (the ones without the .request hook) can now expose the .post_fork hook
–call hooks¶
In the same spirit of exec-* hooks, call hooks works in the same way but directly calling functions in the current process address space (they have to be exposed as valid symbols)
take this c source (call it hello.c):
#include <stdio.h>
void i_am_hello_world_for_uwsgi() {
printf("Hello World!!!\n");
}
and compile it as a shared library:
gcc -o libhello.so -shared -fPIC hello.c
now choose when (and where) to call it in uWSGI:
./uwsgi --help | grep -- --call-
--call-pre-jail call the specified function before jailing
--call-post-jail call the specified function after jailing
--call-in-jail call the specified function in jail after initialization
--call-as-root call the specified function before privileges drop
--call-as-user call the specified function after privileges drop
--call-as-user-atexit call the specified function before app exit and reload
--call-pre-app call the specified function before app loading
--call-post-app call the specified function after app loading
--call-as-vassal call the specified function() before exec()ing the vassal
--call-as-vassal1 call the specified function(char *) before exec()ing the vassal
--call-as-vassal3 call the specified function(char *, uid_t, gid_t) before exec()ing the vassal
--call-as-emperor call the specified function() in the emperor after the vassal has been started
--call-as-emperor1 call the specified function(char *) in the emperor after the vassal has been started
--call-as-emperor2 call the specified function(char *, pid_t) in the emperor after the vassal has been started
--call-as-emperor4 call the specified function(char *, pid_t, uid_t, gid_t) in the emperor after the vassal has been started
options ending with a number are variants expecting arguments (the suffix is the number of arguments they take)
we want to call our function just before our application is loaded:
[uwsgi]
; load the shared library
dlopen = ./libhello.so
; set the hook
call-pre-app = i_am_hello_world_for_uwsgi
...
your custom function will be called just before app loading.
Take in account those functions are called in the process address space, so you can make all sort of (black) magic with them.
Note: dlopen is a wrapper for the dlopen() function, so all the same rules apply (read: USE ABSOLUTE PATHS !!!)
init_func support for plugins, and –need-plugin variant¶
when loading a plugin you can call a symbol defined in it soon after dlopen():
uwsgi --plugin "foobar|myfunc" ...
uWSGI will call the ‘myfunc’ symbol exposed by the ‘foobar’ plugin
–need-plugin is like –plugin but will exit(1) the process if plugin loading fails
added commodity loader for the pecan framework¶
Author: Ryan Petrello
A new python loader (–pecan) has been added for the pecan WSGI framework
https://uwsgi-docs.readthedocs.org/en/latest/Python.html#pecan-support
UWSGI_REMOVE_INCLUDES¶
during the build phase you can remove include headers with the UWSGI_REMOVE_INCLUDES environment variable.
This is useful for cross-compilation where some automatically detected includes could be wrong
router_expires¶
We already have various options in the uWSGI core to set Expires header.
This router has been added to allow customizing them:
[uwsgi]
route = /^foobar1(.*)/ expires:filename=foo$1poo,value=30
route = /^foobar2(.*)/ expires:unix=${time[unix]},value=30
the router takes a filename mtime or a unix time, adds ‘value’ to it, and return it as an http date.
announce Legion’s death on reload/shutdown¶
Every legion member will now announce its death as soon as a reload (or a shutdown) of the instance is triggered
The GlusterFS plugin (beta)¶
This new plugin make use ot the new glusterfs c api, avoiding the overhead of fuse when serving files stored on glusterfs servers.
The plugin supports the multiprocess and multithreads modes, while async modes are currently in beta.
Documentation is available: The GlusterFS plugin
–force-gateway¶
all of the gateways (fastrouter, httprouter, rawrouter, sslrouter ...) has to be run under the master process.
By specifying –force-gateway, you will bypass this limit
preliminary python3 profiler (beta)¶
The –profiler pycall/pyline profilers have been added to python3. They are beta quality (they leaks memory), but should be usable.
file monitor support for OpenBSD,NetBSD,DragonFlyBSD¶
Both –fs-reload and the @fmon decorator now work on this operating systems.
–cwd¶
you can force the startup “current working directory” (used by –vacuum and the reloading subsystem) with this option.
It is useful in chroot setups where the binary executable change its place.
–add-gid¶
This options allows you to add additional group ids to the current process. You can specify it multiple times.
Emperor and Linux namespaces improvements¶
Thanks to the cooperation with the pythonanywhere.com guys the Emperor has been improved for better Linux namespaces integration.
The –emperor-use-clone option allows you to use clone() instead of fork() for your vassal’s spawn. In this way you can create the vassals directly in a new namespace. The function takes the same parameters of the –unshare one
uwsgi --emperor /etc/vassals --emperor-use-clone pid,uts
will create each vassal in a new pid and uts namespace
while
uwsgi --emperor /etc/vassals --emperor-use-clone pid,uts,net,ipc,fs
will basically use all of the currently available namespaces.
Two new exec (and call) hooks are available:
–exec-as-emperor will run commands in the emperor soon after a vassal has been spawn (setting 4 env vars, UWSGI_VASSAL_CONFIG, UWSGI_VASSAL_PID, UWSGI_VASSAL_UID and UWSGI_VASSAL_GID)
–exec-as-vassal will run commands in the vassal just before calling exec() (so directly in the new namespaces)
–wait-for-interface¶
As dealing with the Linux network namespace introduces lot of race conditions (expecially when working with virtual ethernets), this new option allows you to pause an instance until a network interface is available.
This is useful when waiting for the emperor to move a veth to the vassal namespace, avoiding the vassal to run commands on the interface before is available
[uwsgi]
emperor = /etc/uwsgi/vassals
emperor-use-clone = pid,net,fs,ipc,uts
; each vassal should have its veth pair, so the following commands should be improved
exec-as-emperor = ip link del veth0
exec-as-emperor = ip link add veth0 type veth peer name veth1
; do not use the $(UWSGI_VASSAL_PID) form, otherwise the config parser will expand it on startup !!!
exec-as-emperor = ip link set veth1 netns $UWSGI_VASSAL_PID
[uwsgi]
; suspend until the emperor attach veth1
wait-for-interface = veth1
; the following hook will be run only after veth1 is available
exec-as-root = hostname vassal001
exec-as-root = ifconfig lo up
exec-as-root = ifconfig veth1 192.168.0.2
uid = vassal001
gid = vassal001
socket = :3031
...
Availability¶
uWSGI 1.9.15 has been released on August 29th 2013. You can download it from:
uWSGI 1.9.14¶
Changelog [20130721]
Bugfixes¶
- fixed python modifier1 management (was hardcoded to 0)
- fixed url decoding in http and http-socket (it now supports lowercase hex, spotted by Miles Shang)
- more user-friendly error message for undeletable unix sockets
- fixed –http-auto-chunked in http 1.1 keepalive mode (André Cruz)
- fixed python wheel support (Fraser Nevett)
- fixed –safe-fd (was not correctly honoured by the Emperor)
- fixed ruby 2.x reloading
- improved support for OSX Tiger (yes, OSX 10.4)
- better computation of listen queue load
- fixed v8 build on OSX
- fixed pypy rpc
- improved chunked api performance
- fixed latin1 encoding with python3
- fixed –spooler-ordered (Roberto Leandrini)
- fixed php status line reported in request logs
New features¶
Ruby 1.9.x/2.x native threads support¶
Ruby 1.9 (mri) introduced native threads support (very similar to the CPython ones, governed by a global lock named GVL).
For various reasons (check the comments on top of the source plugin) the ruby threads support in uWSGI has been implemented as a “loop engine plugin”.
You need to build the “rbthreads” plugin (it is automatic when using the ‘ruby2’ build profile) and enable it with ‘–rbthreads’
The gem script has been extended, automatically selecting the ‘ruby2’ build profile when a ruby >= 1.9 is detected (this should make the life easier for Heroku users)
Rails4 is the first Ruby on Rails version supporting and blessing threads (in 3.x you need to explicitly enable support). You can use multiple threads in Rails4 only when in “production” mode, otherwise your app will deadlock after the first request.
An example config:
[uwsgi]
plugins = rack,rbthreads
master = true
; spawn 2 processes
processes = 2
; spawn 8 threads
threads = 8
; enable ruby threads
rbthreads = true
; load the Rack app
rack = config.ru
; bind to an http port
http-socket = :9090
http-socket-modifier1 = 7
it will generate a total of 16 threads
Filesystem monitoring interface (fsmon)¶
Currently uWSGI is able to monitor filesystem changes using the “simple” –touch-* facility or the signal framework (using various operating system api like inotify or kqueue).
A new interface for plugin writers named “fsmon” has been added, allowing easy implementation of realtime filesystem monitors.
Three new options have been added:
–fs-reload <path>
–fs-brutal-reload <path>
–fs-signal <path> <signal>
Contrary to the –touch-* options they are realtime (the master is woke up as soon as the item changes) and. uses kernel facilities (currently only inotify() and kqueue() are supported). Thanks to this choice you can now monitor a whole directory for changes (without the need of external processes/wrapper like inotifywatch)
uClibc support¶
Author: Natanael Copa
uWSGI can now be built on uclibc-based systems (generally, embedded systems)
Alpine Linux is the operating system on which the support has been tested
setscheme, setdocroot¶
This two new routing actions allow you to dynamically override DOCUMENT_ROOT and UWSGI_SCHEME
sendfile, fastfile¶
This two actions (added to the router_static plugin) allows you to return static files to the client bypassing the DOCUMENT_ROOT check.
The first one forces the use of the sendfile() syscall (where available), while the second automatically tries to choose the best serving strategy (like offloading)
–reload-on-fd and –brutal-reload-on-fd¶
Two new options allowing you to reload an instance when a file descriptor is ready.
Currently the best usage scenario is for the oom_control cgroup interface (via eventfd).
Supposing you have a process wrapper allocating an eventfd() reporting OOM events (and exposed as the ‘OOM’ environment var) you can force a uWSGI reload when out of memory with:
[uwsgi]
...
reload-on-fd = $(OOM):8 OUT OF MEMORY !!!
it means:
monitor the $(OOM) file descriptor and read 8 bytes from it when ready (it is an eventfd() requirement), then print “OUT OF MEMORY !!!” in the logs and gracefully reload the instance.
Obviously this is only a way to use it. The UNIX world is file-descriptor based so you have plenty of funny ways to use it.
Spooler improvements¶
Author: Roberto Leandrini
Effectively all of the work has been done in uwsgidecorators.py
You can now pass to all of the available spooler-related decorators the “pass_arguments=True” option, to automatically serialize the spooler function parameters. This is an abstraction avoiding you the need to serialize/deserialize arguments.
In addition to this the decorators have been extended to implement __call__ in this way you can directly call spoller decorated functions as normal functions.
–emperor-nofollow¶
Enabling this option will allows the Emperor to watch for symbolic links mtime update instead of the mtime of the real file.
Alberto Scotto is working on an updated version supporting both (should be ready for the next release)
daemontools envdir support¶
Albeit daemontools look old-fashioned, things like envdirs (http://cr.yp.to/daemontools/envdir.html) are heavily used in various context.
uWSGI got two new options (–envdir <path> and –early-envdir <path>) allowing you to support this special (archaic ?) configuration way.
xmldir improvements¶
Author: Guido Berhoerster
The xmldir plugins has been improved supporting iconv-based utf8 encoding. Various minor fixes have been committed.
The examples directory contains two new files showing an xmldir+xslt usage
Breaking News !!!¶
Servlet 2.5 support development has just started. The plugin is present in the tree but it is unusable (it is a hardcoded jsp engine). We expect a beta version after the summer. Obviously we shameless consider The JWSGI interface a better approach than servlet for non-Enterprise people ;)
uWSGI 1.9.13¶
Changelog [20130622]
Bugfixes¶
- Fixed a corner case bug when response offloading is enabled, but no request plugin is loaded
- Fixed harakiri routing when multiple rules are in place (return NEXT instead of CONTINUE)
- Fixed curl crashing master on slow dns responses (Łukasz Mierzwa)
- Removed PTRACE check in uwsgi.h (it is no more needed since uWSGI 1.0)
- Fixed –print-sym
- Added a newline in –cflags
- Improved python3 detection and compilation
- Fixed Coro::AnyEvent loop engine (John Berthels)
- Rack api functions are now static
- Better fastcgi handling of big uploads
- Improved GCC usage on Darwin for Python non-apple builds
- Fixed XCLIENT usage in rawrouter
- Use the clang preprocessor instead of hardcoded ‘cpp’ when CC=clang is used
- Set 16bit options to 65535 when higher values are requested
- Fixed virtualhosting (it is now compatible with 1.4 configurations)
New features¶
PyPy performance and features improvents¶
The PyPy plugin has been improved a lot. The amount of C code has been reduced by 70%, so, now, the vast majority of the plugin is written in python. The c helpers have been removed allowing the python part to directly call native uWSGI functions via cffi.
Support for PyPy continulets (and their greenlet abstraction) has been added (while waiting for a solid gevent port for pypy) and a chat example is already available (using the uwsgi async api):
https://github.com/unbit/uwsgi/tree/master/t/pypy
https://github.com/unbit/uwsgi/blob/master/contrib/pypy/uwsgi_pypy_greenlets.py
The pypy uwsgi api has been improved and now you can use the uwsgidecorators module (even if the spooler subsystem is still missing)
Chunked input api¶
In the last days there have been a bunch of discussions on how to correctly manage chunked input. As basically none of the available standards support it in a “definitive” way, we have defined a low-level api (so we can easily adapt it in the feature).
The api exposes two functions:
uwsgi.chunked_read()
and
uwsgi.chunked_read_nb()
A non blocking chat example:
import uwsgi
def application(e, sr):
while True:
uwsgi.wait_fd_read(uwsgi.connection_fd())
uwsgi.suspend()
msg = uwsgi.chunked_read_nb()
if msg: print "core %d" % e['uwsgi.core'], msg
Toward better third-party plugins management: the –dot-h option¶
As the –cflags option shows the CFLAGS used to build the server, the –dot-h option shows the content of uwsgi.h
This means the content of uwsgi.h is now embedded in the binary (compressed where available).
It could look a bizarre choice but the objective is to allow easy compilation of plugins out of the uwsgi sources (somethign that will be available in 2.0 for sure)
setmethod, seturi and setpathinfo routing action¶
we continue extending the routing api.
Three new actions have been added to dinamically modify the request
UWSGI_INCLUDES¶
You can now ovverride the include search path (while building uWSGI) with this environment variable.
Improved set_user_harakiri api function¶
Now the uwsgi.set_user_harakiri automatically recognize mules and spoolers. It has been added to the ruby/rack, pypy and perl/psgi plugins
–add-cache-item [cache ]KEY=VALUE¶
this is a commodity option (mainly useful for testing) allowing you to store an item in a uWSGI cache during startup
the router_xmldir plugin¶
This is a proof of concept plugin aimed at stressing the transformation api.
It basically generates an xml representation of a directory. This could be useful to implement apache-style directoryindex:
Check this example using xslt:
https://github.com/unbit/uwsgi/issues/271#issuecomment-19820204
Implement __call__ for @spool* decorators¶
Thanks to ‘anaconda’, you can now directly call functions mapped to the spooler, so instead of
myfunc.spool(args)
you can directly do:
myfunc(args)
the old way is obviously supported
the uwsgi[lq] routing var¶
this routing var exports the current size of the listen_queue:
[uwsgi]
...
route-if = higher:${uwsgi[lq]};70 break:503 Server Overload
...
–use-abort¶
On some system the SEGV signal handler cannot be correctly restored after the uWSGI backtrace.
If you want to generate a core file, you may want to trigger a SIGABRT soon after the backtrace.
Availability¶
uWSGI 1.9.13 will be available 22th June 2013 at this url:
uWSGI 1.9.12¶
Changelog [20130605]
Bugfixes¶
- offloading cache writes will return the correct status code and not 202
- you can now control the path of temporary files setting the TMPDIR environment variable (this fixes an old issue for users without control over /tmp)
- fixed a compilation error on amqp imperial monitor
- cron commands are correctly escaped when reported in the stats server
- fixed fastcgi parser corner-case bug with big uploads
- fixed support for newest cygwin
New Features¶
Offloading responses¶
Take the following WSGI app:
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['u' * 100000000]
it will generate about 100megs of data. 98% of the time the worker spent on the request was on the data transfer. As the whole response is followed by the end of the request we can offload the data write to a thread and free the worker suddenly (so it will be able to handle a new request).
100megs are a huge value, but even 1MB can cause a dozen of poll()/write() syscalls that blocks your worker for a bunch of milliseconds
Thanks to the ‘memory offload’ facility added in 1.9.11 implementing it has been very easy.
The offloading happens via the uWSGI Transformations
[uwsgi]
socket = :3031
wsgi-file = myapp.py
; offload all of the application writes
route-run = offload:
By default the response is buffered to memory until it reaches 1MB size. After that it will be buffered to disk and the offload engine will use sendfile().
You can set the limit (in bytes) after disk buffering passing an argument to the offload:
[uwsgi]
socket = :3031
wsgi-file = myapp.py
; offload all of the application writes (buffer to disk after 1k)
route-run = offload:1024
“offload” MUST BE the last transformation in the chain
[uwsgi]
socket = :3031
wsgi-file = myapp.py
; gzip the response
route-run = gzip:
; offload all of the application writes (buffer to disk after 1k)
route-run = offload:1024
JWSGI and JVM improvements¶
The JVM plugin has been extended to support more objects helper (like ArrayList), while JWSGI can now be used as a low-level layer to add support for more JVM-based languages.
JRuby integration is the first attempt of such a usage. We have just releases a JWSGI to Rack adapter allowing you tun run Ruby/Rack apps on top of JRUBY:
https://github.com/unbit/jwsgi-rack
A similar approach for Jython is on work
–touch-signal¶
A new touch option has been added allowing the rise of a uwsgi signal when a file is touched:
[uwsgi]
...
; raise signal 17 on /tmp/foobar modifications
touch-signal = /tmp/foobar 17
...
The “pipe” offload engine¶
A new offload engine allowing transfer from a socket to the client has been added.
it will be automatically used in the new router_memacached and router_redis plugins
memcached router improvements¶
You can now store responses in memcached (as you can already do with uWSGI caching)
[uwsgi]
...
route = ^/cacheme memcachedstore:addr=127.0.0.1:11211,key=${REQUEST_URI}
route = ^/cacheme2 memcachedstore:addr=192.168.0.1:11211,key=${REQUEST_URI}foobar
...
obviously you can get them too
[uwsgi]
...
route-run = memcached:addr=127.0.0.1:11211,key=${REQUEST_URI}
...
The memcached router is now builtin in the default profiles
The new redis router¶
Based on the memcached router, a redis router has been added. It works in the same way:
[uwsgi]
...
route = ^/cacheme redisstore:addr=127.0.0.1:6379,key=${REQUEST_URI}
route = ^/cacheme2 redisstore:addr=192.168.0.1:6379,key=${REQUEST_URI}foobar
...
... and get the values
[uwsgi]
...
route-run = redis:addr=127.0.0.1:6379,key=${REQUEST_URI}
...
The redis router is builtin by default
The “hash” router¶
this special routing action allows you to hash a string and return a value from a list (indexed with the hashed key).
Take the following list:
127.0.0.1:11211
192.168.0.1:11222
192.168.0.2:22122
192.168.0.4:11321
and a string:
/foobar
we hash the string /foobar using djb33x algorithm and we apply the modulo 4 (the size of the items list) to the result.
We get “1”, so we will get the second items in the list (we are obviously zero-indexed).
Do you recognize the pattern ?
Yes, it is the standard way to distribute items on multiple servers (memcached clients for example uses it from ages).
The hash router exposes this system allowing you to distribute items in you redis/memcached servers or to make other funny things.
This an example usage for redis:
[uwsgi]
...
; hash the list of servers and return the value in the MYNODE var
route = ^/cacheme_as/(.*) hash:items=127.0.0.1:11211;192.168.0.1:11222;192.168.0.2:22122;192.168.0.4:11321,key=$1,var=MYNODE
; log the result
route = ^/cacheme_as/(.*) log:${MYNODE} is the choosen memcached server !!!
; use MYNODE as the server address
route = ^/cacheme_as/(.*) memcached:addr=${MYNODE},key=$1
...
you can even choose the hashing algo from those supported in uWSGI
[uwsgi]
...
; hash the list of servers with murmur2 and return the value in the MYNODE var
route = ^/cacheme_as/(.*) hash:algo=murmur2,items=127.0.0.1:11211;192.168.0.1:11222;192.168.0.2:22122;192.168.0.4:11321,key=$1,var=MYNODE
; log the result
route = ^/cacheme_as/(.*) log:${MYNODE} is the choosen memcached server !!!
; use MYNODE as the server address
route = ^/cacheme_as/(.*) memcached:addr=${MYNODE},key=$1
...
the router_hash plugin is compiled-in by default
Availability¶
uWSGI 1.9.12 will be available starting from 20130605 at the following url
uWSGI 1.9.11¶
Changelog [20130526]
Bugfixes¶
- Fixed Python 3 stdout/stderr buffering
- Fixed mule messages (
@mulefunc
is now reliable) - Fixed
SCRIPT_NAME
handling in dynamic mode - Fixed X-Sendfile with gzip static mode
- Fixed cache item maximum size with custom block size
- Fixed cache path handling
New features¶
The new high-performance PyPy plugin¶
Credits: Maciej Fijalkowski
We are pleased to announce the availability of the new PyPy plugin.
PyPy team has been great in helping us. We hope the uWSGI integration (that exposed new challenges to the PyPy project) will help PyPy becaming better and better.
Official docs: The PyPy plugin
Cron improvements¶
Credits: Łukasz Mierzwa
Unique crons¶
You can now avoid overlapping crons. The uWSGI master will track death of a single task, and until its death the same cron will not be triggered:
[uwsgi]
unique-cron = -1 -1 -1 -1 -1 my_script.sh
cron2 syntax¶
A key/value variant of the –cron option is now available:
[uwsgi]
cron2 = minute=39,hour=23,month=-1,week=-1,day=-1,unique=1,legion=foobar,harakiri=30
harakiri cron¶
When using the cron2
option you are allowed to set a harakiri timeout for a cron task. Just add harakiri=n
to the options.
Support for GNU Hurd¶
Debian GNU/Hurd has been recently released. uWSGI 1.9.11 can be built over it, however very few tests have been made.
The memory offload engine¶
Idea: Stefano Brentegani
When serving content from the cache, a worker could get blocked during transfer from memory to the socket.
A new offload engine named “memory” allows to offload memory transfers. The cache router automatically supports it. Support for more areas will be added soon.
To enable it just add --offload-threads <n>
New Websockets chat example¶
An example websocket chat using Redis has been added to the repository:
https://github.com/unbit/uwsgi/blob/master/tests/websockets_chat.py
Error routes¶
You can now define a routing table to be executed as soon as you set the HTTP status code in your plugin.
This allows you to completely modify the response. This is useful for custom error codes.
All of the routing standard options are available (included labels) plus an optimized error-route-status
matching a specific HTTP status code:
[uwsgi]
error-route-status = 502 redirect:http://unbit.it
Support for corner case usage in wsgi.file_wrapper¶
Generally the wsgi.file_wrapper
callable expects a file-like object. PEP 333/3333 reports a special pattern when the object
is not a file (call read()
until the object is consumed). uWSGI now supports this pattern (even if in a hacky way).
HTTP/HTTPS router keepalive improvements¶
Credits: André Cruz
When using --http-keepalive
you can now hold the connection open even if the request has a body.
The harakiri routing action¶
You can now set a harakiri timer for each request using internal routing:
[uwsgi]
; set harakiri to 30 seconds for request starting with /slow
route = ^/slow harakiri:30
RPC wrappers¶
The RPC plugin has been extended to allows interoperation with other standards.
Currently a simple HTTP wrapper and an XML-RPC one are exposed.
The HTTP simple wrapper works by parsing PATH_INFO
.
A /foo/bar/test
call will result in
uwsgi.rpc(‘foo’, ‘bar’, ‘test’)
To enable this HTTP mode just set the modifier2
to ‘2’:
[uwsgi]
http-socket = :9090
http-socket-modifier1 = 173
http-socket-modifier2 = 2
; load the rpc code
import = myrpcfuncs.py
or (to have more control)
[uwsgi]
http-socket = :9090
route-run = uwsgi:,173,2
; load the rpc code
import = myrpcfuncs.py
The XML-RPC wrapper works in the same way, but it uses the modifier2 value ‘3’. It requires a libxml2-enabled build of uWSGI.
[uwsgi]
http-socket = :9090
route-run = uwsgi:,173,3
; load the rpc code
import = myrpcfuncs.py
Then just call it:
proxy = xmlrpclib.ServerProxy("http://localhost:9090')
proxy.hello('foo','bar','test')
You can combine multiple wrappers using routing.
[uwsgi]
http-socket = :9090
; /xml force xmlrpc wrapper
route = ^/xml uwsgi:,173,3
; fallback to HTTP simple
route-if-not = startswith:${PATH_INFO};/xml uwsgi:,173,2
; load the rpc code
import = myrpcfuncs.py
Availability¶
uWSGI 1.9.11 will be available since 20130526 at:
uWSGI 1.9.10¶
Changelog [20130511]
Bugfixes¶
- fixed alarm threads during reloads
- fixed uninitialized memory in –touch-* options
- fixed a regression in –attach-daemon
New Features¶
Welcome to gccgo¶
Go support in gcc 4.8 is amazing, thanks to the split-stack feature you can now have goroutines without allocating a whole pthread.
As Go 1.1 will be no no more compatible with uWSGI, gccgo will became the official way for running go apps.
The gccgo plugin is in early stage of development but it is already able to run in preforking mode.
We are heavy working on a true “goroutines” Loop engine. Stay tuned.
Final routes¶
You can now run routing rules after a request. Obviously not all of the exposed actions make sense after the request but you should be able to write even more complex setup.
Check this request limiter based on HTTP response status (a value you can get only after a request):
https://github.com/unbit/uwsgi/blob/master/t/routing/errorlimiter.ini
Availability¶
uWSGI 1.9.10 will be available since 20130511 at the following url:
uWSGI 1.9.9¶
Changelog [20130508]
Special Warning !!!¶
The router_basicauth plugin has changed its default behaviour to return “break” if authorization fails.
The “basicauth-next” action, uses the old behaviour (returning “next”)
This new approach should reduce security problems caused by wrong configurations
Bugfixes¶
- do not increment “tx” statistics counter for “unaccountable” plugins
- fixed –backtrace-depth
- fixed cache-sync parsing
- fixed mule farms initialization
- fixed multithreading bug when regexp conditional route is used
- fixed default-app usage in the psgi plugin
- fixed python dynamic mode + threads
- fixed error reporting in corerouter when retry is in place
- correctly report harakiri condition for gateways
New Features¶
The WebDav plugin¶
WebDav is one of the much requested features for the project. We now have a beta-quality plugin, already supporting additional standards like the carddav:
https://github.com/unbit/uwsgi/blob/master/t/webdav/carddav.ini
The official modifier is 35, and to mount a simple directory as a webdav shares (for use with windows, gnome...) you only need to specify the –webdav-mount option:
[uwsgi]
plugin = webdav
http-socket = :9090
http-socket-modifier1 = 35
webdav-mount = /home/foobar
remember to protect shares:
[uwsgi]
plugin = webdav,router_basicauth
http-socket = :9090
http-socket-modifier1 = 35
route-run = basicauth:CardDav uWSGI server,unbit:unbit
webdav-mount = /home/foobar
WebDav attributes are stored as filesystem xattr, so be sure to use a filesystem supporting them (ext4, xfs, hfs+...)
LOCK/UNLOCK support is still incomplete
Official docs will be available soon.
Support for Go 1.1 (more or less, sad news for go users...)¶
Albeit you can successfully embed go 1.1 apps in uWSGI, go 1.1 will be completely fork() unsafe.
That means you are not able to use multiprocessing, the master, mules and so on.
Basically half of the uWSGI features will be no more usable in go apps.
Things could change in the future, but currently our objective is better integration with the gccgo project.
Go 1.0.x will continue to be supported (unless gccgo shows itself as a better alternative)
More to come soon.
Improved async modes¶
Stackless, Greenlet and Fiber support have been updated to support new async features
The radius plugin¶
You can now authenticate over radius servers using the router_radius plugin:
[uwsgi]
plugin = webdav,router_radius
http-socket = :9090
http-socket-modifier1 = 35
route-run = radius:realm=CardDav uWSGI server,server=127.0.0.1:1812
webdav-mount = /home/foobar
The SPNEGO plugin¶
Another authentication backend, using SPNEGO (kerberos)
[uwsgi]
plugin = webdav,router_spnego
http-socket = :9090
http-socket-modifier1 = 35
route-run = spnego:HTTP@localhost
webdav-mount = /home/foobar
The plugin is beta quality as it leaks memory (it looks like a bug in MIT-kerberos) and Heimdal implementation does not work.
More reports are wellcomed
The ldap authenticator¶
(Author: Łukasz Mierzwa)
Currently it lacks SASL support. Will be improved soon.
[uwsgi]
...
plugins = router_ldapauth
route = ^/a ldapauth:LDAP realm,url=ldap://ldap.domain,com;basedn=ou=users,dc=domain.com;binddn=uid=proxy,dc=domain,dc=com;bindpw=password
New internal routing features¶
We removed the GOON action, as it was messy and basically useless with the new authentication approach
The “setscriptname” action has been added to override the internally computed SCRIPT_NAME (not only the var)
The “donotlog” action forces uWSGI to not log the current request
The “regexp” routing conditions has been improved to allows grouping. Now you can easily manipulate strings and adding them as new request VARS:
[uwsgi]
...
route-if = regexp:${REQUEST_URI};^/(.)oo addvar:PIPPO=$1
route-run = log:PIPPO IS ${PIPPO}
this will take the first char of foo and place in the PIPPO request var
Gevent atexit hook¶
uwsgi.atexit hook is now honoured by the gevent plugin (Author: André Cruz)
Streaming transformations¶
Transformations can be applied on the fly (no buffering involved).
Check updated docs: uWSGI Transformations
The xattr plugin¶
The xattr plugin allows you to reference files extended attributes in the internal routing subsystem:
[uwsgi]
...
route-run = addvar:MYATTR=user.uwsgi.foo.bar
route-run = log:The attribute is ${xattr[/tmp/foo:MYATTR]}
or (variant with 2 vars)
[uwsgi]
...
route-run = addvar:MYFILE=/tmp/foo
route-run = addvar:MYATTR=user.uwsgi.foo.bar
route-run = log:The attribute is ${xattr2[MYFILE:MYATTR]}
The airbrake plugin¶
(Author: Łukasz Mierzwa)
Currently at early stage of development allows sending uWSGI exceptions and alarms to airbrake servers.
Official docs will be available soon.
Legion Daemons¶
(Author: Łukasz Mierzwa)
No, it is not a blackmetal band, it is a new feature of The uWSGI Legion subsystem allowing you to run external processes only when an instance is a lord:
[uwsgi]
master = true
http = :8081
stats = :2101
wsgi-file = tests/staticfile.py
logdate = true
legion = legion1 225.1.1.1:19678 100 bf-cbc:abc
legion-node = legion1 225.1.1.1:19678
legion-attach-daemon = legion1 memcached -p 10001
legion-smart-attach-daemon = legion1 /tmp/memcached.pid memcached -p 10002 -d -P /tmp/memcached.pid
–touch-exec¶
A new “touch” option (like –touch-reload) is available, triggering the execution of a command:
[uwsgi]
...
touch-exec = /tmp/foobar run_my_script.sh
touch-exec = /var/test/foo.txt run_my_second_script.sh arg1 arg2
Math for cache¶
You can now use the caching subsystem to store 64bit signed numbers and apply atomic operations on them.
The uwsgi api has been extended with 5 new functions (currently exposed only by the python plugin):
*uwsgi.cache_num(key[,cache]) -> get the 64bit number from the specified item
*uwsgi.cache_inc(key[,amount=1,expires,cache]) -> increment the specified key by the specified amount
*uwsgi.cache_dec(key[,amount=1,expires,cache]) -> deccrement the specified key by the specified amount
*uwsgi.cache_mul(key[,amount=2,expires,cache]) -> multiply the specified key by the specified amount
*uwsgi.cache_div(key[,amount=2,expires,cache]) -> divide the specified key by the specified amount
The new api has been exposed to the routing subsystem, allowing you to implement advanced patterns, like the request limiter:
https://github.com/unbit/uwsgi/blob/master/t/routing/limiter.ini
the example shows hot to limit the request of a single ip to 10 every 30 seconds
The long-term objective of this new feature is being the base for the upcoming metric subsystem
Availability¶
uWSGI 1.9.9 will be availabel since 20130508 at the following url
uWSGI 1.9.8¶
Changelog [20130423]
Note: this is an “emergency” release fixing 2 regressions causing a crash during reloads and when using async+uGreen
Bugfixes¶
- fixed a crash when reloading the master
- fixed a crash in async mode + uGreen
- the ‘mime’ routing var requires a request var (not a raw string)
Availability¶
You can download uWSGi 1.9.8 from http://projects.unbit.it/downloads/uwsgi-1.9.8.tar.gz
uWSGI 1.9.7¶
Bugfixes¶
- fixed teajs engine build
- fixed offloading status code (set to 202 when a request is offloaded)
- execute cron tasks within 60 second resolution, instead of 61 seconds
- fixed websocket proxy
- check for python3 unicode encoding (instead of crashing...)
- fixed ipcsem removal on reload
- fixed kqueue timer on OpenBSD, NetBSD and DragonFlyBSD
- fixed/reimplemented perl uwsgi::register_rpc
- fixed fd leak on sendfile() error
- fixed Content-Length when gzip file variant is used
- allows non-request plugins to register rpc functions
- more robust error checking for cgroups
- honour SCRIPT_NAME the in the PSGI plugin when multiple perl apps are mounted
New features¶
Legion cron¶
A common needs when multiple instances of an application are running, is to force only one of them to run cron tasks. The new –legion-cron uses The uWSGI Legion subsystem to accomplish that:
[uwsgi]
; use the new legion-mcast shortcut (with a valor 90)
legion-mcast = mylegion 225.1.1.1:9191 90 bf-cbc:mysecret
; run the script only if the instance is the lord of the legion "mylegion"
legion-cron = mylegion -1 -1 -1 -1 -1 my_script.sh
Curl cron¶
The curl_cron plugin has been added allowing the cron subsystem to call urls (via libcurl) instead of unix commands:
[uwsgi]
; call http://uwsgi.it every minute
curl-cron = -1 -1 -1 -1 -1 http://uwsgi.it/
The output of the request is reported in the log
The UWSGI_EMBED_PLUGINS build variable¶
ou can now embed plugins on the fly during the build phase. Check this example:
UWSGI_EMBED_PLUGINS=gridfs,rack UWSGI_PROFILE=psgi make
this will build a monolithic binary with the default profile for psgi + the gridfs and the rack plugins (both embedded in the binary)
Gzip caching¶
The cachestore routing function can now directly store items in gzip format.
Check the CachingCookbook: http://uwsgi-docs.readthedocs.org/en/latest/tutorials/CachingCookbook.html
–skip-atexit¶
A bug in the mongodb client library could cause a crash of the uWSGI server during shutdown/reload. This option avoid calling atexit() hooks. If you are building a The GridFS plugin infrastructure you may want to use this option while the MongoDB guys solve the issue.
proxyhttp and proxyuwsgi¶
The http and uwsgi routing instructions are now more smart. You can cache their output and get the right status code in the logs.
This requires you to NOT use offloading. If offloading is in place and do not want to use it for this two router use the proxy-prefixed variant that will skip offloading.
You can now make cool things like:
[uwsgi]
socket = 127.0.0.1:3031
; create a cache of 100 items
cache = 100
; check if a cached value is available
route-run = cache:key=${REQUEST_URI}
; proxy all request to http://unbit.it
route-run = http:81.174.68.52:80,unbit.it
; and cache them for 5 minutes
route-run = cachestore:key=${REQUEST_URI},expires=300
The transformation api¶
A generic api for manipulating the response has been added (cachestore uses it)
check uWSGI Transformations
–alarm-fd¶
We are improving The uWSGI alarm subsystem (from 1.3) to be less-dependent on loglines. You can now trigger alarms when an fd is ready for read.
This is really useful for integration with the Linux eventfd() facility.
For example you can monitor (and throw an alarm) when your cgroup is running the OOM-Killer:
[uwsgi]
; define an 'outofmemory' alarm that simply print the alarm in the logs
alarm = outofmemory log:
; raise the alarm (with the specified message) when fd is ready (this is an eventfd se we read 8 bytes from the fd)
alarm-fd = outofmemory $(CGROUP_OOM_FD):8 OUT OF MEMORY !!!
in this example CGROUP_OOM_FD is an environment variable mapping to the number of an eventfd() filedescriptor inherited from some kind of startup script. Maybe (in the near future) we could be able to directly define this kind of monitor directly in uWSGI.
More information on the eventfd() + cgroup integration are here: https://www.kernel.org/doc/Documentation/cgroups/cgroups.txt
an example perl startup script:
use Linux::FD;
use POSIX;
my $foo = Linux::FD::Event->new(0);
open OOM,'/sys/fs/cgroup/uwsgi/memory.oom_control';
# we dup() the file as Linux::FD::Event set the CLOSE_ON_EXEC bit (why ???)
$ENV{'CGROUP_OOM_FD'} = dup(fileno($foo)).'';
open CONTROL,'>/sys/fs/cgroup/uwsgi/cgroup.event_control';
print CONTROL fileno($foo).' '.fileno(OOM)."\n";
close CONTROL;
exec 'uwsgi','mem.ini';
The spooler server plugin and the cheaper busyness algorithm compiled in by default¶
In extremely high-loaded scenario the busyness cheaper algorithm (by Łukasz Mierzwa) has been a real silver bullet in the past months allowing adaptive process spawning to be based on real usage time taking in account performance and response time. For this reason the plugin is now builtin by default.
In addition to this the remote spooler plugin (allowing external process to enqueue jobs) has been added too in the default build profile.
Availability¶
uWSGI 1.9.7 will be available since 20130422 at this url:
uWSGI 1.9.6¶
Changelog 20130409
Bugfixes¶
- workaround for building the python plugin with gcc 4.8
Sorry, this is not a real bugfix, but making a release without bugfixes seems wrong...
New Features¶
Sqlite and LDAP pluginization¶
Storing configurations in sqlite databases or LDAP tree is a pretty “uncommon” way to configure uWSGI instances. For such a reason they have been moved to dedicated plugins.
If you store config in a sqlite database, just add –plugin sqlite3. For LDAP, just add –plugin ldap:
uwsgi --plugin sqlite --sqlite config.db
Configuring dynamic apps with internal routing¶
‘Til now, you need to configure your webserver to load apps dinamically.
Three new instructions have been added to load aplication on demand.
Check the example:
[uwsgi]
http-socket = :9090
route = ^/foo chdir:/tmp
route = ^/foo log:SCRIPT_NAME=${SCRIPT_NAME}
route = ^/foo log:URI=${REQUEST_URI}
route = ^/foo sethome:/var/uwsgi/venv001
route = ^/foo setfile:/var/uwsgi/app001.py
route = ^/foo break:
route = ^/bar chdir:/var
route = ^/bar addvar:SCRIPT_NAME=/bar
route = ^/bar sethome:/var/uwsgi/venv002
route = ^/bar setfile:/var/uwsgi/app002.py
route = ^/bar break:
as you can see, rewriting SCRIPT_NAME is now very easy. The sethome instruction is currently available only for python application (it means ‘virtualenv’)
Carbon avg computation (Author: Łukasz Mierzwa)¶
You can now configure how the carbon plugin send the response average when no requests have been managed.
You have three ways:
–carbon-idle-avg none - don’t push any avg_rt value if no requests were made
—carbon-idle-avg last - use last computed avg_rt value (default)
–carbon-idle-avg zero - push 0 if no requests were made
Numeric checks for the internal routing¶
New check are available:
ishigher or ‘>’
islower or ‘<’
ishigherequal or ‘>=’
islowerequal or ‘<=’
Example:
[uwsgi]
route-if = ishigher:${CONTENT_LENGTH};1000 break:403 Forbidden
Math and time for the internal routing subsystem¶
If you build uWSGI with matheval support (matheval-dev on debian/ubuntu) you will get math support in your routing system via the ‘math’ routing var.
The ‘time’ routing var has been added currently exporting only the ‘unix’ field returning the epoch.
Check this crazy example:
[uwsgi]
http-socket = :9090
route-run = addvar:TEMPO=${time[unix]}
route-run = log:inizio = ${TEMPO}
route-run = addvar:TEMPO=${math[TEMPO+1]}
route-run = log:tempo = ${TEMPO}
As you can see the routing subsystem can store values in request variables (here we create a ‘TEMPO’ var, and you will be able to access it even in your app request vars)
The ‘math’ operations can reference request vars
Check the matheval docs for the supported operations: http://matheval.sourceforge.net/docs/index.htm
Added non-standard seek() and tell() to wsgi.input (post-buffering required)¶
While testing the ‘smart mode’ of the ‘Klaus’ project (https://github.com/jonashaag/klaus) we noticed it was violating the WSGI standard calling seek() and tell() when in smart mode.
We have added support for both methods when post-buffering is enabled.
REMEMBER: they violate the WSGI standard, so try to avoid them (if you can). There are better ways to accomplish that.
Pyshell improvements, AKA Welcome IPython (Idea: C Anthony Risinger)¶
You can invoke the ipython shell instead of the default one when using –pyshell:
uwsgi -s :3031 --pyshell="from IPython import embed; embed()"
Obviously you can pass whatever code to –pyshell
The ‘rpcraw’ routing instruction¶
Another powerful and extremely dangerous routing action. It will call a rpc function sending its return value directly to the client (without further processing).
Empty return values means “go to the next routing rule”.
Return values must be valid HTTP:
uwsgi.register_rpc('myrules', function(uri) {
if (uri == '/foo') {
return "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nServer: uWSGI\r\nFoo: bar\r\n\r\nCiao Ciao";
}
return "";
});
[uwsgi]
plugin = v8
v8-load = rules.js
route = ^/foo rpcraw:myrules ${REQUEST_URI}
Preliminary support for the HTTP Range header¶
The range request header allows requesting only part of a resource (like a limited set of bytes of a static file).
The system can be used when serving static files, but it is disabled by default. Just add –honour-range to enable it.
In the future it will be used for file wrappers (like wsgi.file_wrapper) and for The GridFS plugin (this is the reason for not enabling it by default as you could have already implemented range management in your app)
The ‘lord’ routing condition¶
We are working hard on making a truly amazing cluster subsystem using The uWSGI Legion subsystem
You can now execute internal routing rules when an instance is a lord:
[uwsgi]
...
route-if = lord:mylegion log:I AM THE LORD !!!
the “I AM THE LORD !!!” logline will be printed only when the instance is a lord of the legion ‘mylegion’
GridFS authentication¶
You can now connect to authenticated MongoDB servers when using The GridFS plugin
Just add the username and password parameters to the mount definition
The –for-times config logic¶
You can use –for-times for running uWSGI options the specified number of times:
[uwsgi]
for-times = 8
mule = true
endfor =
this will spawn 8 mules
The ‘uwsgi’ routing var¶
Accessing uWSGI internal parameters when defining routing rules could be handy. The ‘uwsgi’ routing var is the container for such vars.
Currently it exports ‘wid’ (the id of the worker running the rule) and ‘pid’ (the pid of the worker running the rule)
[uwsgi]
master = true
processes = 4
; stupid rule... break connections to the worker 4
route-if = ishigher:${uwsgi[wid]};3 break:403 Forbidden
The ‘alarm’ routing action¶
You can now trigger alarms from the routing subsystem:
[uwsgi]
alarm = pippo cmd:cat
route = ^/help alarm:pippo ${uwsgi[wid]} ${uwsgi[pid]}
http-socket = :9090
when /help is requested the ‘pippo’ alarm is triggered passing the wid and the pid as the message
Welcome to the ruby shell¶
As well as the –pyshell we now have the ruby shell:
uwsgi --rbshell -s :3031
or
uwsgi --rbshell="require 'pry';binding.pry" -s :3031
for using the pry shell: http://pryrepl.org/
... and welcome to the Lua shell¶
As python and ruby, even Lua got its shell. Just add –lua-shell
Goodbye to the old (and useless) probe subsystem¶
The probe subsystem was added during 0.9 development cycle but it was badly designed and basically broken.
It has been definitely removed (the deprecation phase has been skipped as 1.9 is not an LTS release and 1.4 still support it)
Improvements in the Legion subsystem (Author: Łukasz Mierzwa)¶
Two new hooks have been added: –legion-node-joined and –legion-node-left
More fine-tuning¶
–socket-sndbuf and –socket-rcvbuf have been added to allow tuning of the send a receive buffer of the uWSGI sockets (use with caution)
V8 improvements and TeaJS integration¶
The uWSGI V8 support plugin continue to improve. The main target is still uWSGI internal routing but JSGI support is almost complete and we are working for TeaJS (old v8cgi) integration: http://code.google.com/p/teajs/
more to come soon...
Availability¶
uWSGI 1.9.6 will be available since 20130409 at this url:
uWSGI 1.9.5¶
Changelog 20130404
Bugfixes¶
- fixed a memory leak with cachestore routing instruction (Riccardo Magliocchetti)
- fixed a memory leak in carbon plugin (Riccardo Magliocchetti)
- fixed a memory leak in the cgi plugin (Riccardo Magliocchetti)
- fixed old-style python dynamic apps
- force the emperor to honour –max-fd for vassals
- improved PSGI seek with post-buffering
- fixed kvlist escaping
New features¶
The GridFS plugin¶
A plugin exporting GridFS features is available, check official docs: The GridFS plugin
V8 improvements¶
The V8 plugin continues to improve. Preliminary JSGI 3.0 support is available as well as multithreading.
The ‘require’ commonjs standard has been implemented.
Writing commonjs specs will be a very long work, so maybe a partnership with projects like teajs (the old v8cgi) would be a better path to follow.
In the mean time, we are working on docs: uWSGI V8 support
The ‘cgi’ routing instruction¶
You can now call CGI script directly from the uWSGI internal routing
[uwsgi]
plugin = cgi
route = ^/cgi-bin/(.+) cgi:/usr/lib/cgi-bin/$1
Availability¶
uWSGI 1.9.5 will be available since 20130404 at this url
uWSGI 1.9.4¶
Changelog 20130330
Bugfixes¶
fixed cache statistics exported by the stats subsystem (Łukasz Mierzwa)
fixed CoroEV bug in after_request (Tom Molesworth and John Berthels)
update cache items after a restore from persistent storage (Łukasz Mierzwa)
fixed signal handling in non-worker processes
fixed thundering herd in multiple mules setup
ported the cplusplus skeletal plugin to the new api
fixed uWSGI reloading when build as a shared library
New features¶
SmartOS official support¶
From now on, SmartOS is included in the officially supported operating systems
V8 initial support¶
The Lua previous suggestion for writing uWSGI routing rules and configurations, woke up lot of javascript users stating that javascript itself could be a valid alternative. A V8 plugin is now available, supporting RPC, signal handlers and configurations. You need libv8 headers to build it:
python uwsgiconfig.py --plugin plugins/v8
var config = {};
config['socket'] = [':3031', ':3032', ':3033'];
config['master'] = true;
config['processes'] = 3+1;
config['module'] = 'werkzeug.testapp:test_app';
config;
uwsgi --plugin v8 --config foo.js
The previous example will allows you to write dynamic configs in javascript, while you can export javascript functions via the RPC subsystem:
function part1(request_uri, remote_addr) {
return '<h1>i am part1 for ' + request_uri + ' ' + remote_addr + "</h1>" ;
}
function part2(request_uri, remote_addr) {
return '<h2>i am part2 for ' + request_uri + ' ' + remote_addr + "</h2>" ;
}
function part3(request_uri, remote_addr) {
return '<h3>i am part3 for ' + request_uri + ' ' + remote_addr + "</h3>" ;
}
uwsgi_register_rpc('part1', part1);
uwsgi_register_rpc('part2', part2);
uwsgi_register_rpc('part3', part3);
[uwsgi]
plugin = v8
v8-load = func.js
cache2 = name=foobar,items=10
http-socket = :9090
route-run = addheader:Content-Type: text/html
route-run = cache:key=pippo,name=foobar
route-run = cachestore:key=pippo,name=foobar
route-run = rpcnext:part1 ${REQUEST_URI} ${REMOTE_ADDR}
route-run = rpcnext:part2 ${REQUEST_URI} ${REMOTE_ADDR}
route-run = rpcnext:part3 ${REQUEST_URI} ${REMOTE_ADDR}
route-run = break:
The previous example generates an HTTP reponse from 3 javascript functions and store it in the uWSGI cache.
Curious about rpcnext ?
The rpcnext routing action¶
We can already call rpc functions from the routing subsystem to generate response. With the rpcnext action (aliased as rpcblob too) you can call multiple rpc functions and assemble the return values in a single response.
Legion improvements¶
We are hardly working in stabilizing The uWSGI Legion subsystem The objective is have a rock-solid clustering implementation for uWSGI 2.0 that you can use even from your applications.
The code in 1.9.4 has been refactored a bit by Łukasz Mierzwa to allow easier integration with external plugins.
A new “join” hook has been added, it is called as soon as a node becomes active part of a legion (read, it is part of a quorum).
Availability¶
uWSGI 1.9.4 will be available since 20130330 at this url
uWSGI 1.9.3¶
Changelog 20130328
Bugfixes¶
fixed imports in the JVM build system when virtualenvs are used (Ryan Kaskel)
fixed mod_proxy_uwsgi with apache 2.4
fixed php headers generation when Status is created from the php app itself
New features¶
Pluggable configuration system (with Lua support)¶
From this version you will be able to implement configurators (like the already available xml, ini, yaml, json, ldap, sqlite...) as plugins.
The first available configurator is the Lua one (offered by the lua plugin).
This is an example configuration written in lua:
config = {}
config['immediate-uid'] = 'roberto'
config['immediate-gid'] = 'roberto'
config['http-socket'] = ':9090'
config['env'] = { 'FOO=bar', 'TEST=topogigio' }
config['module'] = 'werkzeug.testapp:test_app'
return config
you can load it with:
uwsgi --plugin lua --config config.lua
The –config option is the way to load pluggable configurators. You can even override the already available embedded configurators with your own version.
The Emperor has already been extended to support pluggable configurators:
[uwsgi]
emperor = /etc/uwsgi/vassals
emperor-extra-extension = .lua
emperor-extra-extension = .foo
adding emperor-extra-extension will allows the emperor to search for the specified extension passing the config file to the vassal with the –config option.
Immediate setuid and setgid¶
In a recent uWSGI maling-list thread, the need to not rely on file system permissions for the tyrant mode emerged.
Albeit it is the most secure approach, two new options –immediate-uid and –immediate-gid have been added.
Setting them on top of your vassal file will force the instance to setuid()/setgid() as soon as possibile and without the (theoretical) possibility to override them.
The word “theoretical” here is the key, you always need to remember that a security bug in uWSGI could allow a malicious user to change privileges, so if you really care security (or do not trust uWSGI developers ;) always drop privileges before the vassal/instance is spawned (like in standard tyrant mode)
Honouring symlinks in tyrant mode¶
The option –emperor-tyrant-nofollow has been added forcing the emperor to now follow symlinks when searching for uid/gid in tyrant mode.
This option allows the sysadmin to simply symlink configurations and just change the uid/gid of the symlink it self (remember to pass the -h option to chown !!!)
The “rpcret” routing action (or usa Lua to write advanced rules)¶
The uWSGI internal routing continue to be improved.
You can already call rpc function for the routing system (to generate response bypassing WSGI/PSGI/Rack/... engines):
[uwsgi]
lua-load = myrpcfunctions.lua
route = ^/foo/(.+)/call rpc:hello_world ${REMOTE_ADDR} $1
the hello_worls rpc function is defined (and registered) in the myrpcfunctions.lua taking two arguments.
The function is called when the routing regexp matches, and its output sent to the client.
The “rpcret” works in similar way, but instead generating a response, you generate a routing return code:
function choose(request_uri, remote_addr)
print( 'REQUEST_URI is ' ..request_uri.. ' (from Lua)')
if request_uri == '/topogigio' then
return "goto topogigio"
end
return "break 500 Internal server Error !!!"
end
print('Hello Hello')
uwsgi.register_rpc('choose', choose)
and the uWSGI config:
[uwsgi]
route-run = rpcret:choose ${REQUEST_URI} ${REMOTE_ADDR}
route-run = break
route-label = topogigio
route-run = log:i am topogigio !!!
The ‘choose’ rpc function will be invoked at every request passing REQUEST_URI and REMOTE_ADDR as its argument.
The return string of the function will be used to know what to do next (from the internal ruting point of view).
Currently supported return strings are:
next
move to the next rule
continue
pass the request to the request handler
goon
move to the next rule with a different action
break
close the connection with an optional status code
goto <label>
goto to the specified label
Obviously rpc functions for rpcret can be written in any language/platform supported by uWSGI, but we strongly suggest to go with Lua for performance reasons (the inpact compared to pure C code is pretty irrelevant). If you are lucky and can use LuaJit you will experiment even better performance as for this kind of job a JIT compiler is the best approach.
Availability¶
uWSGI 1.9.3 has been released on 20130328 and can be downloaded from:
uWSGI 1.9.2¶
Changelog 20130326
Bugfixes¶
Fixed python3 response headers managament (wrong refcnt)
Fixed readline() on request body when postbuffering is in place
Fixed ruby fiber plugin
New features¶
route-run and the cachestore routing action¶
You can now store responses automatically in the uWSGI cache:
[uwsgi]
http-socket = :9090
; ensure the sweeper thread will run
master = true
cache2 = name=pippo2,items=10
module = werkzeug.testapp:test_app
route-run = cache:key=${REQUEST_URI},name=pippo2
route-run = cachestore:key=${REQUEST_URI},expires=30,name=pippo2
this example check every request for its availability in the cache ‘pippo2’. If not available the request plugin (werkzeug test app) will run normally and its output will be stored in the cache (only if it returns a HTTP 200 status)
--route-run
is a new option allowing you to directly call routing action without checking for a specific condition (yes, it is an optimization)
routing access to cookie and query string¶
Check updated docs uWSGI internal routing
empty internal routing condition¶
Check updated docs uWSGI internal routing
The Geoip plugin¶
Check official docs The GeoIP plugin
The SSI plugin (beta)¶
Check official docs SSI (Server Side Includes) plugin
Availability¶
uWSGI 1.9.2 has been released on 20130326 and can be downloaded from:
uWSGI 1.9.1¶
First minor release for the 1.9 tree.
Bugfixes¶
Fixed –req-logger after a graceful reload
Fixed a crash with the carbon plugin
Fixed signal handling when multiple workers + copy on write is in place
Fixed exception handling in the Rack plugin
The XSLT plugin¶
The XSLT plugin has been added. It allows to apply XML transformation via request plugin or uWSGI internal routing
Legion scrolls api¶
Scrolls are text blob attached to each member of a Legion cluster. We are slowly defining an api allowing developers to directly use the legion subsystem in their apps and configurations. The addition in 1.9.1 is the uwsgi.scrolls(legion) function returning a list/array of the current scrolls defined by the whole cluster. This is still not something fully usable (and useful) more to come soon...
On demand vassals¶
Another step in better resource usage for massive hosting. You can now tell the Emperor to start vassals only after the first request to a specific socket. Combined with –idle/–die-on-idle options, you can have truly on-demand applications.
To define the socket to wait for for each vassal you have 3 options:
–emperor-on-demand-extension <ext>¶
this will instruct the Emperor to check for a file named <vassal>+<ext>, if the file is available it will be read and its content used as the socket to wait for:
uwsgi --emperor /etc/uwsgi/vassals --emperor-on-demand-extension .socket
supposing a myapp.ini file in /etc/uwsgi/vassals, a /etc/uwsgi/vassals/myapp.ini.socket will be searched for (and its content used as the socket name)
At the first connection, the vassal is spawned and the socket passed as the file descriptor 0. File descriptor 0 is always checked by uWSGI so you do not need to specify a –socket option in the vassal file. This works automagically for uwsgi sockets, if you use other protocols (like http or fastcgi) you have to specify it with the –protocol option
–emperor-on-demand-directory <dir>¶
This is a less-versatile approach supporting only UNIX sockets. Basically the name (without extension and path) of the vassal is appended to the specified directory + the .socket extension and used as the on-demand socket:
uwsgi --emperor /etc/uwsgi/vassals --emperor-on-demand-directory /var/tmp
using the previous example, the socket /var/tmp/myapp.socket will be automatically bound
–emperor-on-demand-exec <cmd>¶
This is what (very probably) you will use in very big deployments. Every time a new vassal is added the supplied command is run passing the vassal name as the first argument. The STDOUT of the command is used as the socket name.
The –exec-post-app hook¶
In addition to the other –exec-* options (used to run commands at the various server stages), a new one has been added allowing you to run commands after the load of an application.
The pyring build profile¶
This is a very specific build profile allowing you to automatically build a uWSGI stack with monolithic python support and modular jvm + ring honouring virtualenvs.
The cache router plugin¶
This has been improved, and in next releases we should be able to directly store response in the uWSGI cache only using the internal routing subsystem
Docs will be available soon
The crypto logger¶
If you host your applications on cloud services without persistent storage you may want to send your logs to external systems. Sadly logs often contain sensible informations you should not transfer in clear. The new crypto logger try to solve this issue allowing you to encrypt each log packet and send it over udp to a server able to decrypt it.
The following example
uwsgi --plugin logcrypto --logger crypto:addr=192.168.173.22:1717,algo=bf-cbc,secret=ciaociao -M -p 4 -s :3031
will send each log packet to the udp server available at 192.168.173.22:1717 encrypting the text with ‘ciaociao’ secret key using the blowfish cbc algorithm.
An example server is available here:
https://github.com/unbit/uwsgi/blob/master/contrib/cryptologger.rb
The rpc internal routing instruction¶
The “rpc” routing instruction has been added, allowing you to call rpc functions directly from the routing subsystem and forward they output to the client.
Check the following examples:
[uwsgi]
http-socket = :9090
route = ^/foo addheader:Content-Type: text/html
route = ^/foo rpc:hello ${REQUEST_URI} ${HTTP_USER_AGENT}
route = ^/bar/(.+)$ rpc:test $1 ${REMOTE_ADDR} uWSGI %V
route = ^/pippo/(.+)$ rpc:test@127.0.0.1:4141 $1 ${REMOTE_ADDR} uWSGI %V
import = funcs.py
Preliminary support for name resolving in the carbon plugin¶
You can specify carbon servers using hostnames. The current code is pretty simple. Future updates will support round robin queries.
New routing conditions¶
New routing conditions have been added (equal,startswith,endswith,regexp) check the updated docs:
http://uwsgi-docs.readthedocs.org/en/latest/InternalRouting.html#the-internal-routing-table
The ‘V’ magic var¶
You can reference the uWSGI version string using the %V magic var in your configurations
The ‘mongodb’ generic plugin¶
This is a commodity plugin for packagers not able to access a shared libmongoclient. This basically link it in a new shared object that can be used by the others mongodb plugin
Build profiles over network¶
You can now reference build profiles using urls (http, https and ftp are supported):
UWSGI_PROFILE=http://uwsgi.it/psgi.ini make
Get it¶
uWSGI 1.9.1 will be available since 20130324 at this url:
uWSGI 1.9¶
This is the version that will lead to the LTS 2.0. It includes a lot of internal changes and removal of a lot of basically unused, broken, or too ugly functionality.
Options deprecated in 1.0.x have been definitely removed.
Non-blocking for all¶
From now on, all of the request plugins, need to be non-blocking. A new set of C/C++/Obj-C api have been added to help the user/developer writing non-blocking code in a safe way. Plugins like the RPC one have been rewritten using that new api, allowing you to use it with engines like Gevent or Coro::Anyevent The async mode has been rewritten to better cooperate with this new rule. More info can be found on uWSGI asynchronous/non-blocking modes (updated to uWSGI 1.9) The new async mode requires some form of coroutine/greenthread/suspend engine to correctly work. Again, check uWSGI asynchronous/non-blocking modes (updated to uWSGI 1.9)
Coro::AnyEvent¶
The Perl/PSGI plugin is one of the most ancient in the uWSGI project, but used to not support the async mode in advanced ways.
Thanks to the new uWSGI asynchronous/non-blocking modes (updated to uWSGI 1.9) mode, a Coro::Anyevent (coroae) loop engine has been added.
To build it you need the Coro::Anyevent package (you can use cpanm to get it), then just add –coroae <n> to your options where <n> is the number of async cores to spawn.
The JVM plugin¶
We finally have a truly working JVM infrastructure in uWSGI 1.9. Check the new docs at JVM in the uWSGI server (updated to 1.9) Improved The JWSGI interface support is available as well as the new Clojure The Clojure/Ring JVM request handler plugin
The Mono ASP.NET plugin¶
The first Mono plugin attempt (in 2010) was a total failure. Now we have a new shining implementation.
Check docs here The Mono ASP.NET plugin
Language independent HTTP body management¶
One of the most annoying task in writing uWSGI request plugins, was re-implementing the management of HTTP body reader every time.
The new non-blocking api added 3 simple generic C/C++/Obj-C functions to deal with it in a language independent way:
char *uwsgi_request_body_read(struct wsgi_request *wsgi_req, ssize_t hint, ssize_t *rlen);
char *uwsgi_request_body_readline(struct wsgi_request *wsgi_req, ssize_t hint, ssize_t *rlen);
void uwsgi_request_body_seek(struct wsgi_request *wsgi_req, off_t pos);
they automatically manage post-buffering, non-blocking and upload progress.
All of the request plugins have been updated to the new api
Faster uwsgi/HTTP/FastCGI/SCGI native sockets¶
All of the –socket protocol parsers have been rewritten to be faster (less syscall usage) and to use less memory. They are now more complex, but you should note (on loaded site) a reduced amount of syscalls per-request.
The SCGI protocol support has been added, while a NPH fastcgi mode (where the output is HTTP instead of cgi) has been implemented.
The FastCGI protocol now supports true sendfile() usage
The old behaviour of storing the request body for HTTP and FastCGI on a temp file, has been removed (unless you use post-buffering). This means you can now have upload progress with protocols other than uwsgi.
Request logging VS err logging¶
One of the most annoying problem with older uWSGI releases was the lack of ability to easily split request logs from error logs. You can now create a logger and map it only to request logging:
[uwsgi]
req-logger = syslog
...
As an example you may want to send request logging to syslog and redis, and error log to mongodb (on the foo.bar collection):
[uwsgi]
req-logger = syslog
req-logger = redislog:127.0.0.1:6269
logger = mongodblog:127.0.0.1:9090,foo.bar
...
Or just use (boring) files
[uwsgi]
req-logger = file:/tmp/reqlog
logger = file:/tmp/errlog
...
Chain reloading¶
When in lazy/lazy_apps mode, you can simply destroy a worker to force it to reload the application code.
A new reloading system named “chain reload”, allows you to reload one worker at time (opposed to the standard way where all of the workers are destroyed in bulk)
Chain reloading can only be triggered via “touch”: –touch-chain-reload <file>
Offloading improvements¶
Offloading appeared in uWSGI 1.4 and is one of the most loved features. In 1.9 we added a new engine: “write”, that allows you to offload the write of files on disk. A general function api uwsgi.offload() is on work, to allow applications to access the offload engine. All of the uWSGI parts sending static files (including the language-specific implementations, like WSGI wsgi.file_wrapper) have been extended to automatically use offloading if available. This means you can use your Framework’s way for serving static files, without losing too much performance and (more important) without blocking your workers.
Better static files management/serving¶
uWSGI 1.9 received many improvements in static file serving.
You may want to check: Serving static files with uWSGI (updated to 1.9)
For syadmins one of the most interesting new features is the ability to use the uWSGI new generation cacheing (see below) to store request -> absolute_path mappings
The New Generation Cache subsystem (cache2)¶
The uWSGI caching subsystem has been completely rewritten to be a more general purpose in-memory key/value store. The old caching subsystem has been re-built on top of it, and is now more of a general “web caching” system. The new cache subsystem allows you to control all of the aspects of your memory store, from the hashing algorithm to the amount of blocks.
You can now have multiple caches per-instance (identified by name)
To create a cache just use the new –cache2 option
[uwsgi]
cache2 = name=mycache,items=100
cache2 = name=faster,items=200,hash=murmur2,keysize=100,blocksize=4096
cache2 = name=fslike,items=1000,keysize=256,bitmap=1,blocks=2000,blocksize=8192
...
In this example we created 3 caches: mycache, faster and fslike.
The first one is a standard old-style, cache able to store 100 items of a maximum size of 64k with keys limited to 2048 bytes using djb33x hashing algorithm The second one use the murmur2 hashing algorithm, each key can be at most 1000 bytes, can store 200 items of max 4k The last one works like a filesystem, where each item can span over multiple blocks. That means, fslike cache can save lot of memory for boject of different size (but it will be slower than non-bitmap based caches)
The options you can specify in cache2 are the following:
name
the name of the cache (must be unique) REQUIRED
items/max_items/maxitems
set the max number of items the cache can store REQUIRED
blocksize
set the size of a single block
blocks
set the number of blocks (used only in bitmap mode)
hash
set the hashing algorithm, currently supported: djbx33 and murmur2
hashsize/hash_size
set the size of the hash table (default to 65536 items)
keysize/key_size
set the max size of a key
store
set the filename in which to persistent store the cache
store_sync/storesync
set the frequency (in seconds) at which msync() is called to flush cache on disk (when in persistent mode)
node/nodes
the new cache subsystem can send cache updates via udp packet. With this option you set one or more (separated with ;) udp addresses on which to send updates
sync
set it to the address of a cache server. Its whole content will be copied in the new cache (use it for initial sync)
udp/udp_servers/udp_server/udpservers/udpserver
bind to the specified udp addresses (separated with ;) listening for cache updates
bitmap
enable botmap mode (set it to 1)
If you are asking yourself why such low-level tunings exists, you have to take in account that the new caching subsystem is used in lot of areas, so for different needs you may want different tuning. Just check Scaling SSL connections (uWSGI 1.9) for an example
The old –cache-server option has been removed. The threaded cache server added in 0.9.8 has been completed superseeded by the new non blocking infrastructure. If you load the “cache” plugin (enabled by default in monolithic build) a cache server will be available and managed by the workers.
Update docs are available here The uWSGI caching framework
The Legion subsystem¶
The Legion subsystem is a new whole addition to the uWSGI project. It has superseeded the old Clustering subsystem (which has been removed in 1.9). It implements a quorum system to manage shared resources in clustered environments. Docs are already available: The uWSGI Legion subsystem
Cygwin (windows) support¶
uWSGI can be compiled on windows machines using the cygwin POSIX emulation system. The event subsystem uses simple poll() (mapped to select() on cygwin), while the lock engine uses windows mutexes. Albeit from our tests it looks pretty solid, we consider the porting still “experimental”
Advanced Exceptions subsystem¶
As well as the request body language-independent management, an exception management system has been added. Currently supported only in the Python and Ruby plugins, allows language-independent handling of exceptions cases (like reloading on a specific exception). The –catch-exception option has been improved to show lot of useful information. Just try it (in development !!!) Future development will allow automatic sending of exception to system like Sentry or Airbrake.
SPDY, SSL and SNI¶
Exciting new features have been added to the SSL system and the HTTP router
SPDY support (currently only version 3) will get lot of users attention, but SNI subsystem is what sysadmins will love
Preliminary docs are available
SNI - Server Name Identification (virtual hosting for SSL nodes)
HTTP router keepalive, auto-chunking, auto-gzip and transparent websockets¶
Many users have started using the HTTP/HTTPS/SPDY router in production, so we started adding features to it. Remember this is ONLY a router/proxy, NO I/O is allowed, so you may not be able to throw away your old-good webserver.
The new options:
--http-keepalive
enable HTTP/1.1 keepalive connections
--http-auto-chunked
for backend response without content-length (or chunked encoding already enabled), transform the output in chunked mode to maintain keepalive connections
--http-auto-gzip
automatically gzip content if uWSGI-Encoding header is set to gzip, but content size (Content-Length/Transfer-Encoding) and Content-Encoding are not specified
--http-websockets
automatically detect websockets connections to put the request handler in raw mode
The SSL router (sslrouter)¶
A new corerouter has been added, it works in the same way as the rawrouter one, but will terminate ssl connections. The sslrouter can use sni for implementing virtualhosting (using the –sslrouter-sni option)
Websockets api¶
20Tab S.r.l. (a company working on HTML5 browsers game) sponsored the development of a fast language-independent websockets api for uWSGI. The api is currently in very good shape (and maybe faster than any other implementation). Docs still need to be completed but you may want to check the following examples (a simple echo):
https://github.com/unbit/uwsgi/blob/master/tests/websockets_echo.pl (perl)
https://github.com/unbit/uwsgi/blob/master/tests/websockets_echo.py (python)
https://github.com/unbit/uwsgi/blob/master/tests/websockets_echo.ru (ruby)
New Internal Routing (turing complete ?)¶
The internal routing subsystem has been rewritten to be ‘programmable’. You can see it as an apache mod_rewrite with steroids (and goto ;) Docs still need to be ported, but the new system allows you to modify/filter CGI vars and HTTP headers on the fly, as well as managing HTTP authentication and caching.
Updated docs here (still work in progress) uWSGI internal routing
Emperor ZMQ plugin¶
A new imperial monitor has been added allowing vassals to be governed over zeromq messages:
http://uwsgi-docs.readthedocs.org/en/latest/ImperialMonitors.html#zmq-zeromq
Total introspection via the stats server¶
The stats server now exports all of the request variables of the currently running requests for each core, so it works in multithread mode too. This is a great way to inspect what your instance is doing and how it does it In the future, uwsgitop could be extended to show the currently running request in realtime.
Nagios plugin¶
Ping requests sent using nagios plugin will no longer be counted in apps request stats. This means that if application had –idle option enabled nagios pings will no longer prevent app from going to idle state, so starting with 1.9 –idle should be disabled when nagios plugin is used. Otherwise app may be put in idle state just before nagios ping request, when ping arrives it needs to wake from idle and this might take longer than ping timeout, causing nagios alerts.
Removed and deprecated features¶
- The –app option has been removed. To load applications on specific mountpoints use the –mount option
- The –static-offload-to-thread option has been removed. Use the more versatile –offload-threads
- The grunt mode has been removed. To accomplish the same behaviour just use threads or directly call fork() and uwsgi.disconnect()
- The send_message/recv_message api has been removed (use language-supplied functions)
Working On, Issues and regressions¶
We missed the timeline for a bunch of expected features:
- SPNEGO support, this is an internal routing instruction to implement SPNEGO authentication support
- Ruby 1.9 fibers support has been rewritten, but need tests
- Erlang support did not got required attention, very probably will be post-poned to 2.0
- Async sleep api is incomplete
- SPDY push is still not implemented
- RADIUS and LDAP internal routing instructions are unimplemented
- The channel subsystem (required for easy websockets communications) is still unimplemented
In addition to this we have issues that will be resolved in upcoming minor releases:
- the –lazy mode lost usefulness, now it is like –lazy-apps but with workers-reload only policy on SIGHUP
- it looks like the JVM does not cooperate well with coroutine engines, maybe we should add a check for it
- Solaris and Solaris-like systems did not get heavy testing
Special thanks¶
A number of users/developers helped during the 1.9 development cycle. We would like to make special thanks to:
Łukasz Mierzwa (fastrouters scalability tests)
Guido Berhoerster (making the internal routing the new skynet)
Riccardo Magliocchetti (static analysis)
André Cruz (HTTPS and gevent battle tests)
Mingli Yuan (Clojure/Ring support and test suite)
联系信息¶
Mailing list | http://lists.unbit.it/cgi-bin/mailman/listinfo/uwsgi |
Gmane mirror | http://dir.gmane.org/gmane.comp.python.wsgi.uwsgi.general |
IRC | #uwsgi @ irc.freenode.org. The owner of the channel is unbit. |
http://twitter.com/unbit | |
Commercial support | http://unbit.com/ |
.
商业支持¶
你可以从 http://unbit.com 购买商业支持
捐助¶
uWSGI 的开发由意大利互联网服务提供商 Unbit 以及它的客户 支持。你可以购买商业支持和许可。如果你不是 Unbit 的客户或者你不想购买一个商业的 uWSGI 许可,你可以考虑捐助。显然你可以在你的捐助中随意询问想要的新特性。
我们将会把支持开发新特性的人加到 credit 里。
请看 old uWSGI site 来获取捐助链接。 你可以通过 GitTip 捐助。