pdf
|<<
<
>
>>|
/
ngx_openresty: an Nginx ecosystem glued by Lua ---- ngx_openresty: an {{#x|Nginx}} ecosystem glued by {{#x|Lua}} 由 Lua 粘合的 Nginx 生态环境 ☺{{#author|agentzh@gmail.com}}☺ {{#author|章亦春 (agentzh)}} {{#date|2012.02}} ---- {{#x|♡}} I've been {{#ci|hacking}} in the {{#x|Fuzhou}} city in the last 7 months 过去 7 个月中我一直在福州写码。。。 ---- {{img src="images/hacking-in-fuzhou.jpg" width="440" height="330"}} ---- {{#x|♡}} The trend in {{#ci|AJAX-ization}} and {{#ci|service-ization}} makes everything speak the {{#x|HTTP protocol}} AJAX 化和 Service 化的趋势让所有东西开始讲 HTTP 协议 ---- {{#x|♡}} Nginx is {{#ci|fast}}, because of {{#x|I/O multiplexing}} Nginx 很快,因为 I/O 多路复用 ---- {{img src="images/anim.gif" width="800" height="800"}} ---- {{#x|♡}} ngx_openresty is a {{#ci|bundle}} for Nginx, lots of useful {{#x|Nginx modules}}, and lots of useful Lua libraries. ngx_openresty 是 Nginx,许多有用的 Nginx 模块, 以及有用的 Lua 库的软件集合。 ---- {{#x|♡}} Our {{#ci|homepage}}: {{http://openresty.org}} 我们的主页 ---- {{img src="images/openresty-org.jpg" width="908" height="616"}} ---- {{#x|♡}} The Nginx {{#ci|configure file}} notation is a small {{#x|language}} Nginx 的配置文件记法就是一种小语言 ---- {{#kw|location}} = '/hello' { {{#kw|set_unescape_uri}} {{#v|$person}} {{#v|$arg_person}}; {{#kw|set_if_empty}} {{#v|$person}} {{#x|'anonymous'}}; {{#kw|echo}} {{#x|"hello, }}{{#v|$person}}{{#x|!"}}; } ---- {{#v|$}} curl 'http://localhost/hello?{{#x|person=%E7%AB%A0%E4%BA%A6%E6%98%A5}}' hello, {{#x|章亦春}} ---- {{#v|$}} curl 'http://localhost/hello' hello, {{#x|anonymous}} ---- {{#x|♡}} Various Nginx modules are {{#ci|enriching}} its {{#x|vicabulary}} 众多 Nginx 模块正丰富着它的词汇表 ---- {{#x|♡}} ngx_memc ➥ an Nginx {{#x|upstream}} module for {{#ci|Memcached}} 针对 Memcached 服务器的 Nginx 上游模块 ---- {{#cm|# (not quite) REST interface to our memcached server}} {{#cm|# at 127.0.0.1:11211}} {{#kw|location}} = /memc { {{#kw|set}} {{#v|$memc_cmd}} {{#v|$arg_cmd}}; {{#kw|set}} {{#v|$memc_key}} {{#v|$arg_key}}; {{#kw|set}} {{#v|$memc_value}} {{#v|$arg_val}}; {{#kw|set}} {{#v|$memc_exptime}} {{#v|$arg_exptime}}; {{#kw|memc_pass}} {{#x|127.0.0.1:11211}}; } ---- {{#v|$}} curl 'http://localhost/memc?{{#x|cmd=flush_all}}'; {{#x|OK}} {{#v|$}} curl 'http://localhost/memc?{{#x|cmd=replace&key=foo&val=FOO}}'; {{#x|NOT_STORED}} ---- {{#x|♡}} ngx_drizzle ➥ an Nginx {{#x|upstream}} module for {{#ci|MySQL}} and {{#ci|Drizzle}} 针对 MySQL 和 Drizzle 数据库的 Nginx 上游模块 ---- {{#kw|upstream}} {{#x|my_mysql_backend}} { {{#kw|drizzle_server}} {{#x|127.0.0.1:3306}} dbname={{#x|test}} password={{#x|some_pass}} user={{#x|monty}} protocol={{#x|mysql}}; {{#cm|# a connection pool that can cache up to}} {{#cm|# 200 mysql TCP connections}} {{#kw|drizzle_keepalive}} max={{#x|200}} overflow={{#x|reject}}; } ---- {{#kw|location}} ~ {{#x|'^/cat/(.*)'}} { {{#kw|set}} {{#v|$name}} {{#v|$1}}; {{#kw|set_quote_sql_str}} {{#v|$quoted_name}} {{#v|$name}}; {{#kw|drizzle_query}} {{#x|"select *}} {{#x|from cats}} {{#x|where name=}}{{#v|$quoted_name}}{{#x|"}}; {{#kw|drizzle_pass}} my_mysql_backend; {{#kw|rds_json}} on; } ---- {{#v|$}} curl 'http://localhost/cat/Jerry' {{#x|[{"name":"Jerry","age":1}]}} ---- {{#x|♡}} ngx_postgres ➥ an Nginx {{#x|upstream}} module for {{#ci|PostgreSQL}} 针对 PostgreSQL 数据库的 Nginx 上游模块 ---- {{#kw|upstream}} {{#x|my_pg_backend}} { {{#kw|postgres_server}} {{#x|10.62.136.3:5432}} dbname={{#x|test}} user={{#x|someone}} password={{#x|123456}}; {{#kw|postgres_keepalive}} max=50 mode=single overflow=ignore; } ---- {{#kw|location}} ~ {{#x|'^/cat/(.*)'}} { {{#kw|set}} {{#v|$name}} {{#v|$1}}; {{#kw|set_quote_pgsql_str}} {{#v|$quoted_name}} {{#v|$name}}; {{#kw|postgres_query}} {{#x|"select *}} {{#x|from cats}} {{#x|where name=}}{{#v|$quoted_name}}{{#x|"}}; {{#kw|postgres_pass}} my_pg_backend; {{#kw|rds_json}} on; } ---- {{#v|$}} curl 'http://localhost/cat/Jerry' {{#x|[{"name":"Jerry","age":1}]}} ---- {{#x|♡}} ngx_redis2 ➥ an Nginx {{#x|upstream}} module for {{#ci|Redis}} 针对 Redis 服务器的 Nginx 上游模块 ---- {{#kw|upstream}} {{#x|my_redis_node}} { {{#kw|server}} 127.0.0.1:6379; {{#kw|keepalive}} 1024 single; } ---- {{#cm|# multiple pipelined queries}} {{#kw|location}} /foo { {{#kw|set}} {{#v|$value}} 'first'; {{#kw|redis2_query}} set one {{#v|$value}}; {{#kw|redis2_query}} get one; {{#kw|redis2_pass}} my_redis_node; } ---- {{#x|♡}} ngx_srcache ➥ General location response {{#ci|cache}} based on Nginx {{#x|subrequests}} 基于 Nginx 子请求的通用 location 响应缓存 ---- {{#kw|location}} /api { {{#kw|set}} {{#v|$key}} \"{{#v|$uri}}?{{#v|$args}}\"; {{#kw|srcache_fetch}} GET /memc key={{#v|$key}}; {{#kw|srcache_store}} PUT /memc key={{#v|$key}}&exptime=3600; {{#cm|# proxy_pass/drizzle_pass/postgres_pass/etc}} } ---- {{#kw|location}} /memc { {{#kw|internal}}; {{#kw|set_unescape_uri}} {{#v|$memc_key}} {{#v|$arg_key}}; {{#kw|set}} {{#v|$memc_exptime}} {{#v|$arg_exptime}}; {{#kw|set_hashed_upstream}} {{#v|$backend}} {{#x|my_memc_cluster}} {{#v|$memc_key}}; {{#kw|memc_pass}} {{#v|$backend}}; } ---- {{#kw|upstream}} {{#x|memc1}} { {{#kw|server}} 10.32.126.3:11211; } {{#kw|upstream}} {{#x|memc2}} { {{#kw|server}} 10.32.126.4:11211; } {{#kw|upstream_list}} {{#x|my_memc_cluster memc1 memc2}}; ---- {{#x|♡}} ngx_iconv ➥ {{#ci|Character set}} converter based on {{#x|libiconv}} 基于 libiconv 的字符编码转换器 ---- {{#kw|location}} /api { {{#cm|# drizzle_pass/postgres_pass/etc}} {{#kw|iconv_filter}} from={{#x|UTF-8}} to={{#x|GBK}}; } ---- {{#x|♡}} Add some {{#ci|sugar}} of {{#x|Lua}} 添加一点儿 Lua 糖果... ---- {{#cm|# nginx.conf}} {{#kw|location}} = /hello { {{#kw|content_by_lua}} {{#x|'}} {{#x|ngx.say("Hello World")}} {{#x|'}}; } ---- {{#v|$}} curl 'http://localhost/hello' Hello World ---- {{#x|♡}} or use an {{#ci|external}} Lua file to keep things {{#c|clean}} 或者使用外部的 Lua 文件让代码保持整洁 ---- {{#cm|# nginx.conf}} {{#kw|location}} = /hello { {{#kw|content_by_lua_file}} {{#x|conf/hello.lua}}; } ---- {{#cm|-- hello.lua}} ngx.say(\"Hello World\") ---- {{#x|♡}} {{#i|Reuse}} existing {{#ci|Nginx modules}} in {{#ci|Lua}} by means of Nginx {{#x|subrequests}} 通过 Nginx 子请求实现在 Lua 中复用现有的 Nginx 模块 ---- {{#kw|location}} = /memc { {{#kw|internal}}; {{#kw|memc_pass}} ...; } {{#kw|location}} = /api { {{#kw|content_by_lua}} ' local resp = ngx.location.capture(\"/memc\") if resp.status ~= 200 then ngx.exit(500) end ngx.say(resp.body) '; } ---- {{#x|♡}} Multiple {{#ci|concurrent}} subrequests in Lua Lua 中发起多个并发子请求 ---- {{#kw|location}} = /api { {{#kw|content_by_lua}} ' local res1, res2, res3 = ngx.location.capture_multi{ {\"/memc\"}, {\"/mysql\"}, {\"/postgres\"} } ngx.say(res1.body, res2.body, res3.body) '; } ---- {{#x|♡}} {{#ci|Shared-memory}} dictionary API in Lua Lua 中的共享内存字典 API ---- {{#kw|lua_shared_dict}} dogs 10m; {{#kw|server}} { {{#kw|location}} = /set { {{#kw|content_by_lua}} ' local dogs = ngx.shared.dogs dogs:set(\"Tom\", ngx.var.arg_n) ngx.say(\"OK\") ' } {{#kw|location}} = /get { {{#kw|content_by_lua}} ' local dogs = ngx.shared.dogs ngx.say(\"Tom: \", dogs.get(\"Tom\")) '; } } ---- {{#v|$}} curl {{#x|'localhost/set?n=58'}} OK {{#v|$}} curl {{#x|'localhost/get'}} Tom: 58 ---- {{#x|♡}} {{#ci|Non-buffered}} response body output in Lua 在 Lua 中不带缓存的数据输出 ---- {{#cm|-- api.lua}} {{#cm|-- asynchronous emit data as a response body part}} ngx.say(\"big data chunk\") {{#cm|-- won't return until all the data flushed out}} ngx.flush(true) {{#cm|-- ditto}} ngx.say(\"another big data chunk\") ngx.flush(true) ---- {{#x|♡}} TCP {{#ci|socket}} API for accessing {{#x|upstream services}} in Lua 用于在 Lua 中访问上游服务的 TCP 套接字 API ---- {{#kw|local}} sock = ngx.socket.tcp() sock:settimeout(1000) {{#cm|-- one second}} {{#kw|local}} ok, err = sock:connect(\"127.0.0.1\", 11211) {{#kw|if}} {{#kw|not}} ok {{#kw|then}} ngx.say(\"failed to connect: \", err) {{#kw|return}} {{#kw|end}} ---- {{#kw|local}} bytes, err = sock:send(\"flush_all\r\n\") {{#kw|if not}} bytes {{#kw|then}} ngx.say(\"failed to send query: \", err) {{#kw|return}} {{#kw|end}} {{#kw|local}} line, err = sock:receive() {{#kw|if not}} line {{#kw|then}} ngx.say(\"failed to receive a line: \", err) {{#kw|return}} {{#kw|end}} ngx.say(\"result: \", line) ---- {{#kw|local}} ok, err = sock:setkeepalive(60000, 500) {{#kw|if not}} ok {{#kw|then}} ngx.say(\"failed to put the connection into pool \" .. \"with pool capacity 500 \" .. \"and maximal idle time 60 sec\") {{#kw|return}} {{#kw|end}} ---- {{#x|♡}} {{#ci|Unix Domain Socket}} is also supported Unix 域套接字也是支持的 ---- {{#kw|local}} sock = ngx.socket.tcp() {{#kw|local}} ok, err = sock:connect(\"/tmp/some.sock\") {{#kw|if not}} ok {{#kw|then}} ngx.say(\"failed to connect to /tmp/some.sock: \", err) {{#kw|return}} {{#kw|end}} ---- {{#x|♡}} The socket API is implemented atop {{#ci|Lua coroutines}} and is {{#x|synchronous}} and {{#x|non-blocking}} 这些套接字 API 都是在 Lua 协程的基础上实现的, 是同步和非阻塞的。 ---- {{#x|♡}} We {{#x|call}} this socket API {{#ci|"cosocket"}} 我们把这组套接字 API 称为“cosocket”. ---- {{#x|♡}} {{#ci|cosocket API}} can also be used to read huge {{#x|request body}} data cosocket API 还可以用于读取 巨大的请求体数据 ---- {{#kw|local}} sock, err = ngx.req.socket() {{#kw|if not}} sock {{#kw|then}} ngx.say(\"failed to get request socket: \", err) {{#kw|return}} {{#kw|end}} sock:settimeout(10000) {{#cm|-- 10 sec timeout}} ---- {{#kw|while true do}} {{#kw|local}} chunk, err = sock:receive(4096) {{#kw|if not}} chunk {{#kw|then}} {{#kw|if}} err == \"closed\" {{#kw|then}} {{#kw|break}} {{#kw|end}} ngx.say(\"faile to read: \", err) {{#kw|return}} {{#kw|end}} process_chunk(chunk) {{#kw|end}} ---- {{#x|♡}} High-level {{#x|Lua libraries}} based on the {{#ci|cosocket API}} 基于 cosocket API 构建的高层次的 Lua 库 ---- {{#x|☺}} lua-resty-mysql: pure Lua MySQL driver based on cosocket {{https://github.com/agentzh/lua-resty-mysql}} lua-resty-mysql: 基于 cosocket 的纯 Lua 实现 的 MySQL 驱动 ---- {{#x|☺}} lua-resty-memcached: pure Lua Memcached driver {{https://github.com/agentzh/lua-resty-memcached}} lua-resty-memcached: 基于 cosocket 的纯 Lua 实现 的 Memcached 驱动 ---- {{#x|☺}} lua-resty-redis: pure Lua Redis driver {{https://github.com/agentzh/lua-resty-redis}} lua-resty-redis: 基于 cosocket 的纯 Lua 实现 的 Redis 驱动 ---- {{#x|☺}} lua-resty-upload: support for {{#ci|big}} file {{#x|uploading}} (multipart/form-data) {{https://github.com/agentzh/lua-resty-upload}} lua-resty-upload: 大文件上传支持 ---- {{#x|♡}} I've been hacking on {{#ci|GitHub}}! {{http://github.com/agentzh}} 我在 GitHub 上玩开源! ---- {{#x|♡}} {{#ci|Follow}} me on {{#x|Sina Weibo}}! {{http://weibo.com/agentzh/}} 在新浪微博上关注我! ---- ☺ {{#ci|Any questions}}? ☺