Using Lua/WSAPI with uWSGI¶
Compilation notes¶
Before compiling the plugin take a look at the
plugins/lua/uwsgiplugin.py configuration file. If you have installed
Lua in some exotic directory you may need to adjust the CFLAGS and LIBS
values.
For example, on a Debian/Ubuntu system you should use something like this:
import os, sys
NAME='lua'
CFLAGS = ['-I/usr/include/lua5.1/']
LDFLAGS = []
GCC_LIST = ['lua_plugin']
LIBS = ['-llua5.1']
The lua.ini buildconf will build uWSGI with embedded Lua support. The
luap.ini buildconf will build Lua support as a plugin.
python uwsgiconfig.py --build lua # embedded
python uwsgiconfig.py --build luap # plugin
# if you have already build the uWSGI core with the default config file...
python uwsgiconfig.py --plugin plugins/lua
# or if you have used another config file (for example core.ini)
python uwsgiconfig.py --plugin plugins/lua core
Note
It should also be possible to compile Lua against luajit – just edit the library and header paths in the plugins/lua/uwsgiplugin.py file.
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 -s :3031 -M -p 4 --lua pippo.lua -m
The Lua plugin’s official uwsgi protocol modifier number is 6, so remember
to set it in your web server configuration with the
uWSGIModifier1/uwsgi_modifier1 directive.
Abusing coroutines¶
One of the most exciting feature of Lua is coroutine (cooperative
multithreading) support. uWSGI can benefit from this using its async core. 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 -s :3031 -M -p 4 --lua pippo.lua -m --async 8
And just like that, you can manage 8 concurrent requests within a single worker!
Threading¶
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 -s :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 after each request. An option to set the frequency at which the GC runs will be available soon.