All notes
Uwsgi

Basics

51cto.

About WSGI

Wikipedia. The Web Server Gateway Interface (WSGI) is a specification for simple and universal interface between web servers and web applications or frameworks for the Python programming language. It has been adopted as a standard for Python web application development.

Common Gateway Interface, CGI, a standard environment for web servers to interface with executable programs installed on a server that generate web pages dynamically.

project目录建立myapp.py,使得application调用框架的wsgi接口,比如web.py就是:


app = web.application(urls, globals())
appapplication = app.wsgifunc()

再比如django就是:


from django.core.handlers.wsgi import WSGIHandler
application = WSGIHandler()

Build

ReadTheDocs.


sudo yum install -y python-devel
sudo yum install -y libuuid-devel
CPUCOUNT=2 python uwsgiconfig.py --build

Command line

StackOverflow.


# -s, --socket
uwsgi --module run:app -s 127.0.0.1:50010

# --http
uwsgi --module run:app --http 127.0.0.1:50010

uwsgi --ini uwsgi.ini

uwsgi --reload /tmp/uwsgiProcess.pid

# 并发4个线程:
uwsgi -s :9090 -w myapp -p 4 

# 主控制线程+4个线程:
uwsgi -s :9090 -w myapp -M -p 4 

# 执行超过30秒的client直接放弃:
uwsgi -s :9090 -w myapp -M -p 4 -t 30 

# 限制内存空间128M:
uwsgi -s :9090 -w myapp -M -p 4 -t 30 --limit-as 128 

# 服务超过10000个req自动respawn:
uwsgi -s :9090 -w myapp -M -p 4 -t 30 --limit-as 128 -R 10000 

# 后台运行等:
uwsgi -s :9090 -w myapp -M -p 4 -t 30 --limit-as 128 -R 10000 -d uwsgi.log

# -H, --home, --venv, --virtualenv, --pyhome
uwsgi -H /path/to/your/virtualenv

# The uwsgi.ini files are under /etc/uwsgi/vassals/.
# You can add the line at /etc/rc.local before the line "exit 0".
sudo uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data

uwsgi.ini


[uwsgi]
# variables
projName = example
projectdomain = example.com
base = /var/www/example.com

# config
venv = %(base)/venv
pythonpath = %(base)/src/%(projName)
module = %(projName).wsgi
master = true
socket = /tmp/%(projectdomain).sock
chmod-socket = 666
# logto = %(base)/logs/uwsgi.log
daemonize = /var/log/uwsgi/yourproject.log # background the process & log
# clear environment on exit
# vacuum = true

Django project system could be:

projDir
	manage.py
	appDir
		settings.py
		uwsgi.ini
		wsgi.py
		urls.py
		...

Deploy under a subpath

uwsgiDoc: Nginx.

The WSGI standard dictates that SCRIPT_NAME is the variable used to select a specific application. Unfortunately nginx is not able to rewrite PATH_INFO accordingly to SCRIPT_NAME.
For such reason you need to instruct uWSGI to:

  1. Map specific apps in the so called "mountpoint"
  2. and rewrite SCRIPT_NAME and PATH_INFO automatically. E.g. the app itself (eventually using a WSGI/Rack/PSGI middleware) can rewrite SCRIPT_NAME and PATH_INFO.

Set directly in uwsgi.ini as follows:


[uwsgi]
chdir = %d
master = true
processes = 2
pidfile=/tmp/uwsgi.pid
daemonize=$(HOME)/uwsgi.log

socket = /tmp/proj.sock
chmod-socket = 666

callable = app
mount = /proj=proj.py
manage-script-name = true

# Mount under subpath "/foo" in URL
uwsgi -s /tmp/uwsgi.sock -w app --chown-socket=www-data:www-data --manage-script-name --mount=/foo=/www/app.py
uwsgi_modifier1 deprecated

StackOverflow: modifier1 not working.
uwsgiDoc: Nginx says:

