乱入 OpenResty 手册!¶
好吧,这是有关如何在 42分钟之内 超高速上手 OpenResty 的一个不怎么靠谱的手册;-)
概述
- 私人体验,分享感触, 完全 Zoom.Quiet 个人编撰...
工程
TODO
- 根据读者的怒吼进行温和的修订
- 根据个人项目的深入增补
章节索引¶
0: 为毛要入?!¶
先得声明,要想真正在 42分钟 内进入 `OpenResty`_ 玩起来,不是没有前提的,,,
至少:
- 42岁 ?! 那还能有好奇心,绝对是人类精品,肯定冇问题了!
- 42个月, Linux/Unix 系统使用~注意!是使用不是管理体验;不然各种标准的命令行操作要死人的,,
- 42周的, 编程体验,不许什么語言,就是 JS 也成,起码要有一丝码感,,,
- 42次的, 蕃茄工作时间 的经历,至少知道什么是” 心流 “般的专注!
- 42天的, 相关资料通读,相关社区的加入/旁听,,,一定要摸清楚相关主要开发人员的沟通习惯 ;-)
- 42小时, 的周边环境整备,熟悉工作系统以及配置好顺手的工具(git/apt/yum/brew ...)
然后,还是想继续这遭没谱乱入的话 ;-)
警告
- 走起!
预备¶
一切开始前,先必须 相信 !
相信¶
2012: 由 Lua 粘合的 Nginx 生态环境
2011: Applications of ngx_openresty and perl at lz.taobao.com
2010:
...
仅仅是过一下眼,还没有领会到 OpenResty 妙处的,基本很难相信了 - 不相信,那么任何乱入行为,都会被自个儿否决 - 那真心没招了,,,
说明, OpenResty 不是你的那份儿菜,甭忙了,继续 C++/JAVA/.NET 吧,,,
+22分钟:初尝¶
整起来先! - 嗯嗯嗯,现在可以计时了: 00:00
安装¶
参考: 官方安装说明
走起
$ brew install pcre
$ axel http://agentzh.org/misc/nginx/ngx_openresty-1.0.8.26.tar.gz
$ tar xzvf ngx_openresty-1.0.8.26.tar.gz
$ cd ngx_openresty-1.0.8.26
$ ./configure --with-cc-opt="-I/usr/local/Cellar/pcre/8.21/include" \
--with-ld-opt="-L/usr/local/Cellar/pcre/8.21/lib"
$ make
$ make install
$ $ /usr/local/openresty/nginx/sbin/nginx -v
nginx: nginx version: ngx_openresty/1.0.10.48
跑¶
玩 OpenResty 就是要反复调整内置nginx 的配置,为了方便起见
- 推荐
openresty.server
. - 是简化版的: Nginx-init-ubuntu
- 兼容 Linux/Unix/MAC 系统
$ /opt/sbin/openresty.server
usage: /opt/sbin/openresty.server {check|start|term|stop|reload|restart|upgrade}
建议部署在 /opt/sbin 目录 然后就可以随时:
- /opt/sbin/openresty.server check 检验配置脚本是否正确
- /opt/sbin/openresty.server start 启动 Nginx
- /opt/sbin/openresty.server reload 热加载新的配置文件
- 测试是否在跑? 使用 ps aux | grep nginx
玩¶
那么开始 OpenResty 的编程吧!
理解重要的目录结构:
/usr/local/openresty/
+- luajit Lua实时编译组件
+- lualib Lua预装原生库
+- nginx openresty 狠狠定制过的稳定版本 Nginx
+- log 默认的日志,pid 存放目录
+- conf 默认的配置文件目录
+- nginx.conf
那么,要作是只是:
修订 主配置文件,加入我的专用配置文件包含:
http { include my_openresty.conf; ...
创建 my_openresty.conf 并写入:
server { listen 9090; server_name localhost; error_log logs/error.my_openresty.log info; location / { content_by_lua "ngx.say('Hello,world!')"; } }
然后享受成果
$ /opt/sbin/openresty.server reload $ curl localhost:9090 Hello,world!
改¶
每次都要修改和各种 web 服务配置杂在一起的 nginx 配置,而且再重启 nginx 才能见到成果?!
当然有好招 - 引入独立 Lua 脚本,到指定 url 路由 - 增补 my_openresty.conf :
server {
listen 9090;
server_name localhost;
error_log logs/error.my_openresty.log info;
location / {
content_by_lua "ngx.say('Hello,world!')";
}
location /readme {
lua_code_cache off; # <<< 关键技巧!
content_by_lua_file conf/lua/readme.lua;
}
}
- 创建业务脚本: /usr/local/openresty/nginx/conf/lua/readme.lua
-- readme for my openresty app.
ngx.say("Hallo World!")
- 再重启 nginx, curl localhost:9090/readme 获得同样的 “Hallo World!”
- 要是看到:
nginx: [warn] lua_code_cache is off; this will hurt performance in /usr/local/openresty/nginx/conf/my_openresty.conf:23
- 别怕,正常的提醒,說没有 Lua 脚本的缓存,整个性能会下载,没关系,测试开发阶段,俺们不论运行性能,先关注开发效率!
- 然后,增补 readme.lua
-- readme for my openresty app.
VERTION="URIsAok4openresty v12.03.6"
ngx.say(VERTION
,"\n\tusage:"
,"$crul -d 'uri=http://sina.com' locahost:9090/=/chk"
)
- 不用重启, 再次 curl 就发现返回已经变化了!
$ curl localhost:9090/readme
URIsAok4openresty v12.03.6
usage:$crul -d 'uri=http://sina.com' 127.0.0.1:9090/=/chk
那么¶
要干点儿实事儿了!
仅仅以使用 OpenResty 包装 金山网址云安全开放API 为实例
- 那么,要解决的子问题有:
- 怎么获得 POST 来的数据?
- 怎么对提交的数据进行分组解析?
- 怎么对字串进行 base64 ?
- 怎么对字串进行 md5 ?
- 怎么对字串进行合并 ?
- 怎么在请求期间抓取外部网站数据 ?
- 怎么对请求方式进行判定?
- ...
1: 进入初阶¶
好吧,想 C/C++/PHP/node.js/Python ... 切换到 Lua ?!
- 其实,真心简单,毕竟, Lua 实在是非常简单的 脚本语言
- 其实,任何开发语言的乱入,完全可以不管什么设计思想,开发模式之类的高级东西
警告
- 走起!
整备¶
只要明确一些 Lua 的`基本形式` 80% 的实际编程就可以混进去了 !-) :
- 基本语法
-- 单行注释
--[[
多行
注释
]]
a="hollo"
b= a .. 1 -- 字串连接,连接数字会自动转换类型
b,a = a,b -- 巨爽直的变量交换
c=0
if 1 ~= c -- 不等于?
print "Yes"
else
print "No"
end
function d(e,f)
return e,f,e*f
end
A1,A2,result = d(2,3)
print(A1,A2,result)
基本数据
- 数字,字串,布尔 基本和其它脚本语言类同
- nil ~ 空值
- 特殊的是 function 也是基本数据类型!
- 关系表,嗯嗯嗯,就是Python 里的字典吼
T1 ={ 10, -- 相当于 [1] = 10
,[100] = 40,
,John={ -- 如果你原意,你还可以写成:["John"] =
Age=27 -- 如果你原意,你还可以写成:["Age"] =27
,Gender=Male -- 如果你原意,你还可以写成:["Gender"] =Male
}
,20 -- 相当于 [2] = 20
}
+15分钟:突入¶
google 真心好朋友,通过搜索可以获得很多已经用上 OpenResty 的先驱们的各种代码片段
只要快速使用 写lua->curl测试 流程,结合以往的开发经验,驗证猜想,突进就好!
- 嗯嗯嗯,现在可以计时了: 22:01
阵地¶
增补 my_openresty.conf :
server {
...
location ~ ^/=/(\w+) {
content_by_lua_file conf/lua/$1.lua;
lua_code_cache off;
}
...
}
解释一下
创建 chk.lua :
-- try openresty easy creat RESTful API srv.
ngx.req.read_body()
local method = ngx.var.request_method
ngx.say("request_method:\t",method)
if method ~= 'POST' then
ngx.say('pls. /=/chk only work as POST ;-)')
else
ngx.say('realy working,,,')
end
表忘记:
- $ /opt/sbin/openresty.server restart 重启 OpenResty 中的 Nginx
工作环境¶
根据 ngx.var.request_method 的状态值, 已经完成一个业务流程:
- 如果是普通的 GET 请求,直接吼回提示,不处置
- 如果是数据的 POST 请求,开始 真正的干活
注解
(~_~)
- 其实,使用 print 或是 ngx.log() 都是可以直接将调试值输出到错误日志里的
- 但是,会间杂在大量其它正当日志中,很不容易观察, 例如: 插图 1-1 日志中不同的调试方式输出 所示
- 就是在 readme.lua 中增补两种调试打印代码引发的
VERTION="URIsAok4openresty v12.03.6"
print("VERTION: ",VERTION)
ngx.log(ngx.INFO,"VERTION: ",VERTION)
- 使用 tail -f /path/2/error.log 持续观察日志时的情景

