之前听说过@agentzh出品的openresty集成了很多有用的第三方module,特别是集成了lua相关的一系列东西,非常好用,这次终于有机会来尝试了。使用之后总体感觉就是一个字,“爽”,有一种甩开java的畅快感。
首先为了省事,没有安装nginx官方版本再自己一个个的去安装module,安装的是openresty最新的稳定版。我们使用的到的模块,基本包括了ngx_lua、ngx_redis2、cjson、rds这些模块。另外为了从nginx直接访问数据库,使用了drizzle模块。
操作的流程概括起来讲,就是一方面通过ngx_lua模块抽取http post数据,进行一些简单的分析判断,将数据插入redis;另外一方面,当需要返回数据给客户端的时候,会调用redis2模块从多个redis DB中获取数据并拼接,最后转为json格式的字符串。
下面展示几个lua的code点:
-
使用content_by_lua_file调用lua脚本文件来实现逻辑,比如
location ~ ^/lua1 { default_type 'text/plain'; set $latesttime $arg_latesttime; set $lasttime $arg_lasttime; set $limit $arg_limit; set $offset $arg_offset; content_by_lua_file conf/timeline.lua; }
而timeline.lua文件里面就可以放具体的业务逻辑了
-
使用content_by_lua,直接把脚本写在nginx配置文件中,比如
location ~ ^/luatest$ { default_type 'text/plain'; set $latesttime $arg_latesttime; set $lasttime $arg_lasttime; set $limit $arg_limit; set $offset $arg_offset; content_by_lua ' local parser = require("redis.parser") local minscore if #ngx.var.lasttime == 0 then minscore = "-inf" else minscore = ngx.var.lasttime end local maxscore if #ngx.var.latesttime == 0 then maxscore = "+inf" else maxscore = ngx.var.latesttime end local subQuery = "ZRANGEBYSCORE luatest_zset " .. minscore .. " " .. maxscore .. " withscores limit " .. ngx.var.offset .. " " .. ngx.var.limit .. "\\r\\n" local res = ngx.location.capture("/redis",{args={query=subQuery}}); if res.status == 200 then reply = parser.parse_reply(res.body) local data = {} data["data"] = reply local cjson = require("cjson") ngx.say(cjson.encode(data)) return else ngx.exit(500) end '; } location /redis { internal; set_unescape_uri $query $arg_query; redis2_raw_query $query; redis2_pass redis-pool; }
这一大段的code中,主要有下面几个点:
- 使用set_misc中的set_unescape_uri来对传入location中的参数进行unescape,nginx会对传入的url参数进行escape,不管它是来自于客户端直接请求,还是在内部通过capture形式调用
- internal保证不能通过外部访问,确保安全性
- redis这个location专门接受内部调用,用来直接发起对redis的请求,redis-pool是配置在upstream中的对redis的连接池,使用redis2_raw_query可以保证这种灵活性。但是需要注意的是,使用redis2_raw_query的时候需要在你字符串的最后加入换行“\r\n”,否则语句不会被执行
- 在content_by_lua中,获取request的参数,做一些简单的判断,拼接成redis query串,然后使用ngx.location.capture发起subrequest,并使用lua对返回的数据进行处理,转换为json格式的字符串,使用ngx.say()返回给client。注意此处ngx.say()会在输出末尾自动加上换行,如果想返回一个纯字符串,可以使用ngx.print()
第一天code完了之后快下班的时候,才注意到原来开了eclipse一整天一分钟都没用过,而原来用java可能需要开发2、3天还得小心测试的code一天已经搞定并测试过了。并且nginx+lua的组合在淘宝已经经过了实战的检验,被证明是可以承受住相当的流量的。那么这样一个开发、调试迅速,系统消耗还低的组合,你是不是值得拥有?