SlideShare a Scribd company logo
Using ngx_lua in UPYUN 
Monkey Zhang (timebug) 
2014.11 @ Beijing OSC
Hello Nginx 
nginx [engine x] is an HTTP and reverse proxy server, as 
well as a mail proxy server, written by Igor Sysoev.
A simple example 
$ ./configure --prefix=/opt/nginx  
--add-module=/path/to/echo-nginx-module 
http { 
server { 
listen 8080; 
location /hello { 
set $foo "hello"; 
echo $foo; 
set $foo "world"; 
echo $foo; 
} 
} 
} 
$ curl http://localhost:8080/hello 
world 
world
In Fact … 
http { 
server { 
listen 8080; 
location /hello { 
set $foo "hello"; 
set $foo "world"; 
echo $foo; 
echo $foo; 
} 
} 
} 
✦ REWRITE PHASE: set (ngx_http_rewrite_module) 
✦ CONTENT PHASE: echo (echo-nginx-module)
Nginx Internals: 
HTTP request phase handlers 
✦ POST READ PHASE 
✦ SERVER REWRITE PHASE 
✦ FIND CONFIG PHASE 
✦ REWRITE PHASE 
✦ POST REWRITE PHASE 
✦ PRE ACCESS PHASE 
✦ ACCESS PHASE 
✦ POST ACCESS PHASE 
✦ TRY FILES PHASE 
✦ CONTENT PHASE 
✦ LOG PHASE
Why Nginx Module 
Development is Not Easy ?
A true story 
$ ./configure --prefix=/opt/nginx  
--add-module=/path/to/echo-nginx-module  
--add-module=/path/to/base64-nginx-module 
http { 
server { 
listen 8080; 
location /base64 { 
base64 on; 
base64_max_length 10485760; 
echo "hello world"; 
} 
} 
} 
$ curl http://localhost:8080/base64 
aGVsbG8gd29ybGQK
500 lines C
If is Evil 
http://agentzh.blogspot.jp/2011/03/how-nginx-location-if-works.html 
http { 
server { 
listen 8080; 
location /if { 
set $foo 1; 
if ($foo = 1) { 
set $foo 2; 
echo "foo = $foo"; 
} 
set $foo 3; 
proxy_pass http://127.0.0.1:$server_port/$foo; 
} 
location ~ /(d+) { 
echo "bar = $1"; 
} 
} 
} 
$ curl http://localhost:8080/if 
foo = 3
If is Evil: 
How it works 
✦ REWRITE PHASE 
set $foo 1; 
if ($foo = 1) { 
set $foo 2; 
} 
set $foo 3; 
✦ CONTENT PHASE 
if ($foo = 1) { 
echo "foo = $foo"; 
}
If is Evil: 
Break ngx_rewite Directives 
✦ REWRITE PHASE 
set $foo 1; 
if ($foo = 1) { 
set $foo 2; 
break; 
} 
✦ CONTENT PHASE 
if ($foo = 1) { 
echo "foo = $foo"; 
} 
http { 
server { 
listen 8080; 
location /if { 
set $foo 1; 
if ($foo = 1) { 
set $foo 2; 
break; 
echo "foo = $foo"; 
} 
set $foo 3; 
proxy_pass http://127.0.0.1:$server_port/$foo; 
} 
location ~ /(d+) { 
echo "bar = $1"; 
} 
} 
} 
$ curl http://localhost:8080/if 
foo = 2
Hello Lua 
Lua is a powerful, fast, lightweight, embeddable 
scripting language.
$ ./configure --prefix=/opt/nginx  
--add-module=/path/to/lua-nginx-module 
http { 
server { 
listen 8080; 
location /add { 
set $res ''; 
rewrite_by_lua ' 
local a = tonumber(ngx.var.arg_a) or 0 
local b = tonumber(ngx.var.arg_b) or 0 
ngx.var.res = a + b 
'; 
content_by_lua ' 
ngx.say(ngx.var.res) 
'; 
} 
} 
} 
$ curl 'http://localhost:8080/add?a=6&b=7' 
13
LuaJIT 
LuaJIT is a Just-In-Time Compiler (JIT) for the Lua 
programming language.
How it works 
LuaJIT VM embedded into the Nginx
Base64 Filter by Lua 
http { 
lua_package_path “$prefix/app/src/?.lua;;"; 
server { 
listen 8080; 
location /base64 { 
set $b64_en ''; 
set $b64_e0 ''; 
set $b64_e1 ''; 
echo_duplicate 1000 hello; 
header_filter_by_lua ' 
ngx.header.content_length = nil -- ((n + 2) / 3 ) * 4 
ngx.header.content_type = "text/plain" 
ngx.header.content_transfer_encoding = "base64" 
'; 
body_filter_by_lua_file app/src/b64_body_filter.lua; 
} 
} 
}
Base64 Filter by Lua: 
local chunk = ngx.arg[1] Chunk by Chunk 
local e0 = ngx.var.b64_e0 or '' 
local e1 = ngx.var.b64_e1 or '' 
local en = tonumber(ngx.var.b64_en) or 0 
if en == 1 then 
chunk = e0 .. chunk 
elseif en == 2 then 
chunk = e0 .. e1 .. chunk 
end 
if not ngx.arg[2] then 
en = #chunk % 3 
if en == 1 then 
e0 = chunk:sub(-1) 
elseif en == 2 then 
e1 = chunk:sub(-1) 
e0 = chunk:sub(-2, -2) 
end 
chunk = chunk:sub(1, #chunk - en) 
else -- eof 
en = 0 
end 
ngx.var.b64_en = en 
ngx.var.b64_e0 = e0 
ngx.var.b64_e1 = e1 
ngx.arg[1] = ngx.encode_base64(chunk)
30 lines Lua
★ngx_upreferer_module.c ~ 2100 C 
★src/modules/referer.lua ~ 500 Lua 
ngx.md5 ngx.time ngx.re.* ngx.req.* 
ngx.decode_args 
string.sub string.find string.byte 
table.concat
based on Lua coroutines & synchronous & 100% non-blocking 
cosocket API 
ngx.socket.* connect send receive 
sslhandshake close settimeout etc.
A true story: 
Yupoo Referer Redirect (Year 2012) 
eval_escalate off; 
eval_override_content_type text/plain; 
eval $answer { 
set $redis_key "$scheme://<key>"; 
redis_pass redis; 
} 
if ($answer = "101") { 
rewrite ^ http://r.yupoo.com/101.gif redirect; 
break; 
} 
if ($answer = "102") { 
rewrite ^ http://r.yupoo.com/102.gif redirect; 
break; 
} 
if ($answer ~ "^http://") { 
rewrite ^ $answer redirect; 
break; 
}
WTF! 
✦Fork ngx_http_redis to support 
ypacl command 
✦vkholodkov/nginx-eval-module 
last commit on Nov 26, 2010
when upgrade Nginx to the 
latest version 
Coredump
Yupoo Referer Redirect by Lua 
lua-resty-redis (based on the cosocket API) 
ngx.redirect rewrite_by_lua_file 
Local redis = require "resty.redis" 
local red = redis:new() 
redis.add_commands("ypacl") 
-- set_timeout and connect 
local res, err = red:ypacl(key) 
if res == "101" then 
return ngx.redirect("http://r.yupoo.com/101.gif") 
else 
-- do something else 
end
LuaJIT FFI
lua-resty-uuid: 
Based on LuaJIT FFI 
-- modified version of original pull request by smallfish 
-- https://github.com/openresty/lua-resty-string/pull/7 
local ffi = require "ffi" 
local new = ffi.new 
local string = ffi.string 
local _M = {} 
ffi.cdef[[ 
typedef unsigned char uuid_t[16]; 
void uuid_generate(uuid_t out); 
void uuid_unparse(const uuid_t uu, char *out); 
]] 
local libuuid = ffi.load("libuuid") 
function _M.generate() 
if libuuid then 
local uuid = new("uuid_t") 
local result = new("char[36]") 
libuuid.uuid_generate(uuid) 
libuuid.uuid_unparse(uuid, result) 
return string(result) 
end 
end 
return _M
Openresty 
Yichun "agentzh" Zhang (章亦春) agentzh@gmail.com, CloudFlare Inc.
ONEPIECE
UPYUN CDN
UPYUN CDN: 
Metadata Cache 
✦CJSON, MessagePack 
✦ngx.shared.DICT 
✦lua-resty-lock 
✦lua-resty-shcache 
✦lua-resty-lrucache (*)
Metadata Cache: 
The original version (Year 2012) 
Issues 
local metadata = ngx.shared.metadata 
-- local key, bucket = ... 
local value = metadata:get(key) 
if value ~= nil then 
if value == "404" then 
return -- HIT_NEGATIVE 
else 
return value -- HIT 
end 
end 
local rds = redis:new() 
local ok, err = rds:connect("127.0.0.1", 6379) 
if not ok then 
metadata:set(key, "404", 120) -- expires 2 minutes 
return -- NO_DATA 
end 
res, err = rds:hget("upyun:" .. bucket, ":something") 
if not res or res == ngx.null then 
metadata:set(key, "404", 120) 
return -- NO_DATA 
end 
metadata:set(key, res, 300) -- expires 5 minutes 
rds:set_keepalive() 
return res -- MISS 
✦ "dog-pile effect" 
✦ NO_DATA when redis crash 
✦ code inflexible
Metadata Cache: 
lua-resty-shcache 
✦ cache locks (based on lua-resty-lock) 
✦ serialization / de-serialization 
✦ external lookup via Lua closure 
✦ MISS, HIT, HIT_NEGATIVE, STALE, NET_ERR 
-- app/src/modules/metadata.lua 
local shcache = require "resty.shcache" 
function _M.get_metadata(bucket) 
local lookup_metadata = function () 
-- fetch from redis 
return res 
end 
local cache_data = shcache:new( 
ngx.shared.metadata, 
{ external_lookup = lookup_metadata, 
encode = cmsgpack.pack, 
decode = cmsgpack.unpack, 
}, 
{ positive_ttl = cache_positive_ttl, 
negative_ttl = cache_negative_ttl, 
name = "metadata", 
}) 
-- local key = ... 
local data, _ = cache_data:load(key) 
if not data then 
return 
end 
return data 
end
Metadata Cache: 
MessagePack 
Bucket JSON MessagePack 
huab*** 6035 bytes 4135 bytes - 69%
Metadata Cache: 
lua-resty-lrucache 
✦ based on LuaJIT FFI 
✦ HIT_LRU 
✦ avoid serialization / de-serialization
UPYUN CDN: 
Upstream Health Check 
✦lua-resty-checkups (*) 
✦lua-upstream-nginx-module
Upstream Health Check: 
lua-resty-checkups 
-- app/etc/config.lua 
_M.global = { 
checkup_timer_interval = 5, 
checkup_timer_overtime = 60, 
} 
_M.api = { 
timeout = 2, 
typ = "general", -- http, redis, mysql etc. 
cluster = { 
{ -- level 1 
try = 2, 
servers = { 
{ host = "127.0.0.1", port = 12354 }, 
{ host = "127.0.0.1", port = 12355 }, 
{ host = "127.0.0.1", port = 12356 }, 
} 
}, 
{ -- level 2 
servers = { 
{ host = "127.0.0.1", port = 12360 }, 
{ host = "127.0.0.1", port = 12361 }, 
} 
}, 
}, 
}
Upstream Health Check: 
checkups with nginx.conf 
-- app/etc/config.lua 
_M.global = { 
checkup_timer_interval = 5, 
checkup_timer_overtime = 60, 
ups_status_sync_enable = true, 
ups_status_timer_interval = 2, 
} 
_M.api = { 
cluster = { 
{ -- level 1 
try = 2, 
upstream = "api.com", 
}, 
{ -- level 2 
upstream = "api.com", 
upstream_only_backup = true, 
}, 
}, 
} 
# nginx/conf/upstream.conf 
upstream api.com { 
server 127.0.0.1:12354; 
server 127.0.0.1:12355; 
server 127.0.0.1:12356; 
server 127.0.0.1:12360 backup; 
server 127.0.0.1:12361 backup; 
} 
type status timer interval 
checkup shm_zone global 5s 
upstream 
per 
worker 
per 
worker 
2s
nginx.conf service 
server_name *.b0.upaiyun.com Custom Domain Binding 
valid_referers, rewrite, allow, deny Custom Antileech Rules and Redirect: 
ip, user-agent, referer, token etc. 
expires 7d Custom Expires Time: 
support specific URI rules etc. 
ssl_certificate* Custom SSL Certificates Load 
upstream { server 127.0.0.1 } 
Custom CDN Source: 
support multi-network routing etc. 
max_fails=3 fail_timeout=30s 
health_check (*) 
Custom Health Check Strategy: 
passive, active 
round-robin, ip_hash, hash (1.7.2+) Custom Load Balancing Strategy 
… …
nginx.conf as a service 
powered by ngx_lua
UPYUN DevOps 
conf hash + project version + upyun.cfg 
Ansible Playbook 
✦ rsync code and binary 
✦ conf template instantiate 
✦ kill -HUP `cat /var/run/nginx.pid` (*)
Lua CDN 
Lua WAF 
Lua SSL
Join our team 
©2012-2014 Trybiane
A Systems Engineer at UPYUN 
★Email: timebug.info@gmail.com 
★Github: https://github.com/timebug
Nginx ngx_lua agentzh Lua 
blog.cloudflare.com Openresty LuaJIT Github 
Maxim Dounin Igor Sysoev chaoslawful 
https://groups.google.com/forum/#!forum/OpenResty 
Ansible Michael Pall Open source 
Thanks 
…
Q & A

More Related Content

PDF
Roll Your Own API Management Platform with nginx and Lua
PDF
Lua tech talk
PDF
Devinsampa nginx-scripting
PDF
Using ngx_lua in UPYUN 2
PDF
PDF
RestMQ - HTTP/Redis based Message Queue
PDF
Bootstrapping multidc observability stack
PDF
Using ngx_lua in upyun 2
Roll Your Own API Management Platform with nginx and Lua
Lua tech talk
Devinsampa nginx-scripting
Using ngx_lua in UPYUN 2
RestMQ - HTTP/Redis based Message Queue
Bootstrapping multidc observability stack
Using ngx_lua in upyun 2

What's hot (20)

ODP
Integrating icinga2 and the HashiCorp suite
PDF
Redis & ZeroMQ: How to scale your application
PDF
Redis as a message queue
PDF
Static Typing in Vault
PDF
Securing Prometheus exporters using HashiCorp Vault
PDF
Observability with Consul Connect
PDF
Puppet and the HashiStack
PDF
Testing your infrastructure with litmus
PDF
Using Node.js to Build Great Streaming Services - HTML5 Dev Conf
PDF
Node.js streaming csv downloads proxy
PDF
Bootstrapping multidc observability stack
PDF
Top Node.js Metrics to Watch
PDF
Nodejs - A quick tour (v6)
PDF
Новый InterSystems: open-source, митапы, хакатоны
PDF
glance replicator
PDF
Application Logging in the 21st century - 2014.key
PDF
Autoscaling with hashi_corp_nomad
PDF
Facebook的缓存系统
PDF
Failsafe Mechanism for Yahoo Homepage
PPT
On UnQLite
Integrating icinga2 and the HashiCorp suite
Redis & ZeroMQ: How to scale your application
Redis as a message queue
Static Typing in Vault
Securing Prometheus exporters using HashiCorp Vault
Observability with Consul Connect
Puppet and the HashiStack
Testing your infrastructure with litmus
Using Node.js to Build Great Streaming Services - HTML5 Dev Conf
Node.js streaming csv downloads proxy
Bootstrapping multidc observability stack
Top Node.js Metrics to Watch
Nodejs - A quick tour (v6)
Новый InterSystems: open-source, митапы, хакатоны
glance replicator
Application Logging in the 21st century - 2014.key
Autoscaling with hashi_corp_nomad
Facebook的缓存系统
Failsafe Mechanism for Yahoo Homepage
On UnQLite
Ad

Viewers also liked (19)

PPTX
lua & ngx_lua 的介绍与应用
PDF
Infinit demo day pitch at Techstars NYC'14
PDF
PDF
基于OpenResty的百万级长连接推送
PPTX
We transfer
PDF
Httpool_WeTransfer_2016_ANG
PDF
Nginx Scripting - Extending Nginx Functionalities with Lua
PDF
Practical ngx_mruby
PPTX
Lua: the world's most infuriating language
PDF
Как и зачем создавать NginX-модуль - теория, практика, профит / Василий Сошни...
PDF
Using ngx_lua / lua-nginx-module in pixiv
PDF
MySQL Replication: What’s New in MySQL 5.7 and Beyond
PDF
Beautiful Monitoring With Grafana and InfluxDB
PDF
텐서플로 걸음마 (TensorFlow Tutorial)
PPTX
Introduction to Zabbix - Company, Product, Services and Use Cases
PDF
Introduction to Machine Learning and Deep Learning
PDF
The Barcelona Tech Startup Guide
PDF
The Amsterdam Tech Startup Guide
PPTX
OpenResty/Lua Practical Experience
lua & ngx_lua 的介绍与应用
Infinit demo day pitch at Techstars NYC'14
基于OpenResty的百万级长连接推送
We transfer
Httpool_WeTransfer_2016_ANG
Nginx Scripting - Extending Nginx Functionalities with Lua
Practical ngx_mruby
Lua: the world's most infuriating language
Как и зачем создавать NginX-модуль - теория, практика, профит / Василий Сошни...
Using ngx_lua / lua-nginx-module in pixiv
MySQL Replication: What’s New in MySQL 5.7 and Beyond
Beautiful Monitoring With Grafana and InfluxDB
텐서플로 걸음마 (TensorFlow Tutorial)
Introduction to Zabbix - Company, Product, Services and Use Cases
Introduction to Machine Learning and Deep Learning
The Barcelona Tech Startup Guide
The Amsterdam Tech Startup Guide
OpenResty/Lua Practical Experience
Ad

Similar to Using ngx_lua in UPYUN (20)

PDF
JDD 2017: Nginx + Lua = OpenResty (Marcin Stożek)
PDF
Socket programming, and openresty
PPTX
Implementing a Fileserver with Nginx and Lua
PPTX
Session: A Reference Architecture for Running Modern APIs with NGINX Unit and...
PDF
Developing a user-friendly OpenResty application
ODP
Introduction to Lua Luajit Openresty Luvit
PPTX
Inside Sqale's Backend at RubyConf Taiwan 2012
KEY
Nginx - Tips and Tricks.
PPTX
Inside Sqale's Backend at Sapporo Ruby Kaigi 2012
PDF
NginX - good practices, tips and advanced techniques
PPTX
Inside Sqale's Backend at YAPC::Asia Tokyo 2012
PDF
A Tale of a Server Architecture (Frozen Rails 2012)
PDF
ITB2019 NGINX Overview and Technical Aspects - Kevin Jones
PPTX
NGINX 101 - now with more Docker
PPTX
NGINX 101 - now with more Docker
PDF
Alternative Infrastucture
PDF
NGINX.conf 2016 - Fail in order to succeed ! Designing Microservices for fail...
PDF
Next Generation DevOps in Drupal: DrupalCamp London 2014
PDF
Using NGINX as an Effective and Highly Available Content Cache
PPTX
NGINX: Basics & Best Practices - EMEA Broadcast
JDD 2017: Nginx + Lua = OpenResty (Marcin Stożek)
Socket programming, and openresty
Implementing a Fileserver with Nginx and Lua
Session: A Reference Architecture for Running Modern APIs with NGINX Unit and...
Developing a user-friendly OpenResty application
Introduction to Lua Luajit Openresty Luvit
Inside Sqale's Backend at RubyConf Taiwan 2012
Nginx - Tips and Tricks.
Inside Sqale's Backend at Sapporo Ruby Kaigi 2012
NginX - good practices, tips and advanced techniques
Inside Sqale's Backend at YAPC::Asia Tokyo 2012
A Tale of a Server Architecture (Frozen Rails 2012)
ITB2019 NGINX Overview and Technical Aspects - Kevin Jones
NGINX 101 - now with more Docker
NGINX 101 - now with more Docker
Alternative Infrastucture
NGINX.conf 2016 - Fail in order to succeed ! Designing Microservices for fail...
Next Generation DevOps in Drupal: DrupalCamp London 2014
Using NGINX as an Effective and Highly Available Content Cache
NGINX: Basics & Best Practices - EMEA Broadcast

Recently uploaded (20)

PDF
MIND Revenue Release Quarter 2 2025 Press Release
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PPTX
sap open course for s4hana steps from ECC to s4
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
KodekX | Application Modernization Development
PDF
Spectral efficient network and resource selection model in 5G networks
PPTX
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
PPTX
20250228 LYD VKU AI Blended-Learning.pptx
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PDF
Encapsulation theory and applications.pdf
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PPTX
Programs and apps: productivity, graphics, security and other tools
PDF
Encapsulation_ Review paper, used for researhc scholars
PPTX
Cloud computing and distributed systems.
MIND Revenue Release Quarter 2 2025 Press Release
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
sap open course for s4hana steps from ECC to s4
“AI and Expert System Decision Support & Business Intelligence Systems”
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
Review of recent advances in non-invasive hemoglobin estimation
KodekX | Application Modernization Development
Spectral efficient network and resource selection model in 5G networks
ACSFv1EN-58255 AWS Academy Cloud Security Foundations.pptx
20250228 LYD VKU AI Blended-Learning.pptx
Understanding_Digital_Forensics_Presentation.pptx
Advanced methodologies resolving dimensionality complications for autism neur...
Encapsulation theory and applications.pdf
Diabetes mellitus diagnosis method based random forest with bat algorithm
Network Security Unit 5.pdf for BCA BBA.
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Programs and apps: productivity, graphics, security and other tools
Encapsulation_ Review paper, used for researhc scholars
Cloud computing and distributed systems.

Using ngx_lua in UPYUN

  • 1. Using ngx_lua in UPYUN Monkey Zhang (timebug) 2014.11 @ Beijing OSC
  • 2. Hello Nginx nginx [engine x] is an HTTP and reverse proxy server, as well as a mail proxy server, written by Igor Sysoev.
  • 3. A simple example $ ./configure --prefix=/opt/nginx --add-module=/path/to/echo-nginx-module http { server { listen 8080; location /hello { set $foo "hello"; echo $foo; set $foo "world"; echo $foo; } } } $ curl http://localhost:8080/hello world world
  • 4. In Fact … http { server { listen 8080; location /hello { set $foo "hello"; set $foo "world"; echo $foo; echo $foo; } } } ✦ REWRITE PHASE: set (ngx_http_rewrite_module) ✦ CONTENT PHASE: echo (echo-nginx-module)
  • 5. Nginx Internals: HTTP request phase handlers ✦ POST READ PHASE ✦ SERVER REWRITE PHASE ✦ FIND CONFIG PHASE ✦ REWRITE PHASE ✦ POST REWRITE PHASE ✦ PRE ACCESS PHASE ✦ ACCESS PHASE ✦ POST ACCESS PHASE ✦ TRY FILES PHASE ✦ CONTENT PHASE ✦ LOG PHASE
  • 6. Why Nginx Module Development is Not Easy ?
  • 7. A true story $ ./configure --prefix=/opt/nginx --add-module=/path/to/echo-nginx-module --add-module=/path/to/base64-nginx-module http { server { listen 8080; location /base64 { base64 on; base64_max_length 10485760; echo "hello world"; } } } $ curl http://localhost:8080/base64 aGVsbG8gd29ybGQK
  • 9. If is Evil http://agentzh.blogspot.jp/2011/03/how-nginx-location-if-works.html http { server { listen 8080; location /if { set $foo 1; if ($foo = 1) { set $foo 2; echo "foo = $foo"; } set $foo 3; proxy_pass http://127.0.0.1:$server_port/$foo; } location ~ /(d+) { echo "bar = $1"; } } } $ curl http://localhost:8080/if foo = 3
  • 10. If is Evil: How it works ✦ REWRITE PHASE set $foo 1; if ($foo = 1) { set $foo 2; } set $foo 3; ✦ CONTENT PHASE if ($foo = 1) { echo "foo = $foo"; }
  • 11. If is Evil: Break ngx_rewite Directives ✦ REWRITE PHASE set $foo 1; if ($foo = 1) { set $foo 2; break; } ✦ CONTENT PHASE if ($foo = 1) { echo "foo = $foo"; } http { server { listen 8080; location /if { set $foo 1; if ($foo = 1) { set $foo 2; break; echo "foo = $foo"; } set $foo 3; proxy_pass http://127.0.0.1:$server_port/$foo; } location ~ /(d+) { echo "bar = $1"; } } } $ curl http://localhost:8080/if foo = 2
  • 12. Hello Lua Lua is a powerful, fast, lightweight, embeddable scripting language.
  • 13. $ ./configure --prefix=/opt/nginx --add-module=/path/to/lua-nginx-module http { server { listen 8080; location /add { set $res ''; rewrite_by_lua ' local a = tonumber(ngx.var.arg_a) or 0 local b = tonumber(ngx.var.arg_b) or 0 ngx.var.res = a + b '; content_by_lua ' ngx.say(ngx.var.res) '; } } } $ curl 'http://localhost:8080/add?a=6&b=7' 13
  • 14. LuaJIT LuaJIT is a Just-In-Time Compiler (JIT) for the Lua programming language.
  • 15. How it works LuaJIT VM embedded into the Nginx
  • 16. Base64 Filter by Lua http { lua_package_path “$prefix/app/src/?.lua;;"; server { listen 8080; location /base64 { set $b64_en ''; set $b64_e0 ''; set $b64_e1 ''; echo_duplicate 1000 hello; header_filter_by_lua ' ngx.header.content_length = nil -- ((n + 2) / 3 ) * 4 ngx.header.content_type = "text/plain" ngx.header.content_transfer_encoding = "base64" '; body_filter_by_lua_file app/src/b64_body_filter.lua; } } }
  • 17. Base64 Filter by Lua: local chunk = ngx.arg[1] Chunk by Chunk local e0 = ngx.var.b64_e0 or '' local e1 = ngx.var.b64_e1 or '' local en = tonumber(ngx.var.b64_en) or 0 if en == 1 then chunk = e0 .. chunk elseif en == 2 then chunk = e0 .. e1 .. chunk end if not ngx.arg[2] then en = #chunk % 3 if en == 1 then e0 = chunk:sub(-1) elseif en == 2 then e1 = chunk:sub(-1) e0 = chunk:sub(-2, -2) end chunk = chunk:sub(1, #chunk - en) else -- eof en = 0 end ngx.var.b64_en = en ngx.var.b64_e0 = e0 ngx.var.b64_e1 = e1 ngx.arg[1] = ngx.encode_base64(chunk)
  • 19. ★ngx_upreferer_module.c ~ 2100 C ★src/modules/referer.lua ~ 500 Lua ngx.md5 ngx.time ngx.re.* ngx.req.* ngx.decode_args string.sub string.find string.byte table.concat
  • 20. based on Lua coroutines & synchronous & 100% non-blocking cosocket API ngx.socket.* connect send receive sslhandshake close settimeout etc.
  • 21. A true story: Yupoo Referer Redirect (Year 2012) eval_escalate off; eval_override_content_type text/plain; eval $answer { set $redis_key "$scheme://<key>"; redis_pass redis; } if ($answer = "101") { rewrite ^ http://r.yupoo.com/101.gif redirect; break; } if ($answer = "102") { rewrite ^ http://r.yupoo.com/102.gif redirect; break; } if ($answer ~ "^http://") { rewrite ^ $answer redirect; break; }
  • 22. WTF! ✦Fork ngx_http_redis to support ypacl command ✦vkholodkov/nginx-eval-module last commit on Nov 26, 2010
  • 23. when upgrade Nginx to the latest version Coredump
  • 24. Yupoo Referer Redirect by Lua lua-resty-redis (based on the cosocket API) ngx.redirect rewrite_by_lua_file Local redis = require "resty.redis" local red = redis:new() redis.add_commands("ypacl") -- set_timeout and connect local res, err = red:ypacl(key) if res == "101" then return ngx.redirect("http://r.yupoo.com/101.gif") else -- do something else end
  • 26. lua-resty-uuid: Based on LuaJIT FFI -- modified version of original pull request by smallfish -- https://github.com/openresty/lua-resty-string/pull/7 local ffi = require "ffi" local new = ffi.new local string = ffi.string local _M = {} ffi.cdef[[ typedef unsigned char uuid_t[16]; void uuid_generate(uuid_t out); void uuid_unparse(const uuid_t uu, char *out); ]] local libuuid = ffi.load("libuuid") function _M.generate() if libuuid then local uuid = new("uuid_t") local result = new("char[36]") libuuid.uuid_generate(uuid) libuuid.uuid_unparse(uuid, result) return string(result) end end return _M
  • 27. Openresty Yichun "agentzh" Zhang (章亦春) [email protected], CloudFlare Inc.
  • 30. UPYUN CDN: Metadata Cache ✦CJSON, MessagePack ✦ngx.shared.DICT ✦lua-resty-lock ✦lua-resty-shcache ✦lua-resty-lrucache (*)
  • 31. Metadata Cache: The original version (Year 2012) Issues local metadata = ngx.shared.metadata -- local key, bucket = ... local value = metadata:get(key) if value ~= nil then if value == "404" then return -- HIT_NEGATIVE else return value -- HIT end end local rds = redis:new() local ok, err = rds:connect("127.0.0.1", 6379) if not ok then metadata:set(key, "404", 120) -- expires 2 minutes return -- NO_DATA end res, err = rds:hget("upyun:" .. bucket, ":something") if not res or res == ngx.null then metadata:set(key, "404", 120) return -- NO_DATA end metadata:set(key, res, 300) -- expires 5 minutes rds:set_keepalive() return res -- MISS ✦ "dog-pile effect" ✦ NO_DATA when redis crash ✦ code inflexible
  • 32. Metadata Cache: lua-resty-shcache ✦ cache locks (based on lua-resty-lock) ✦ serialization / de-serialization ✦ external lookup via Lua closure ✦ MISS, HIT, HIT_NEGATIVE, STALE, NET_ERR -- app/src/modules/metadata.lua local shcache = require "resty.shcache" function _M.get_metadata(bucket) local lookup_metadata = function () -- fetch from redis return res end local cache_data = shcache:new( ngx.shared.metadata, { external_lookup = lookup_metadata, encode = cmsgpack.pack, decode = cmsgpack.unpack, }, { positive_ttl = cache_positive_ttl, negative_ttl = cache_negative_ttl, name = "metadata", }) -- local key = ... local data, _ = cache_data:load(key) if not data then return end return data end
  • 33. Metadata Cache: MessagePack Bucket JSON MessagePack huab*** 6035 bytes 4135 bytes - 69%
  • 34. Metadata Cache: lua-resty-lrucache ✦ based on LuaJIT FFI ✦ HIT_LRU ✦ avoid serialization / de-serialization
  • 35. UPYUN CDN: Upstream Health Check ✦lua-resty-checkups (*) ✦lua-upstream-nginx-module
  • 36. Upstream Health Check: lua-resty-checkups -- app/etc/config.lua _M.global = { checkup_timer_interval = 5, checkup_timer_overtime = 60, } _M.api = { timeout = 2, typ = "general", -- http, redis, mysql etc. cluster = { { -- level 1 try = 2, servers = { { host = "127.0.0.1", port = 12354 }, { host = "127.0.0.1", port = 12355 }, { host = "127.0.0.1", port = 12356 }, } }, { -- level 2 servers = { { host = "127.0.0.1", port = 12360 }, { host = "127.0.0.1", port = 12361 }, } }, }, }
  • 37. Upstream Health Check: checkups with nginx.conf -- app/etc/config.lua _M.global = { checkup_timer_interval = 5, checkup_timer_overtime = 60, ups_status_sync_enable = true, ups_status_timer_interval = 2, } _M.api = { cluster = { { -- level 1 try = 2, upstream = "api.com", }, { -- level 2 upstream = "api.com", upstream_only_backup = true, }, }, } # nginx/conf/upstream.conf upstream api.com { server 127.0.0.1:12354; server 127.0.0.1:12355; server 127.0.0.1:12356; server 127.0.0.1:12360 backup; server 127.0.0.1:12361 backup; } type status timer interval checkup shm_zone global 5s upstream per worker per worker 2s
  • 38. nginx.conf service server_name *.b0.upaiyun.com Custom Domain Binding valid_referers, rewrite, allow, deny Custom Antileech Rules and Redirect: ip, user-agent, referer, token etc. expires 7d Custom Expires Time: support specific URI rules etc. ssl_certificate* Custom SSL Certificates Load upstream { server 127.0.0.1 } Custom CDN Source: support multi-network routing etc. max_fails=3 fail_timeout=30s health_check (*) Custom Health Check Strategy: passive, active round-robin, ip_hash, hash (1.7.2+) Custom Load Balancing Strategy … …
  • 39. nginx.conf as a service powered by ngx_lua
  • 40. UPYUN DevOps conf hash + project version + upyun.cfg Ansible Playbook ✦ rsync code and binary ✦ conf template instantiate ✦ kill -HUP `cat /var/run/nginx.pid` (*)
  • 41. Lua CDN Lua WAF Lua SSL
  • 42. Join our team ©2012-2014 Trybiane
  • 43. A Systems Engineer at UPYUN ★Email: [email protected] ★Github: https://github.com/timebug
  • 44. Nginx ngx_lua agentzh Lua blog.cloudflare.com Openresty LuaJIT Github Maxim Dounin Igor Sysoev chaoslawful https://groups.google.com/forum/#!forum/OpenResty Ansible Michael Pall Open source Thanks …
  • 45. Q & A