插图 1-1 日志中不同的调试方式输出
进一步的,其实,比较舒服的开发环境,应该是:
可以同时观察到代码,错误日志,以及测试请求过程的!
俺推荐使用 tmux 作为多窗格终端
具体情景如 插图 1-2 使用tumx 同时进行观察的情景 所示
- 上方是 tail -f /path/2/error.log 监察
- 下左是脚本编辑
- 下右是 curl 请求测试区

插图 1-2 使用tumx 同时进行观察的情景
搞掂¶
好的,什么都整顿舒服了,就可以快速一条条试错的方式,堆出所有业务逻辑了!
local method = ngx.var.request_method
--各种事先申請好的 金山网址云 接口授权信息
APPKEY = "k-60666"
SECRET = "99fc9fdbc6761f7d898ad25762407373"
ASKHOST = "http://open.pc120.com"
ASKTYPE = "/phish/?"
--内部辅助函式,组成 金山网址云 需要的查询請求链接
function checkForValidUrl(uri)
crtURI = ngx.encode_base64(uri)
timestamp = ngx.now()
ngx.say('timestamp:\t',timestamp)
signbase = ASKTYPE .. "appkey=" .. APPKEY .. "&q=" .. crtURI .. "×tamp=" .. timestamp
sign = ngx.md5(signbase .. SECRET)
return ASKHOST .. signbase .. "&sign=" .. sign
end
if method ~= 'POST' then
ngx.say('pls. only POST chk me;-)')
else
local data = ngx.req.get_body_data()
ngx.say("get_body_data:\t",data)
local args = ngx.req.get_post_args()
local uri = args.uri
ngx.say("args.uri:\t",uri)
local chkURI = checkForValidUrl(uri)
ngx.say(chkURI)
end
测试输出如:
$ curl -d "uri=http://sina.com" localhost:9090/=/chk
get_body_data: uri=http://sina.com
args.uri: http://sina.com
timestamp: 1331304634.315
http://open.pc120.com/phish/?appkey=k-60666&q=aHR0cDovL3NpbmEuY29t\
×tamp=1331304408.315&sign=d7e80af8f0f83438e315e320eba4efe6
HttpLuaModule 真心 碉堡 了! 所有最常见的操作都内置了!
- 字串的 base64 编码: ngx.encode_base64()
- 字串的 md5 编码: ngx.md5()
- 当前时间戮: ngx.now()
- POST 上来的数据体 ngx.req.get_body_data()
- 数据体的解析: ngx.req.get_post_args()
- 实际数据的引用: args.uri
但是,怎么 向外部url发出请求并接收数据?!
luarocks¶
摇滚吧! Lua....
和所有成熟的开发语言一样, Lua 也有自个儿的第3方扩展包管理平台:
真心有爱的方便工具!
- 安装?! 下载,解开,./configure,make,make install
- 使用? 先搜索
$ luarocks search url
Search results:
===============
Rockspecs and source rocks:
---------------------------
luacurl
1.2.1-1 (src) - http://luarocks.org/repositories/rocks
1.2.1-1 (rockspec) - http://luarocks.org/repositories/rocks
1.1-3 (src) - http://luarocks.org/repositories/rocks
1.1-3 (rockspec) - http://luarocks.org/repositories/rocks
1.1-2 (src) - http://luarocks.org/repositories/rocks
1.1-2 (rockspec) - http://luarocks.org/repositories/rocks
- 安装扩展
$ luarocks install luacurl
Installing http://luarocks.org/repositories/rocks/luacurl-1.2.1-1.src.rock...
Using http://luarocks.org/repositories/rocks/luacurl-1.2.1-1.src.rock... switching to 'build' mode
Archive: /tmp/luarocks_luarocks-rock-luacurl-1.2.1-1-8288/luacurl-1.2.1-1.src.rock
inflating: luacurl-1.2.1-1.rockspec
extracting: luacurl-1.2.1.zip
Archive: luacurl-1.2.1.zip
creating: luacurl-1.2.1/
inflating: luacurl-1.2.1/CMakeLists.txt
inflating: luacurl-1.2.1/luacurl.c
export MACOSX_DEPLOYMENT_TARGET=10.3; gcc -O2 -fPIC -I/usr/local/include -c luacurl.c -o luacurl.o -I/usr/include
export MACOSX_DEPLOYMENT_TARGET=10.3; gcc -bundle -undefined dynamic_lookup -all_load -o luacurl.so -L/usr/local/lib luacurl.o -L/usr/lib -lcurl
Updating manifest for /usr/local/lib/luarocks/rocks
luacurl 1.2.1-1 is now built and installed in /usr/local/ (license: MIT/X11)
- 使用?! 参考: 官方手册
- 以及: [Lua-cURL by msva](http://msva.github.io/lua-curl/)
抄个小函式就好:
curl = require "luacurl"
function _fetch_uri(url, c)
local result = { }
if c == nil then
c = curl.new()
end
c:setopt(curl.OPT_URL, url)
c:setopt(curl.OPT_WRITEDATA, result)
c:setopt(curl.OPT_WRITEFUNCTION, function(tab, buffer)
table.insert(tab, buffer)
return #buffer
end)
local ok = c:perform()
return ok, table.concat(result)
end
- 感谢 TooNTonG 的提醒
- 这里使用 Lua-cURL 的对外请求是阻塞的,这也是俺的期望...
- 参考: IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇) - 智障大师
增补到工作脚本中
--前略,,,
local chkURI = checkForValidUrl(url)
--ngx.say(chkURI)
ok, html = _fetch_uri(chkURI)
ngx.say("_fetch_uri:\t", ok, "\t", html)
测试,确认效果:
... http://open.pc120.com/phish/?appkey=k-60666&q=aHR0cDovL3NpbmEuY29t×tamp=1331306157.315&sign=4190815f3920d9bbd0d1410525343a0e _fetch_uri: true {"success":1,"phish":0}
基本功能,达成, 收功!
小结¶
37:00 ~ 这一堆,一刻鈡,整出来不难吧?
想来: - 其实,关键功能性行为代码,就8行
- 其中7 行全部可以在一篇文档中查到: HttpLuaModule
- 仅仅有一行,是需要学习新的工具,安装新的组件,学习新的文档,抄进来新的函式
- 即: ok, html = _fetch_uri(chkURI)
其余,都是力气活儿
- 只要别抄錯
- 都是赋值,赋值,赋值,赋值,,,,
只要注意每一步,都使用 ngx.say() 吼回来,测试确认无误,就可以继续前进了,,,
这就是脚本语言的直觉式开发调试体验!
11:21 p.m., Wednesday Jan. 15 吉米:¶
luarocks with luajit 的方式
./configure –with-lua=”/usr/local/openresty/luajit/bin” –prefix=”/usr/local” –lua-suffix=”jit-2.1.0-alpha” –with-lua-include=”/usr/local/openresty/luajit/include/luajit-2.1” –force-config
解释一下:
--lua-suffix="jit-2.1.0-alpha" :
/usr/local/openresty/luajit/bin/luajit-2.1.0-alpha
这是专家建议的编译出专用版本的技巧.
2: 怎么深入?¶
好了,已经完成了预期的 :
- 使用 OpenResty
- 包装 金山网址云安全开放API 为接口服务
- 只要 curl -d “uri=http://sina.com” localhost:9090/=/chk 即可返回,金山云的数据!
但是 :
- 代码很集中,业务逻辑离头部很远,很难看
- 当没有进行 POST 请求时,以及各种其它意外请求时,并没智能的捕获,并提示
- 变量名设计的都很挫,需要重构
...
所以,继续折腾...
调研¶
核心是先理解 Lua 的包管理机制:
OpenResty 邮件列表中的讨论过程,核心作者的 明确提示 :
agentzh agentzh@gmail.com
发件人当地时间: 发送时间 11:00 (GMT+08:00)。发送地当前时间:上午12:07。 ✆
回复: openresty@googlegroups.com
主题: Re: [openresty:99] 纯lua 问题: attempt to ... (a nil value)
...
一般地,由于全局变量是每请求的生命期,因此以此种方式定义的函数的生命期也是每请求的。
为了避免每请求创建和销毁 Lua closure 的开销,建议将函数的定义都放置在自己的 Lua module 中,
例如:
-- my_module.lua
module("my_module", package.seeall)
function foo() ... end
然后,再在 content_by_lua_file 指向的 .lua 文件中调用它:
local my_module = require "my_module"
my_module:foo()
因为 Lua module 只会在第一次请求时加载一次
(除非显式禁用了 lua_code_cache 配置指令),后续请求便可直接复用。
Regards,
-agentzh
警告
- 来吧!
+5分钟:重构¶
设计
/usr/local/openresty/nginx/conf
+- my_openresty.conf 业务路由配置
+- lua/ 业务脚本入口
+- readme.lua /readme 响应
+- chk.lua /=/chk 响应
+- ksc.lua 可复用的功能包
模块化¶
所以,兴冲冲,将代码分离出来:
-- KCS API support
module("ksc", package.seeall)
--curl as lua
curl = require "luacurl"
function _fetch_uri(url, c)
local result = { }
if c == nil then
c = curl.new()
end
c:setopt(curl.OPT_URL, url)
c:setopt(curl.OPT_WRITEDATA, result)
c:setopt(curl.OPT_WRITEFUNCTION, function(tab, buffer)
table.insert(tab, buffer)
return #buffer
end)
local ok = c:perform()
return ok, table.concat(result)
end
-- global var
--ngx.say(PHISHTYPE["2"])
APPKEY = "k-60666"
SECRET = "99fc9fdbc6761f7d898ad25762407373"
ASKHOST = "http://open.pc120.com"
ASKTYPE = "/phish/?"
function checkForValidUrl(uri)
crtURI = ngx.encode_base64(uri)
timestamp = ngx.now()
signbase = ASKTYPE .. "appkey=" .. APPKEY .. "&q=" .. crtURI .. "×tamp=" .. timestamp
sign = ngx.md5(signbase .. SECRET)
return ASKHOST .. signbase .. "&sign=" .. sign
end
精简原 chk.lua
ngx.req.read_body()
local method = ngx.var.request_method
local KSC = require "ksc"
if method ~= 'POST' then
ngx.say('pls. only POST chk me;-)')
else
local args = ngx.req.get_post_args()
local uri = args.uri
local chkURI = KSC.checkForValidUrl(uri)
ok, html = KSC._fetch_uri(chkURI)
ngx.say("_fetch_uri:\t", ok, "\t", html)
end
哗! 业务逻辑首屏得见!
撞!¶
但是! 立即...
*1 lua handler aborted: runtime error:
/usr/local/openresty/nginx/conf/lua/chk.lua:6: module 'ksc' not found:
no field package.preload['ksc']
no file '/usr/local/openresty/lualib/ksc.lua'
no file './ksc.lua'
no file '/usr/local/openresty/luajit/share/luajit-2.0.0-beta9/ksc.lua'
no file '/usr/local/share/lua/5.1/ksc.lua'
no file '/usr/local/share/lua/5.1/ksc/init.lua'
no file '/usr/local/openresty/luajit/share/lua/5.1/ksc.lua'
no file '/usr/local/openresty/luajit/share/lua/5.1/ksc/init.lua'
no file '/usr/local/openresty/lualib/ksc.so'
no file './ksc.so'
no file '/usr/local/lib/lua/5.1/ksc.so'
no file '/usr/local/openresty/luajit/lib/lua/5.1/ksc.so'
no file '/usr/local/lib/lua/5.1/loadall.so', client: 127.0.0.1, server: localhost, request: "POST /=/chk HTTP/1.1", host: "127.0.0.1:9090"
...
明显是没有找到 ksc.lua 包, 为毛?! - 一通乱 搜索 - 据某处的示例修订:
-- ksc.lua
module(..., package.seeall)
,,,
-- chk.lua
local KSC = require "lua.ksc"
- 并增补 my_openresty.conf
server {
lua_package_path '/usr/local/openresty/nginx/conf/lua/?.lua;;';
...
- /opt/sbin/openresty.server reload 热加载新的配置文件
就可以了!
12:57 a.m., Thursday Jan. 16 吉米 :¶
lua_package_path '/usr/local/openresty/nginx/conf/lua/?.lua;;';
放到 server 外面去
lua_package_path '/usr/local/openresty/nginx/conf/lua/?.lua;;';
server {
...
}
善! 这个可以有 ;-)
仔细...¶
可是,立即遭到了 agentzh 的劝戒,,, - 再尝试,才知道
- lua_package_path 不允许用在 server 或是 location 域中,只能在 http 中!
- lua_package_path 增补后, Nginx 仅仅 reload 可能不加载这一行为,最好 restart !
- 不相信! 尝试一下,才真正见到日志中的 [emerg]
Checking configuration for correct syntax and
then trying to open files referenced in configuration...
nginx: [emerg] "lua_package_path" directive
is not allowed here in
/usr/local/openresty/nginx/conf/my_openresty.conf:10
nginx: configuration file
/usr/local/openresty/nginx/conf/nginx.conf test failed
- 其实,这本身,在 HttpLuaModule 的维基手册示例第一行就 暗示 过!