Note: ancient uWSGI versions used to support the so called "uwsgi_modifier1 30" approach. Do not do it. it is a really ugly hack. uwsgi_param SCRIPT_NAME /admin; and uwsgi_modifier1 30; is now deprecated.

WCF NOTE: the following is obsolete in newer uwsgi! Use previous method. CodePainters. StackOverflow.


location /admin/ {
	# uwsgi_pass /path/to/uwsgi.socket;
	uwsgi_pass unix:///tmp/example.com.sock
	include uwsgi_params;
	# SCRIPT_NAME, CGI parameter. Let this name be removed from the beginning of the PATH_INFO parameter by the WSGI environment.
	uwsgi_param SCRIPT_NAME /admin;
	uwsgi_modifier1 30;
}

# Django media
# For a large deployment it is considered good practice to let one server handle static/media files, and another handle Django application.
location /media  {
	alias /path/to/your/mysite/media;  # your Django project's media files
}
location /static {
	alias /path/to/your/mysite/static; # your Django project's static files
}

The modifier1 is simply used to denote the uWSGI protocol's packet type. According to the table, value 30 means: "Standard WSGI request followed by the HTTP request body. The PATH_INFO is automatically modified, removing the SCRIPT_NAME from it." Packets of type 0 (default) and 30 differ only in how SCRIPT_INFO is handled.

upstart


# file: /etc/init/uwsgi.conf
description "uWSGI starter"

start on (local-filesystems and runlevel [2345])
stop on runlevel [016]

respawn

# home - is the path to our virtualenv directory
# pythonpath - the path to our django application
# module - the wsgi handler python script

exec /path/to/your/virtualenv/bin/uwsgi \
--uid your-user \
--home /path/to/your/virtualenv \
--pythonpath /path/to/your/django/project \
--socket /tmp/uwsgi.sock \
--chmod-socket \
--module wsgi \
--logdate \
--optimize 2 \
--processes 2 \
--master \
--logto /path/to/your/log/uwsgi.log

Configurations

Options

Use uwsgi --help to find all the options.

The configuration system is unified, so each command line option maps 1:1 with entries in the config files.


uwsgi --http-socket :9090 --psgi myapp.pl

is same as


[uwsgi]
http-socket = :9090
psgi = myapp.pl

You can drop privileges using the uid and gid options.

Variables

ReadTheDocs.


# Environment vars. $(VARNAME)
foobar = $(PATH)
foobar = $(HOME)

