想在api服务器里实现一个acl的功能,对某些请求(需要登录,需要检查appkey,需要限制频次等)做限制,对某些起始状态(比如登陆)或者终结状态(比如报错)的请求放行。
因为lua里木有switch case,因此通过一个acl_list的table去查找规则,因为需要限制的请求种类比较多,就把rule_check_token当成默认值了,一看是是这么写的:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | local _M = {     ACL_PASS = 0,     ACL_DENIED = 1, } function _M.rule_always_pass(self)     return _M.ACL_PASS end function _M.rule_check_token(query)     if query == nil then         return _M.ACL_DENIED     end     local token = query['token'] or ngx.req.get_headers()['Authorization']     local uid = query['uid']     -- ... end _M.acl_list = setmetatable({     ['error'] = _M.rule_always_pass,     ['user/login'] = _M.rule_always_pass, }, {     __index = _M.rule_check_token }) return _M | 
然而却会在local token= xxxx那一行报stack overflow,想了半天也发现哪里有无限递归,因为query传进来的是http请求的query string解析出的键值对表。
把query打印出来一看,发现竟然是这个模块本身……
仔细看了文档才知道,原来__index后面的值是一个function时,lua会调用这个function去获得不存在的键,并且第一个参数是模块本身(即_M,一个table)。在这个例子里:
- 调用一个非默认规则的api
- lua调用了rule_check_token
- 参数query被传入了_M本身
- 运行到local token= xxxx这一行
- 这个table里(_M)又没有token这个键
- 回到2
所以就死循环了
所以要好好看文档
解决方法是可以套一个function
| 1 |  __index = function() return _M.rule_check_token end | 
贴一个打印table的工具,方便调试:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 | function print_r(tbl, pref)     for k,v in pairs(tbl) do          if type(v) == 'table' then             print((pref or "|-"), k, "\t", "<table>")             _print_r(v, "  "..(pref or "|-"))         else             if type(v) == 'function' then                 v = "   <function>"             end             print((pref or "|-"), k, "\t", v)         end     end end | 
可以打印出如下形式:
| 1 2 3 4 5 6 7 8 | |-_VERSION      0.01 |-rule_always_pass         <function> |-ACL_DENIED    1 |-acl_list      <table>   |-error          <function>   |-user/login     <function> |-ACL_PASS      0 |-rule_check_token         <function> | 
使用openresty请自行改写成local function和ngx.say的形式