插图 2-1 维基中手册文档的提示
警告
(~.~)
- 这得要多少 心灵相通 才意识的到吼?!
快速修订回来:
-- ksc.lua
module("ksc", package.seeall)
,,,
-- chk.lua
local KSC = require "ksc"
一切如愿鸟 !-)
12:57 a.m., Thursday Jan. 16 吉米 :¶
-- chk.lua
local KSC = require "lua.ksc"
不要修改..
这个得研究一下,不过,形式上的确不进行消减要直觉的多, 谢谢!
复用?¶
对于各种意外,其实,很想直接使用 /readme 中的输出就好的,
- 根据以往幻灯中的提示,使用 ngx.location.capture()
if method ~= 'POST' then
ngx.say('pls. only POST chk me;-)')
local readme = ngx.location.capture("/readme")
if readme.status == 200 then
ngx.say(readme.body)
end
效果
$ curl localhost:9090/=/chk
pls. only POST chk me;-)
URIsAok4openresty v12.03.6
usage:$crul -d 'uri=http://sina.com' 127.0.0.1:9090/=/chk
BINGO!
小结¶
42:01 ~ 这两个重构,纯粹是复制/粘贴,5分鈡,整出来不难吧?
但是,过程中的心理冲突,绝对不轻
比如,为毛 ngx.redirect() 不 重定向 ?!
- 俺是首先尝试使用 重定向 来合并输出的
- 结果,只是使用标准的 http 302 状态码返回而已
等等,都需要补课,老实查阅文档,认真领悟,大胆尝试,建立靠谱的思路和反应,,,
整个儿的¶
最终,完成所有功能的配置和代码:
- 版本仓库: https://github.com/ZoomQuiet/urisaok/tree/openresty
- 对外发布: http://py.kingsoft.net:8008/=/chk
- 相关视频: {3月8日语音讲座vol.45}ZQ: 网址云服务嵌入Nginx - Youku
nginx.conf¶
http {
...
# 设置纯Lua扩展库PATH(';;' is the default path):
lua_package_path '/usr/local/openresty/nginx/conf/lua/?.lua;;';
include my_openresty.conf;
...
my_openresty.conf¶
server {
listen 9090;
server_name localhost;
error_log logs/error.my_openresty.log info;
default_type 'text/plain';
location / {
content_by_lua_file conf/lua/readme.lua;
}
location /readme {
content_by_lua_file conf/lua/readme.lua;
}
location ~ ^/=/(\w+) {
content_by_lua_file conf/lua/$1.lua;
lua_code_cache off;
}
}
readme.lua¶
-- readme for /=/ export base help!
ngx.req.read_body()
VERTION="URIsAok4openresty v12.03.6"
ngx.say(VERTION
,"\n\tusage:"
,"$crul -d 'uri=http://sina.com' 127.0.0.1:9090/=/chk"
)
ksc.lua¶
-- KCS API support
module("ksc", package.seeall)
curl = require "luacurl"
function _fetch_uri(url, c)
local result = { }
if c == nil then
c = curl.new()
end
c:setopt(curl.OPT_URL, url)
c:setopt(curl.OPT_WRITEDATA, result)
c:setopt(curl.OPT_WRITEFUNCTION, function(tab, buffer)
table.insert(tab, buffer)
return #buffer
end)
local ok = c:perform()
return ok, table.concat(result)
end
-- global var
PHISHTYPE = {["-1"]='UNKNOW'
,["0"]='GOOD'
,["1"]='PHISH'
,["2"]='MAYBE PHISH'
}
--ngx.say(PHISHTYPE["2"])
APPKEY = "k-60666"
SECRET = "99fc9fdbc6761f7d898ad25762407373"
ASKHOST = "http://open.pc120.com"
ASKTYPE = "/phish/?"
function checkForValidUrl(uri)
ngx.say("uri:\t",uri)
crtURI = ngx.encode_base64(uri)
timestamp = ngx.now()
signbase = ASKTYPE .. "appkey=" .. APPKEY .. "&q=" .. crtURI .. "×tamp=" .. timestamp
sign = ngx.md5(signbase .. SECRET)
return ASKHOST .. signbase .. "&sign=" .. sign
end
chk.lua¶
-- try openresty easy creat RESTful API srv.
ngx.req.read_body()
local method = ngx.var.request_method
local KSC = require "ksc"
if method ~= 'POST' then
ngx.say('pls. only POST chk me;-)')
local readme = ngx.location.capture("/readme")
if readme.status == 200 then
ngx.say(readme.body)
end
else
local data = ngx.req.get_body_data()
local args = ngx.req.get_post_args()
local uri = args.uri
local url = uri --fields[3]
local chkURI = KSC.checkForValidUrl(url)
ok, html = KSC._fetch_uri(chkURI)
if ok then
local cjson = require "cjson"
json = cjson.decode(html)
if 1 == json.success then
ngx.log(ngx.INFO,"\n\tKCS say: ",html,"\n\t")
ngx.say("KCS /phish?:\t", KSC.PHISHTYPE[tostring(json.phish)])
else
ngx.say("KCS say:\t",html)
end
end
end
∞分钟 折腾到永远¶
好了,已经完成了预期的 :
- 使用 OpenResty
- 包装 金山网址云安全开放API 为接口服务
- 只要 curl -d “uri=http://sina.com” localhost:9090/=/chk 即可返回,金山云的数据!
但是 :
- 代码很集中,业务逻辑离头部很远,很难看
- 当没有进行 POST 请求时,以及各种其它意外请求时,并没智能的捕获,并提示
- 变量名设计的都很挫,需要重构
...
所以,继续折腾...
本章节,撰写ing,,,先散乱的从 OpenResty 列表中收集了一些自个儿感觉很基础的技巧
- 但是工作原因,没有时间继续深入了
- 好在是开源图书,有心者可以共同完善下去...
ngx_lua 管理的 cosocket 和 nginx_http_uptream 的平行关系 http://agentzh.org/misc/slides/libdrizzle-lua-nginx/#57
再出发¶
用非常与注的 42 分钟,算是将将入得 OpenResty 的门来, 然后?!
- 真正理解 `OpenRsty`_ 之下的 Nginx 了嘛?
- 真正能嵌入复杂的业务跑生产系统了嘛?
- 真正的真正,,,
所以,,,
警告
- 益rz ...
一切的效率发源:
Kernel Asynchronous I/O (AIO) Support for Linux http://lse.sourceforge.net/io/aio.html
替换 luacurl 回归非阻塞¶
问题¶
分析¶
解决¶
agentzh agentzh@gmail.com 发送至: openresty@googlegroups.com 日期: 2012年3月16日 下午6:33 主题: Re: ngx_lua 中访问远方 HTTP 服务的两种推荐的做法 (Was Re: [openresty:287] 42分钟,乱入 OpenResty 手册!-)) 明显是因为你漏了我的示例代码中的下面这一行: >> >> resolver 114.114.114.114; >> >> 咱能直接复制粘贴么?上面的 Nginx 出错信息也指示你未配置 resolver 指令: >> >> http://wiki.nginx.org/HttpCoreModule#resolver >> > > - FT! 俺想 DNS 当前可用就没有配置吼,, > - 不明白的代码就没抄,,, > 看来都有深意,先用起来,再理解,,, > - 是为了防止各种内部网络的乱解析? >
因为 Nginx 不会自动读取当前系统中的 DNS resolver 配置。
非阻塞访问远方 http 服务有两种做法。下面以从 ngx_lua 中访问百度搜索为例,演示一下这两种做法:
使用 nginx 子请求 + ngx_proxy 模块:
resolver 114.114.114.114;
- location = /baidu {
internal; proxy_pass http://www.baidu.com/s?wd=$arg_query;
}
- location = /test {
- content_by_lua ‘
- local res =
ngx.location.capture(“/baidu”, { args = { query = “openresty” }})
- if res.status ~= 200 then
ngx.say(“failed to query baidu: ”, res.status, ”: ”, res.body) return
end ngx.say(res.body)
‘;
}
然后请求这里的 /test 接口便可以得到 baidu 里搜索 openresty 查询词时的 HTML 结果页(不过要仔细字符编码哦)。
使用 cosocket API 访问之:
resolver 114.114.114.114;
- location = /test {
- content_by_lua ‘
local sock = ngx.socket.tcp() sock:settimeout(1000) local ok, err = sock:connect(“www.baidu.com”, 80) if not ok then
ngx.say(“failed to connect to baidu: ”, err) return
end local req = “GET /s?wd=openresty HTTP/1.0\r\nHost: www.baidu.com\r\n\r\n” sock:send(req) local read_headers = sock:receiveuntil(“\r\n\r\n”) headers, err = read_headers() if not headers then
ngx.say(“failed to read response headers: ”, err) return
end local body, err = sock:receive(“*a”) if not body then
ngx.say(“failed to read response body: ”, err) return
end ngx.say(body)
‘;
}
这里直接用 TCP cosocket API 现写了一个简单的 HTTP 1.0 客户端访问 baidu.com,得到了 openresty 查询词的结果页。当然,未来等有人仿照上面的做法,专门基于 cosocket 实现了完整的 HTTP 客户端库 lua-resty-http 之后,便会更有方便,就像那些已经基于 cosocket 实现的 lua-resty-memcached, lua-resty-redis 和 lua-resty-mysql 库一样(见 https://github.com/agentzh/lua-resty-memcached ,还有 https://github.com/agentzh/lua-resty-redis 以及 https://github.com/agentzh/lua-resty-mysql )
以且仅以上面两种方式访问远方服务在 ngx_lua 中才是非阻塞的。
Regards, -agentzh
怎么可以观察到 proxy_pass 真实进行请求的 url ?¶
> - 以便明确lua 的处理是否正确?
有两种方法:
一是使用 nc 工具。比如在本地用下面的命令启动 nc 以监听 1978 端口:
nc -l 1978
(有的系统会需要使用命令 nc -l -p 1978),然后临时修改你的 nginx 配置以便让 proxy_pass 指向它:
proxy_pass http://127.0.0.1:1978/phish/?$args
重新加载 nginx 配置后如从前那般请求你的接口,然后在启动 nc 的终端检查 ngx_proxy 发送的原始的 HTTP 请求。
- 重新编译 nginx 以启用 Nginx 调试日志,然后在 nginx 配置文件中使用 debug 日志级别。然后正常请求你的接口,在 Nginx 的 error.log 文件中应当会看到类似下面这样的 dump:
[debug] 20844#0: *1 http proxy header: “GET /foo?blah HTTP/1.0^M Host: 127.0.0.1:1987^M Connection: close^M ^M “
当然,使用 tcpdump 之类的抓包工具来搞也是可以的。
是也乎¶
这里的一个建议是,尽量不要对 ngx.var.request_uri 进行正则匹配,因为 Nginx 的标准变量 $request_uri 是未经过 URI 解码的原始形式,比如 “/a b” 和 “/a%20b” 是彼此等价的 URI,但作为字符串它们又是不相等的。建议改成分别对 ngx.var.uri 和 ngx.req.get_args()[“test”] 进行匹配。
可以看一看下面这个例子:
- location /test {
default_type “text/plain”; content_by_lua ‘
ngx.say(“request uri: ”, ngx.var.request_uri) ngx.say(“uri: ”, ngx.var.uri) ngx.say(“arg test: ”, ngx.var.arg_test) ngx.say(“arg test 2: ”, ngx.req.get_uri_args()[“test”])‘;
}
假设当前 nginx 监听的是本机的 1984 端口,则当在 Firefox 地址栏里输入下面这个 URL 时,
http://localhost:1984/test it?test=a b
会得到这样的页面输出:
request uri: /test%20it?test=a%20b uri: /test it arg test: a%20b arg test 2: a b
我们看到,Firefox 作为编写良好的 HTTP 客户端,根据 RFC 的要求,自动把原始 URL 中的空格字符编码为了 %20 序列。
附录A: 脱水版 agentzh之Nginx教程¶
千呼万唤之下,终于:agentzh 的 Nginx 教程(版本 2012.03.11) - http://agentzh.org/misc/nginx/agentzh-nginx-tutorials-zhcn.html - 算是成了系列
但是! - agentzh 真心看原文书多了 - 行文没有了中国教材的权威感觉 - 一步步,反复演示/引导,分享了对于 Nginx 的各种血淋淋的细节技术经验
可是~ - 我们就想要快速知道怎么玩吼 - 认同你 agentzh 的人品, OpenResty 的品质了 - 不甭什么内核细节了,告诉俺怎么用就对才好!
所以,俺根据自个儿的理解,脱水压缩系列 blog 成为条目,以便快速掌握,,,
警告
- 完全 Zoom.Quiet 私人理解
- 不代表 OpenResty 官方学识
- 大家自个儿看着办,俺尽量负责了,,, Orz ...
关于作者¶
-
- Python 中文社区创始人 / 管理员之一,热心于各种开源技术社区的公众事业,大家熟知的社区”大妈”;OBP及蟒营工程设计者
修订记要¶
- 130306 增补监察JS
- 120310 01:01 Zoom.Quiet 首版完成...
- 120308 Zoom.Quiet 启动乱入系列手册,分享乱来的学习体验;-)
- 120305 Zoom.Quiet 基本完成 `OpenReaty`_ 的基础探索
- 120229 Zoom.Quiet 开始学习 `OpenReaty`_
All tests successful. Files=7, Tests=28, 2 wallclock secs ( 0.04 usr 0.02 sys + 1.12 cusr 0.11 csys = 1.29 CPU) Result: PASS
AGENT/Test-Nginx-0.18.tar.gz /usr/bin/make test – OK
Running make install Installing /Library/Perl/5.12/Test/Nginx.pm Installing /Library/Perl/5.12/Test/Nginx/LWP.pm Installing /Library/Perl/5.12/Test/Nginx/Socket.pm Installing /Library/Perl/5.12/Test/Nginx/Util.pm Installing /usr/local/share/man/man3/Test::Nginx.3pm Installing /usr/local/share/man/man3/Test::Nginx::LWP.3pm Installing /usr/local/share/man/man3/Test::Nginx::Socket.3pm Appending installation info to /Library/Perl/Updates/5.12.3/darwin-thread-multi-2level/perllocal.pod
AGENT/Test-Nginx-0.18.tar.gz /usr/bin/make install – OK
zoomq @ Zq111216MBP in ~/Works/lua/org.openresty $ sudo cpan Test::Nginx
基于结构化文本~
的图书工程¶
本书行文体例¶
本书使用不同的体例来区分不同的情景.
精巧地址¶
本书包含很多外部网站的URL地址,但是图书必竟不是网页,读者无法点击进入相关网站;所以,笔者尝试使用URL精简工具来帮助读者可以快速输入自动跳转到原有网站来访问;
- 比如说: 本书的维基入口 http://wiki.woodpecker.org.cn/moin/ObpLovelyPython
- 精巧地址: http://bit.ly/2QA425
- 输入的字符量少了三倍! 这是借助 http://bit.ly 提供的网址精简服务达到的效果;
- 提醒:毕竟这是借用外国的免费服务进行的精简,如果读者输入后不能自动跳转的话,可能是网络问题也可能是服务问题,那就只能麻烦读者重新使用原有的URL进入了;
程序体例¶
使用有语法颜色的代码引用
def foo():
print "Love Python, Love FreeDome"
print "E文标点,.0123456789,中文标点,. "
可以 用 .. code-block:: 追加各种语法高亮声明:
.. code-block:: python
:linenos:
def foo():
print "Love Python, Love FreeDome"
print "E文标点,.0123456789,中文标点,. "
效果:
1 2 3 | def foo():
print "Love Python, Love FreeDome"
print "E文标点,.0123456789,中文标点,. "
|
外部包含:
.. literalinclude:: example.py
:language: python
效果:
@route('%s/'%ini.urlprefix)
def index():
__urlog("INFO","idx++")
return template('index.tpl',urlprefix=ini.urlprefix)
文本体例¶
引用,题词:
No matter where you go, there you are.
—Buckaroo Banzai
技巧警示:
注解
(~_~)
- This icon signifies a tip, suggestion, or general note.
警告
(#_#)
- 警告得注意的...
参见
(^.^)
- 指向参考的...
附加说明:
进一步的
包含题外的信息,笔者心路,等等和正文有关,但是不直接的信息
知识引用:
- 使用边注
- 追随正文
- 活动説明
- 效果如右
rST排版技巧¶
跨章节指引¶
行文中,经常要对其它章节进行指引,在 html 中对应的就是 锚点链接
- rST 中提供了非常优雅的解决:
- 使用通用元素定义
- 比如説:
各个章节的首页一般是 index.rst
头一行,习惯性加个聲明:
.. _chapter2index:
那么,在其它任意文本中,随时可以使用:
:ref:`基本电子学 <chapter2index>`
来生成一个指向第二章 首页的链接!
插图/表格指代¶
行文中,经常对指定插图/表格 进行指代
- rST 中提供了非常优雅的解决:
- 进行通用元素定义
- 比如说
.. _fig_2_4:
.. figure:: _static/figs/tmux-curl-test.png
插图 2-4 命令行测试情景
然后,就可以在任意地方使用 插图 2-4 命令行测试情景 来指代, 实际输出的就是 “插图 2-4 命令行测试情景”

插图 2-4 命令行测试情景
上下标号¶
有时要进行数学/化学的表示,在 html 中就需要上/下标( <sub> , <sup>) 的表达, rST 中当然也有:
H\ :sub:`2`\ O
E = mc\ :sup:`2`
效果:
H2O
E = mc2
注解
注意:
这里的 只是为了制造语法空间,输出时,是没有空格的了,,,
线性表格¶
中文的非等宽性导致 rST 这种字符艺术式的图表很难作!
===== =====
A not A
===== =====
False True
True False
===== =====
所以,使用列表也可以方便的生成表格:
.. list-table:: 实例
:widths: 15 10 30
:header-rows: 1
* - Treat
- Quantity
- Description
* - Albatross
- 2.99
- On a stick!
* - Crunchy Frog
- 1.49
- If we took the bones out, it wouldn't be
crunchy, now would it?
* - Gannet Ripple
- 1.99
- On a stick!
效果
Treat | Quantity | Description |
---|---|---|
Albatross | 2.99 | On a stick! |
Crunchy Frog | 1.49 | If we took the bones out, it wouldn’t be crunchy, now would it? |
Gannet Ripple | 1.99 | On a stick! |
段落层次约定¶
使用 reSTsections
共分4级
=======================
大标题
=======================
-----------------------
小标题
-----------------------
^^^^^^^^^^^^^^^^^^^^^^^
二级标题
^^^^^^^^^^^^^^^^^^^^^^^
"""""""""""""""""""""""
三级标题
"""""""""""""""""""""""
再小,就使用列表!:
- 列表项目1
- 列表项目2
- ...
效果: