NGINX + Lua
Using NGINX/OpenResty
with embedded Lua
Epifanov Ivan,
Lead Developer
Lua
• Fast
• Light (241K)
• Simple
• Powerfull
• Embeddable
• Portable
Lua do Pesadelo
(Nightmare Moon)
Who uses Lua?
• Games: WoW, Angry Birds, Baldur's
Gate, Civilization V, Crysis, HoMM V,
L.A. Noire, etc.
• Software: Lightroom, MySQL proxy,
MySQL Workbench, Celestia, Far
Manager, GIMP, MediaWiki, etc.
• Misc.: Mercedes Autos, LG Smart TV,
Space Shuttle Gas Detection, etc.
LuaJIT
• JIT compilation for lua code
• High performance (3-100x)
• Low memory footprint
• Crossplatform
• Cross-architecture (x86, ARM, MIPS,
PPC
• local ffi = require("ffi")
ffi.cdef[[
int printf(const char *fmt, ...);
]]
ffi.C.printf("Hello %s!", "world")
OpenResty/NGINX-LUA
• NGINX bundle with luajit and additional
modules.
• Developed by TaoBao and CloudFlare
• Access to every processing stage of
nginx
• Non-blocking without callback-hell
• co-sockets, shared pool and sub-
requests
Full control
• init_by_lua(file)
• init_worker_by_lua(file)
• set_by_lua(file)
• log_by_lua(file)
• content_by_lua(file)
• header_filter_by_lua(file)
• access_by_lua(file)
• rewrite_by_lua(file)
What
• Databases: lua-resty-redis, lua-resty-
mysql, etc.
• Templates: lua-resty-template, etlua
• Frameworks: Lapis
• Preprocessors: Moonscript
(coffiescript-like syntax)
Where
• Image Server, for image resizes (now)
• Image Server, for queue, API, control
(in the future)
• Mobile detection
• EDGE-side block cache
• Maybe parts of SSO API, like cookie-
setter
For example...
Redis RESTfull api for userscores
• GET /scores
return userid list
• GET/PUT/POST/DELETE /scores/<id>
manipulate user scores
Init
http {
...
init_by_lua '
json = require "cjson";
redis = require "resty.redis"
';
...
server {
...
set $redis_host "192.168.2.104";
set $redis_port "6379";
set $redis_pass "nightmaremoon";
...
}
...
}
Init
local red = redis:new()
local ok, err = red:connect(ngx.var.redis_host, ngx.var.redis_port)
if not ok then
ngx.say("failed to connect: ", err)
return
end
local ok, err = red:auth(ngx.var.redis_pass)
if not ok then
ngx.say("failed to auth: ", err)
return
end
GET /scores
local keys, err = red:keys("*")
local scores = {}
for i, key in ipairs(keys) do
scores[key] = red:get(key)
end
ngx.header.content_type = "text/plain"
ngx.say(json.encode(scores))
GET /score/<userid>
location ~* ^/keys/(?<key>.*)$
{
content_by_lua '
-- ... (connect and auth)
local val, err = red:get("user:score:" ..
ngx.var.key)
ngx.say(json.encode({ value = val }))
';
}
DELETE /scores/<userid>
local method = ngx.req.get_method()
if method == "GET" then
...
elseif method == "PUT" then
...
elseif method == "DELETE" then
red:delete("user:score:" .. ngx.var.key)
ngx.say(json.encode({result = "success"}))
end
PUT /scores/<userid>
data =
json.decode(ngx.req.get_body_data())
red:set("user:score:" .. ngx.var.key,
data.value)
ngx.req.set_method(ngx.HTTP_GET)
ngx.exec("/scores/" .. ngx.var.key)
GET /scores/max
local max = 0
local keys = redis.call('keys', 'user:score:*');
for i, key in ipairs(keys) do
local value = tonumber(redis.call('get', key));
if value > max then
max = value
end
end
return max
GET /scores/max
local sha, err = red:script("load","...")
local val, err = red:evalsha(sha, 0)
ngx.say(json.encode({ max = val }))
GET /scores/max/<id1>,...,<idN>
local max = 0
for key in string.gmatch(ngx.var.key, '([^,]+)') do
local res = ngx.location.capture("/scores/" .. key)
data = json.decode(res.body)
if tonumber(data.value) > max then
max = tonumber(data.value)
end
end
ngx.say(json.encode({ max = max }))
What to read
• https://gist.github.com/isage/509d99cf4
def36014f5c
• http://wiki.nginx.org/HttpLuaModule
• http://openresty.org
• search github for lua-resty*
??????
Questions?
PROFIT!
2015, Epifanov Ivan
http://github.com/isage/

Nginx-lua

  • 1.
    NGINX + Lua UsingNGINX/OpenResty with embedded Lua Epifanov Ivan, Lead Developer
  • 2.
    Lua • Fast • Light(241K) • Simple • Powerfull • Embeddable • Portable Lua do Pesadelo (Nightmare Moon)
  • 3.
    Who uses Lua? •Games: WoW, Angry Birds, Baldur's Gate, Civilization V, Crysis, HoMM V, L.A. Noire, etc. • Software: Lightroom, MySQL proxy, MySQL Workbench, Celestia, Far Manager, GIMP, MediaWiki, etc. • Misc.: Mercedes Autos, LG Smart TV, Space Shuttle Gas Detection, etc.
  • 4.
    LuaJIT • JIT compilationfor lua code • High performance (3-100x) • Low memory footprint • Crossplatform • Cross-architecture (x86, ARM, MIPS, PPC • local ffi = require("ffi") ffi.cdef[[ int printf(const char *fmt, ...); ]] ffi.C.printf("Hello %s!", "world")
  • 5.
    OpenResty/NGINX-LUA • NGINX bundlewith luajit and additional modules. • Developed by TaoBao and CloudFlare • Access to every processing stage of nginx • Non-blocking without callback-hell • co-sockets, shared pool and sub- requests
  • 6.
    Full control • init_by_lua(file) •init_worker_by_lua(file) • set_by_lua(file) • log_by_lua(file) • content_by_lua(file) • header_filter_by_lua(file) • access_by_lua(file) • rewrite_by_lua(file)
  • 7.
    What • Databases: lua-resty-redis,lua-resty- mysql, etc. • Templates: lua-resty-template, etlua • Frameworks: Lapis • Preprocessors: Moonscript (coffiescript-like syntax)
  • 8.
    Where • Image Server,for image resizes (now) • Image Server, for queue, API, control (in the future) • Mobile detection • EDGE-side block cache • Maybe parts of SSO API, like cookie- setter
  • 9.
    For example... Redis RESTfullapi for userscores • GET /scores return userid list • GET/PUT/POST/DELETE /scores/<id> manipulate user scores
  • 10.
    Init http { ... init_by_lua ' json= require "cjson"; redis = require "resty.redis" '; ... server { ... set $redis_host "192.168.2.104"; set $redis_port "6379"; set $redis_pass "nightmaremoon"; ... } ... }
  • 11.
    Init local red =redis:new() local ok, err = red:connect(ngx.var.redis_host, ngx.var.redis_port) if not ok then ngx.say("failed to connect: ", err) return end local ok, err = red:auth(ngx.var.redis_pass) if not ok then ngx.say("failed to auth: ", err) return end
  • 12.
    GET /scores local keys,err = red:keys("*") local scores = {} for i, key in ipairs(keys) do scores[key] = red:get(key) end ngx.header.content_type = "text/plain" ngx.say(json.encode(scores))
  • 13.
    GET /score/<userid> location ~*^/keys/(?<key>.*)$ { content_by_lua ' -- ... (connect and auth) local val, err = red:get("user:score:" .. ngx.var.key) ngx.say(json.encode({ value = val })) '; }
  • 14.
    DELETE /scores/<userid> local method= ngx.req.get_method() if method == "GET" then ... elseif method == "PUT" then ... elseif method == "DELETE" then red:delete("user:score:" .. ngx.var.key) ngx.say(json.encode({result = "success"})) end
  • 15.
    PUT /scores/<userid> data = json.decode(ngx.req.get_body_data()) red:set("user:score:".. ngx.var.key, data.value) ngx.req.set_method(ngx.HTTP_GET) ngx.exec("/scores/" .. ngx.var.key)
  • 16.
    GET /scores/max local max= 0 local keys = redis.call('keys', 'user:score:*'); for i, key in ipairs(keys) do local value = tonumber(redis.call('get', key)); if value > max then max = value end end return max
  • 17.
    GET /scores/max local sha,err = red:script("load","...") local val, err = red:evalsha(sha, 0) ngx.say(json.encode({ max = val }))
  • 18.
    GET /scores/max/<id1>,...,<idN> local max= 0 for key in string.gmatch(ngx.var.key, '([^,]+)') do local res = ngx.location.capture("/scores/" .. key) data = json.decode(res.body) if tonumber(data.value) > max then max = tonumber(data.value) end end ngx.say(json.encode({ max = max }))
  • 19.
    What to read •https://gist.github.com/isage/509d99cf4 def36014f5c • http://wiki.nginx.org/HttpLuaModule • http://openresty.org • search github for lua-resty*
  • 20.
  • 21.