# @(FILENAME). nodename value will be the content of /etc/hostname
nodename = @(/etc/hostname)
; read from http
test = @(http://example.com/hello)

# placeholder substitution.
# The content of foobar will be mapped to the content of socket.
socket = :3031
foobar = %(socket)

# magic variable.
# The content of my_config_file will be set to %p value (the current file's absolute path) as soon as it is parsed.
my_config_file = %p

Placeholders

Placeholders can be assigned directly, or using the set-placeholder / set-ph option. These latter options can be useful to:


[uwsgi]
; These are placeholders...
my_funny_domain = uwsgi.it
set-ph = max_customer_address_space=64
set-placeholder = customers_base_dir=/var/www
; And these aren't.
socket = /tmp/sockets/%(my_funny_domain).sock
chdir = %(customers_base_dir)/%(my_funny_domain)
limit-as = %(max_customer_address_space)

Placeholders are accessible, like any uWSGI option, in your application code via uwsgi.opt.


import uwsgi
print uwsgi.opt['customers_base_dir']

Magic variables

ReadTheDocs.

Signals

ReadTheDocs.

SignalDescriptionConvenience command
SIGHUP gracefully reload all the workers and the master process--reload
SIGTERMbrutally reload all the workers and the master process(use --die-on-term to respect the convention of shutting down the instance)
SIGINT immediately kill the entire uWSGI stack--stop
SIGQUITimmediately kill the entire uWSGI stack 

Note: there are better ways to manage your instances than signals, as an example the master-fifo is way more robust (ReadTheDocs), where 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.


## Reload

# using kill to send the signal
kill -HUP `cat /tmp/project-master.pid`
# or the convenience option --reload
uwsgi --reload /tmp/project-master.pid
# or if uwsgi was started with touch-reload=/tmp/somefile
touch /tmp/somefile

## Stop

kill -INT `cat /tmp/project-master.pid`
# or for convenience...
uwsgi --stop /tmp/project-master.pid

Deploy with nginx

ReadTheDocs.


# test.py
def application(env, start_response):
	start_response('200 OK', [('Content-Type','text/html')])
	return [b"Hello World"] # python3
	#return ["Hello World"] # python2

# Run this by:
# uwsgi --http :8000 --wsgi-file test.py
# Run Django by:
# uwsgi --http :8000 --module mysite.wsgi

FAQ

Options --wsgi-file and --module not recognized

SO: uwsgi options modules not recognized.


# Usually it means that the python plugin for uWSGI isn't installed or loaded. To verify run:
uwsgi --plugins-list

# On Debian or Ubuntu
apt-get install uwsgi-plugin-python

# On Linux Alpine
apk add --update uwsgi-python
# List plugins:
uwsgi --plugin /usr/lib/uwsgi/python_plugin.so --plugins-list
# Or
uwsgi --plugins-dir /usr/lib/uwsgi/ --need-plugin python --plugins-list

emperor

uwsgi-docs: Emperor.


# NOTE: the emperor glob argument must be included in quotes, otherwise shell will expand before uwsgi!!
# This is good:
uwsgi --emperor "*/uwsgi.ini" --master-fifo=/tmp/uwsgi --daemonize ${HOME}/uwsgi.log
# This is not good:
uwsgi --emperor */uwsgi.ini --master-fifo=/tmp/uwsgi --daemonize ${HOME}/uwsgi.log
dir:// - scan a directory for uWSGI config files
glob:// - monitor a shell pattern

Wcf Note: "dir" specify only a single directory, so "glob" may be more useful. But their syntaxes are the same, see example below.


# Scan config files under the dir
uwsgi --emperor /etc/uwsgi/vassals

# Scan all config files matching the glob
uwsgi --emperor /opt/apps/app*/app*.*
uwsgi --emperor "/etc/vassals/domains/*/conf/uwsgi.xml"

# runs uWSGI's Emperor mode looking for any uwsgi.ini files for all apps
uwsgi --emperor "/var/www/*/conf/uwsgi.ini"

Any new additions will be automatically started, no need to restart Emperor itself. If an existing uwsgi.ini is modified/touched, it is also automatically restarted.

FAQ

"chdir=%d" works only once in multi-app deployment?

wcfNote: emperor respects each settings in each uwsgi.ini.

The Master-FIFO

uwsgi-docs: MasterFIFO.

The disadvantages to use signals:

Only the uid running the master has write access to the fifo.
The FIFO is created in non-blocking modes and recreated by the master every time a client disconnects.


echo r > /tmp/yourfifo

# You can send multiple commands in one shot.
# add 3 workers and print stats
echo +++s > /tmp/yourfifo

uWSGI supports up to 10 different FIFO files. By default the first specified is bound (mapped as '0').


[uwsgi]
master-fifo = /tmp/fifo0
master-fifo = /tmp/fifo1
master-fifo = /var/run/foofifo
processes = 2

# By default /tmp/fifo0 will be allocated

echo 1 > /tmp/fifo0
# Then the /tmp/fifo1 file will be bound.

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').

Available commands:

Smooth reload server

Concepts

Harakiri


uwsgi --help | grep harakiri
# -t|--harakiri       set harakiri timeout
# --harakiri-verbose  enable verbose mode for harakiri
# --cron-harakiri     set the maximum time (in seconds) we wait for cron command to complete