开放平台openresty改造实际线上项目 开发笔记,


lua 分享

配置参数 lua_code_cache on

  • 当配置参数lua_code_cache 为 off 的时候, 每次修改lua代码会生效。init_by_lua_block 每次执行请求都会执行。 不能使用lrucache 并且不能缓存。 init_worker_by_lua_block 执行work线程数的次数。。lua_code_cache 为off 情况下可以用于代码调试,不过会产生很多bug。
  • 当配置参数lua_code_cache 为 ok 的时候, init_by_lua_block 执行一次。能够使用lrucache。init work 阶段一样。
在使用lua_code_cache on 的情况下用于一次性全流程测试。不能说明调通就证明cache on 就正确。 推荐使用方式,段代码微调使用off ,测试使用 on ,线上使用on

配置参数 lua_shared_dict config 100m; 共享字典与lur cache

  • lua_shared_dict 所有work 进程共享, lru cache 本 work 进程共享。
  • 根据c 的结构体发现 能够存储以下三种类型 boolean 、number 、string。 而 lru cache 能够存储lua的所有类型
enum {
    SHDICT_TNIL = 0,        /* same as LUA_TNIL */
    SHDICT_TBOOLEAN = 1,    /* same as LUA_TBOOLEAN */
    SHDICT_TNUMBER = 3,     /* same as LUA_TNUMBER */
    SHDICT_TSTRING = 4,     /* same as LUA_TSTRING */
    SHDICT_TLIST = 5,
};
  • 使用lru cached 需要开启 lua_code_cache on;使用 lua_shared_dict 需要在配置文件中配置。
  • lua_shared_dict 存储 表接口数据需要 encode 到string , decode 到 table。

set_header() 与 get_headers() 方法

  • ngx.req.set_header(“Accept-Encoding”, v) 只能设置一个值, v 如果是table 也是取得table中的第一个字赋值给Accept-Encoding
  • ngx.req.get_headers() 可能是string 也可能是 table ,如果是table 类型相当于多参数, 当使 用set_header 方法table只能修改其中一个。

math.random(1, #list) 方法

  • math random 随机不离散, 必须借助于随机种子,随机种子只需要种植一次。 这个与java 完全不同。
  • 随机种子在 init 阶段设置就可以 math.randomseed(ngx.time())
function _M:init()
    math.randomseed(ngx.time())
    local server = ngx.shared.server
    server:set("local_ip", utils.get_local_ip())
end


local index = math.random(1, 20)


nginx http{include 与 default_type} 对 ngx.resp.get_headers() 中 Content-Type 的影响

  • 配置了include 与 default_type 如果后端没有返回Content-Type nginx会根据include和返回的body自动匹配。 如果include没有匹配的则Content-Type为defaul ttype
  • ngx.resp.get_headers() 取到的是已经匹配了Content-Type的值,所以说后端没有返回Content-Type nginx会设置上并且ngx.resp.get_headers()会取到。
  • 这样说来ngx.resp.get_headers()的执行是在nginx模块http的后面
http {
    #文件扩展名与文件类型映射表
    include       mime.types;

    #默认文件类型,默认为text/plain
    default_type text/html;
    
    ...


say 和 exit 组合使用 问题 : 晶大神的 blog https://segmentfault.com/a/1190000004534300

  • 当传入的status >= 200(200即为ngx.HTTP_OK),ngx.exit() 会中断当前请求,并将传入的状态码(status)返回给nginx。
  • 当传入的status == 0(0即为ngx.OK)则 ngx.exit() 会中断当前执行的phrase(ngx-lua模块处理请求的阶段,如content_by_lua*),进而继续执行下面的phrase。
  • 对于 ngx.exit() 需要进一步注意的是参数status的使用,status可以传入ngx-lua所定义的所有的HTTP状态码常量(如:ngx.HTTP_OK、ngx.HTTP_GONE、ngx.HTTP_INTERNAL_SERVER_ERROR等)和两个ngx-lua模块内核常量(只支持NGX_OK和NGX_ERROR这两个,如果传入其他的如ngx.AGAIN等则进程hang住)。
  • say 相当于直接执行header filter 和 bodyfilter 等阶段,一直到结束。
  • 一般情况下 先say 然后 在exit 。 当 status >=200 的时候中断请求,然后执行响应阶段, 其他情况中断当前阶段继续执行下一个阶段。
  • say之后做exit依然会到body阶段,并可根据arg[1]修改返回内容。
  • 只是做exit 状态直接返回。

pairs 和 ipairs

  • pairs 主要用于map , ipairs 主要用于数组
  • ipairs 有序, 下标 1开始, 如果遇到没有下标的值就结束。相当于有些值是表结构或者 下标为字符串结构的不能遍历
  • pairs 用于 map 能遍历出来所有,无序。

ngx.var.upstream_host 自定义变量的使用

  • 所有变量必须在 location 中初始化。
server {
    server_name api.weibo.com;
    listen 0.0.0.0:8888;
    listen 127.0.0.1:8888;
    #listen 10.211.55.3:8888;
    error_page 400 404 408 411 412 413 414 417 494 /va_error_handler;
    error_page 500 502 503 504 /va_error_handler;

    client_body_buffer_size 5m;
    lua_need_request_body on;

    #real_ip_header     X-Real-IP;
    #real_ip_recursive  off;

    location / {
        set $upstream_host               '';
        set $upstream_uri                '';
        set $ctx_ref                     '';

使用方式:
 if string.find(temp_uri, "?") == nil then
    ngx.var.upstream_uri = temp_uri .. "?" .. ctx.req.uri_args_str
else
    ngx.var.upstream_uri = temp_uri .. "&" .. ctx.req.uri_args_str
end


ngx.header.content_length 巧用

  • 置为 nil 用trunk的方式返回

获取请求参数问题 问题

1、 ngx.req 所涉及方法都十分耗时
2、 ngx.req 获取参数如果有相同key返回格式
  • 参数可能存在table类型 eg: url?aa=123&aa=456 --> {aa={123,456}}
  • 返回的数据类型不限于string也会有boolean
  • ngx.req.get_uri_args
  • ngx.req.get_post_args 使用前先调用ngx.req.read_body() 或者在配置中加入lua_need_request_body on;
  • ngx.req.get_headers
  • 对于获取参数这种频繁操作透明代理解决方案, 封装到ngx.ctx.req

各阶段不可使用API

  • body_filter_by_lua 这个阶段不允许用sql
  • body_filter_by_lua 这个阶段不允许用say
  • body_filter_by_lua 也不能用exit
  • body_filter_by_lua 修改返回的header不会生效

pcall xpcall 问题

  • 1、xpcall
  • xpcall 可以获取报错的栈信息。 但是 function(err) 是异步的回调函数
  • 如下代码,当va.header_filter()报错时依然会输出come here
xpcall(function() va.header_filter() end, function(err)
    ngx.log(ngx.ERR, "\n==>Err. va.header_filter Error: " .. err )
    ngx.log(ngx.ERR, "\n==>Err. va.header_filter Error: " .. debug.traceback() )
    end)
ngx.log(ngx.ERR, "come here“ )
  • 2、pcall
  • pcall无法打印错误的堆栈信息,但是可以做到同步获取的效果
local status, retval = pcall(
       	function() auth_client:httpAuth
    end
if not status then
    ngx.log(ngx.ERR, "auth_client is error: ", retval, " url: ", ngx.var.request_uri .. "\n")
end

header_filter_by_lua_block body_filter_by_lua_block

access,content等请求之前阶段,调用的ngx.say ngx.status都会到这两个阶段

header_filter_by_lua_block
  • 1、可以修改返回头,先于body_filter_by_lua_block阶段执行,修改头后body阶段就会有变动
  • 2、本阶段只会被调用一次
body_filter_by_lua_block
  • 1、本阶段不要修改返回头,有修改也不会生效返回头信息,因为返回头已经被返回到调用方了。
  • 2、本阶段会被多次执行,执行次数看返回报文的大小而定。 返回流结束标识ngx.arg[2]=true

unpack 使用

  • 数组过大,会报错 too many results to unpack
  • 长度限制在于数组的下标长度。 数组个数限制在8000
  • 根据栈的使用情况,这个限制可能更小一点
  • 使用周晶FFI 方法

… 和 table insert

  • 经过代码测试, … 少于30 情况下 性能优于 table insert
local strs = {};
    for i = 1, 3, 1 do
        strs[i] = "helloworld";
    end

local startTime = os.clock();
for i = 1, 2000000, 1 do
    local result = {};

   -- for index, str in ipairs(strs) do
        table.insert(result, strs[1])
        table.insert(result, strs[2])
        table.insert(result, strs[3])
   -- end
    result = table.concat(result);

end

local endTime = os.clock();
    local useTime = endTime - startTime;

    print("消耗时间:" .. useTime .. "s");


local strs = {};
    for i = 1, 3, 1 do
        strs[i] = "helloworld";
    end
    local startTime = os.clock();
	for i = 1, 2000000, 1 do
	
	   result = strs[1] .. strs[2] .. strs[3]
	end
    local endTime = os.clock();
    local useTime = endTime - startTime;

    print("消耗时间:" .. useTime .. "s");
    

ngx.re.find string.find 性能调优

  • ngx.re.find 使用 “jo” 启用 pcre JIT 支持。
  • 为什么要比较,就是单一字符串场景两种放法都可。那么使用哪个更好呢?

1、jo 参数的作用
下面是常用的选项:

  • i 大小写不敏感
  • j 开启 PCRE JIT 预编译
  • o 编译一次模式,开启 worker 进程对编译过的 regex 缓存
  • J PCRE Javascript 兼容模式
  • m 多行模式
  • s 单行模式
  • u utf-8模式
  • U utf-8模式,只是不检测 utf-8有效性检测
  • x 扩展模式
    local url_str = "http://blog.stackoverflow.com/2016/10/Stack-Overflow-92-Podcast-The-Guerilla-Guide-to-Interviewing/?cb=1"

    local max = 1000000
    local t = os.clock()
    for _ = 1, max do
        ngx.re.find(url_str, [[^https?://[\w-_?.:/+=&#%]+$]])
    end
    ngx.log(ngx.ERR, "ngx.re.find nil cost is ", os.clock() - t)

    local t1 = os.clock()
    for _ = 1, max do
        ngx.re.find(url_str, [[^https?://[\w-_?.:/+=&#%]+$]], "jo")
    end
    ngx.log(ngx.ERR, "ngx.re.find jo cost is ", os.clock() - t1)
    
    2018/09/13 06:32:40 [error] 13#13: *14 [lua] init.lua:103: access(): ngx.re.find nil cost is 1.84
    2018/09/13 06:32:40 [error] 13#13: *14 [lua] init.lua:110: access(): ngx.re.find jo cost is 0.16

2、string.find ngx.re.find 无正则情况对比

    local url_str = "我打死奥的煎熬ID阿吉豆我安静到都弄一你师父哪懂啊达到ID煎熬ID骄傲的"

    local max = 1000000
    local t = os.clock()
    for _ = 1, max do
        string.find(url_str, "安静到")
    end
    ngx.log(ngx.ERR, "string.find cost is ", os.clock() - t)


    local t1 = os.clock()
    for _ = 1, max do
        ngx.re.find(url_str, "安静到", "jo")
    end
    ngx.log(ngx.ERR, "ngx.re.find jo cost is ", os.clock() - t1)
    
    2018/09/13 07:46:42 [error] 14#14: *33 [lua] init.lua:110: access(): string.find cost is 0
    2018/09/13 07:46:42 [error] 14#14: *33 [lua] init.lua:117: access(): ngx.re.find jo cost is 0.05

3、string.find 对正则支持的很弱,不建议使用。 性能也是弱与ngx.re.find的

    local url_str = "http://blog.stackoverflow.com/2016/10/Stack-Overflow-92-Podcast-The-Guerilla-Guide-to-Interviewing/?cb=1"
    local max = 1000000
    local t = os.clock()
    for _ = 1, max do
        url_str:find("^https?://[%w-_%.%?:/%+=&#%%]+$")
    end
    ngx.log(ngx.ERR, "string.find cost is ", os.clock() - t)


    local t1 = os.clock()
    for _ = 1, max do
        ngx.re.find(url_str, "^https?://[%w-_%.%?:/%+=&#%%]+$", "jo")
    end
    ngx.log(ngx.ERR, "ngx.re.find jo cost is ", os.clock() - t1)
    
    2018/09/13 06:54:08 [error] 14#14: *88 [lua] init.lua:108: access(): string.find cost is 0.84
    2018/09/13 06:54:08 [error] 14#14: *88 [lua] init.lua:115: access(): ngx.re.find jo cost is 0.55

ngx.re.find 注意事项

注意一些正则的特殊字符,需要转义后匹配
例如: 
“.”   需要转义为 /.
“*”   需要转义为 /*

lru_cache优化

  • 通过火焰图看到 log、ngx.re.find、ngx.re.sub、ngx.re.gsub 、table 、json_decode 等性能不好
  • 路由信息存储到共享字段中,由于共享字典原因 必须存储字符串。所以必须进行encode 和 decode
  • 我们采用了lru_cache work进程级别的缓存方式
  • https://github.com/openresty/lua-resty-lrucache 当然也是春哥的

死锁

static int
ngx_http_lua_shdict_get_helper(lua_State *L, int get_stale)
{
    ngx_shmtx_lock(&ctx->shpool->mutex);
    rc = ngx_http_lua_shdict_lookup(zone, hash, key.data, key.len, &sd);
    switch (value_type) {
    case SHDICT_TSTRING:
        lua_pushlstring(L, (char *) value.data, value.len); -- 交个lua
        break;
    default:
        ngx_shmtx_unlock(&ctx->shpool->mutex);
        return luaL_error(L, "bad value type found for key %s in "
                          "shared_dict %s: %d", key.data, name.data,
                          value_type);
    }
    user_flags = sd->user_flags;
    ngx_shmtx_unlock(&ctx->shpool->mutex);
    return 1;
}

int
ngx_http_lua_ffi_shdict_get(ngx_shm_zone_t *zone, u_char *key,
    size_t key_len, int *value_type, u_char **str_value_buf,
    size_t *str_value_len, double *num_value, int *user_flags,
    int get_stale, int *is_stale, char **err)
{
    ngx_shmtx_lock(&ctx->shpool->mutex);
    rc = ngx_http_lua_shdict_lookup(zone, hash, key, key_len, &sd);
    switch (*value_type) {
    case SHDICT_TSTRING:
        *str_value_len = value.len;
        ngx_memcpy(*str_value_buf, value.data, value.len); -- 自己搞
        break;
    default:
        ngx_shmtx_unlock(&ctx->shpool->mutex);
        return NGX_ERROR;
    }
    ngx_shmtx_unlock(&ctx->shpool->mutex);
    return NGX_OK;
}
  • 就此发现使用的是 c 进行共享字典操作的。 根据春哥所说的, 获取共享字典并C不是原子操作,
    FFI方式是原子操作。

内存泄露

  • 查看 top 内存不断增长
  • 系统出现 nginx: lua atpanic: Lua VM crashed, reason: not enough memory
  • 利用 https://github.com/openresty/openresty-gdb-utils 分析工具进行分析
  • lgcstat
15172 str        objects: max=2956, avg = 51, min=18, sum=779126
 987 upval      objects: max=24, avg = 24, min=24, sum=23688
 104 thread     objects: max=1648, avg = 1622, min=528, sum=168784
 431 proto      objects: max=226274, avg = 2234, min=78, sum=963196
 952 func       objects: max=144, avg = 30, min=20, sum=28900
 446 trace      objects: max=23400, avg = 1857, min=160, sum=828604
2965 cdata      objects: max=4112, avg = 17, min=12, sum=51576
18961 tab        objects: max=24608, avg = 207, min=32, sum=3943256
   9 udata      objects: max=176095, avg = 39313, min=32, sum=353822
  • lgcpath
(gdb) lgcpath 100000 tab
path 000:[registry] ->Tab["_LOADED"] ->Tab["ffi"] ->Tab["gc"] ->cfunc ->env ->Tab sz:196640 (GCobj*)0x40784f58 ->END
path 001:[registry] ->Tab[tv=0x4132e470] ->Tab sz:524328 (GCobj*)0x40783108 ->END
  • 通过以上能确定大对象和问题点。
  • 查看操作这个对象的地方,和官方文档。
  • 逻辑链路测试,一直到内存不增长。
  • 分析原因。

内部跳转 复制 ctx 使用 exit能够回收的幻觉

  • 我们使用了v框架 v框架 中有支持内部跳转的ctx 复制方法
 function V.access()
    -- @usage: use to check the xctx lib for ab/wrk test
    -- if ctx.req.uri_args.f == "a" then
    --     ctx.xxxxx = 'a'
    -- elseif ctx.req.uri_args.f == "b" then
    --     ctx.xxxxx = 'b'
    -- end
    _v()
    xctx.stash_ngx_ctx()
end
这里是先调用的v  然后在进行 ctx 复制 
  • 在我们测试过程中,发现exit 可以防止内存溢出,这个就是假象。本来经过lgcstat和lgcpath 分析发现tab很大,最大的table是ctx,于是想办法清空ctx,误入歧途。lua是一门奇怪的语言,排查问题重点不能直击异常信息,会误入歧途。

while 循环 cpu 100% bt

 #0  0x00007fd9c5a1cbfa in err_unwind (L=L@entry=0x4192c378, stopcf=0x7fffbb362c00, errcode=errcode@entry=0) at lj_err.c:111
#1  0x00007fd9c5a1cfe5 in lj_err_unwind_dwarf (version=<optimized out>, actions=1, uexclass=5500374307216568836, uex=0x7fd9c64f6720, ctx=0x7fffbb362850)
    at lj_err.c:241
#2  0x00007fd9c4d20bb3 in _Unwind_RaiseException () from /lib64/libgcc_s.so.1
#3  0x00007fd9c5a1ce69 in err_raise_ext (errcode=4) at lj_err.c:302
#4  lj_err_throw (L=L@entry=0x4192c378, errcode=errcode@entry=4) at lj_err.c:516
#5  0x00007fd9c5a1d0a3 in lj_err_mem (L=L@entry=0x4192c378) at lj_err.c:552
#6  0x00007fd9c5a1cb30 in lj_mem_newgco (L=L@entry=0x4192c378, size=20) at lj_gc.c:833
#7  0x00007fd9c5a1fb8c in func_newL (L=L@entry=0x4192c378, pt=pt@entry=0x40f927a0, env=0x4192d9d8) at lj_func.c:122
#8  0x00007fd9c5a1fdb0 in lj_func_newL_gc (L=<optimized out>, pt=0x40f927a0, parent=0x41f80018) at lj_func.c:160
#9  0x00007fd9c5a18944 in lj_BC_FNEW () from /usr/local/openresty/luajit/lib/libluajit-5.1.so.2


#10 0x00007fd9c5a2989d in lua_pcall (L=L@entry=0x4192c378, nargs=nargs@entry=0, nresults=nresults@entry=1, errfunc=errfunc@entry=0) at lj_api.c:1129
#11 0x000000000052bc83 in ngx_http_lua_cache_load_code (log=log@entry=0x24f8680, L=L@entry=0x4192c378, key=key@entry=0x24e7ba8 "=log_by_lua(app-prod.conf:199)")
    at ../ngx_lua-0.10.13/src/ngx_http_lua_cache.c:56
#12 0x000000000052bdeb in ngx_http_lua_cache_loadbuffer (log=0x24f8680, L=L@entry=0x4192c378,
    src=0x24e7958 "\n", ' ' <repeats 12 times>, "local xpcall = xpcall\n", ' ' <repeats 12 times>, "local ngx = ngx\n", ' ' <repeats 12 times>, "xpcall(function() va.log() end, function(err)\n", ' ' <repeats 16 times>, "local ctx = ngx.ctx\n", ' ' <repeats 16 times>, "local err_str = err\n       "..., src_len=506,
    cache_key=0x24e7ba8 "=log_by_lua(app-prod.conf:199)", name=0x24e7b58 "=log_by_lua(app-prod.conf:199)") at ../ngx_lua-0.10.13/src/ngx_http_lua_cache.c:148

#13 0x000000000053a9cc in ngx_http_lua_log_handler_inline (r=0x24f92a0) at ../ngx_lua-0.10.13/src/ngx_http_lua_logby.c:153
#14 0x000000000053a79b in ngx_http_lua_log_handler (r=0x24f92a0) at ../ngx_lua-0.10.13/src/ngx_http_lua_logby.c:135
#15 0x000000000048e0c0 in ngx_http_log_request (r=r@entry=0x24f92a0) at src/http/ngx_http_request.c:3569
#16 0x000000000048f7d9 in ngx_http_free_request (r=r@entry=0x24f92a0, rc=0) at src/http/ngx_http_request.c:3516
#17 0x000000000048f9a0 in ngx_http_close_request (rc=0, r=<optimized out>) at src/http/ngx_http_request.c:3463
#18 0x000000000048f5b0 in ngx_http_run_posted_requests (c=0x7fd9c63090c0) at src/http/ngx_http_request.c:2274
#19 0x0000000000491cab in ngx_http_process_request_line (rev=0x7fd9b96869f0) at src/http/ngx_http_request.c:1049
#20 0x000000000047b1c7 in ngx_epoll_process_events (cycle=<optimized out>, timer=<optimized out>, flags=<optimized out>)
    at src/event/modules/ngx_epoll_module.c:902
#21 0x000000000047264b in ngx_process_events_and_timers (cycle=cycle@entry=0x24c0470) at src/event/ngx_event.c:252


#22 0x0000000000479102 in ngx_worker_process_cycle (cycle=cycle@entry=0x24c0470, data=data@entry=0xb) at src/os/unix/ngx_process_cycle.c:820
#23 0x0000000000477bc4 in ngx_spawn_process (cycle=cycle@entry=0x24c0470, proc=0x479090 <ngx_worker_process_cycle>, data=0xb, name=0x744b75 "worker process",
    respawn=respawn@entry=11) at src/os/unix/ngx_process.c:198
#24 0x000000000047a342 in ngx_reap_children (cycle=0x24c0470) at src/os/unix/ngx_process_cycle.c:687
#25 ngx_master_process_cycle (cycle=cycle@entry=0x24c0470) at src/os/unix/ngx_process_cycle.c:180
#26 0x0000000000453448 in main (argc=<optimized out>, argv=<optimized out>) at src/core/nginx.c:384


排查问题方式

  • 1、首先利用排除法 grep -v 方式排除已知的错误信息
  • 2、发现了 错误信息
  • 3、程序错误 -> 直接改程序,基本上是根据log 定位问题并且更改
  • 4、死锁、假死、cpu 100%问题 -> gdb -> lbt+bt 定位死锁位置 -> google 源码查看问题 -> 找春哥的回复解决。(能解决90%以上)
  • 5、内存泄露 -> Lua VM 泵机 -> error.log 出现 nginx: lua atpanic: Lua VM crashed, reason: not enough memory -> gdb -> lgcstat、lgcpath -> 找到最大对象和最大对象的索引位置 -> 分析产生原因(逻辑链路)最好用排除法 -> 复现以及解决(还是找春哥回复)
  • 6、 不能直击问题, 例如暴露 HTTP time out 可能是因为其他死锁照成的。等修复了死锁就不会出现 time out。
  • 7、openresty 库 和第三方库冲突问题,myql 兼容问题。错误log 暴露出 mysql 需要更高版本的 ngx_lua_module。如果你更改ngx_lua_module可就错误了。因为 查看源码看到 mysql调用的是ngx方法, 结果是ngx的lua模块bug。

lua的使用优化建议

1. 使用局部变量local
  • 避免命名冲突
  • 使用local function声明的函数为局部函数,在引用的时候必须要在声明的函数后面
  • 过程下最多拥有200个local变量,模块级local变量无限制
  • 访问速度更快,在生命周期之外就会释放掉
local a = 1 local b = 1 a = a + b 局部变量产生的指令
ADD 0 0 1

a = a + b 全局变量产生的指令
GETGLOBAL 0 0 ; a
GETGLOBAL 1 1 ; b
ADD 0 0 1
SETGLOBAL 0 0 ; a

代码测试 demo

local local_demo
local_demo = function()
    local t1 =os.clock ()

    for i = 1, 1000000000 do
        local x = math.sin(i)
    end

    ngx.update_time()
    local t2 =os.clock ()
    ngx.log(ngx.ERR, "local_demo cost time ========== " .. t2 - t1)
end

local_demo(): local_demo cost time ========== 0.31

local local_demo2
local_demo2 = function()
    local t1 =os.clock ()

    local sin = math.sin
    for i = 1, 1000000000 do
        local x = sin(i)
    end

    ngx.update_time()
    local t2 =os.clock ()
    ngx.log(ngx.ERR, "local_demo2 cost time ========== " .. t2 - t1)
end

local_demo2(): local_demo2 cost time ========== 0.33

2. 对于大table提前声明大小可以很大提高效率

– 数组的长度越大,提升越高

local local_demo
local_demo = function()
    local t1 =os.clock ()

    for i = 1, 50000000 do
        local a = {}
        a[1] = 1;
        a[2] = 1;
        a[3] = 1;
        a[4] = 1;
        a[5] = 1;
        a[6] = 1;
    end

    ngx.update_time()
    local t2 =os.clock ()
    ngx.log(ngx.ERR, "local_demo cost time ========== " .. t2 - t1)
end

local_demo(): local_demo cost time ========== 12.64


local local_demo2
local_demo2 = function()
    local t1 =os.clock ()

    for i = 1, 50000000 do
        local a = {1,2,3,4,5,6}
        a[1] = 1;
        a[2] = 1;
        a[3] = 1;
        a[4] = 1;
        a[5] = 1;
        a[6] = 1;
    end

    ngx.update_time()
    local t2 =os.clock ()
    ngx.log(ngx.ERR, "local_demo2 cost time ========== " .. t2 - t1)
end

local_demo2(): local_demo2 cost time ========== 0.02
local local_demo
local_demo = function()
    local t1 =os.clock ()

    for i = 1, 50000000 do
        local a = {}
        a[1] = 1;
        a[2] = 1;
    end

    ngx.update_time()
    local t2 =os.clock ()
    ngx.log(ngx.ERR, "local_demo cost time ========== " .. t2 - t1)
end

local_demo(): local_demo cost time ========== 4.61


local local_demo2
local_demo2 = function()
    local t1 =os.clock ()

    for i = 1, 50000000 do
        local a = {1,2}
        a[1] = 1;
        a[2] = 1;
    end

    ngx.update_time()
    local t2 =os.clock ()
    ngx.log(ngx.ERR, "local_demo2 cost time ========== " .. t2 - t1)
end

local_demo2(): local_demo2 cost time ========== 0.02

3. 避免使用table.insert
  • table.insert相比与数组直接复制效率差了3倍
local local_demo
local_demo = function()
    local t1 =os.clock ()
    local a = {}
    for i = 1, 5000000 do
        a[i] = "a"
    end

    ngx.update_time()
    local t2 =os.clock ()
    ngx.log(ngx.ERR, "local_demo cost time ========== " .. t2 - t1)
end

local_demo cost time ========== 0.2


local local_demo2
local_demo2 = function()
    local t1 =os.clock ()

    local a = {}
    for i = 1, 5000000 do
        table.insert(a, "a")
    end

    ngx.update_time()
    local t2 =os.clock ()
    ngx.log(ngx.ERR, "local_demo2 cost time ========== " .. t2 - t1)
end

local_demo2(): local_demo2 cost time ========== 0.68

4. table的循环效率对比
  • table的循环效率 反序数值for循环 > 正序数值for循环 > ipairs > pairs
local tab = {}
for i = 1, 20000000 do
	tab[#tab + 1] = true
end
 
local time1 = os.clock()
for i = 1, #tab do
	tab[i] = true
end
print('正序数值for循环====================', os.clock() - time1)
 
local time2 = os.clock()
for i = #tab, 1, -1 do
	tab[i] = true
end
print('反序数值for循环====================', os.clock() - time2)
 
local time3 = os.clock()
for k, v in ipairs(tab) do
	v = true
end
print('ipairs====================', os.clock() - time3)
 
local time4 = os.clock()
for k, v in pairs(tab) do
	v = true
end
print('pairs====================', os.clock() - time4)

对比结果:
正序数值for循环  0.07
反序数值for循环  0.06
ipairs           0.11
pairs            0.39



5. 减少函数调用可以很大提高效率,尽量避免把函数作为参数使用
  • 多一层函数的调用导致效率下降10倍
local local_demo
local_demo = function(tab)
    local t1 =os.clock ()

    for k, v in ipairs(tab) do
        local a = 1 * 2 + 1 / 2 - 1 + 2
    end

    ngx.update_time()
    local t2 =os.clock ()
    ngx.log(ngx.ERR, "local_demo cost time ========== " .. t2 - t1)
end

local_demo(): local_demo cost time ========== 0.09


local func = function (a, b)
    a = a * b + a / b - a + b
    return a
end
local local_demo2
local_demo2 = function(tab)
    local t1 =os.clock ()

    for k, v in ipairs(tab) do
        local a = func(1, 2)
    end

    ngx.update_time()
    local t2 =os.clock ()
    ngx.log(ngx.ERR, "local_demo2 cost time ========== " .. t2 - t1)
end

local_demo2(): local_demo2 cost time ========== 0.1

6. 在长字符串拼接中 string.format比…效率低很多
  • string.format 调用的是c函数str_format, … 是一个操作符
local local_demo
local_demo = function()
    local t1 =os.clock ()

    for i = 1, 100000000 do
        local str = "monkey" .. "," .. "monkey" .. "," .. "monkey"
    end

    ngx.update_time()
    local t2 =os.clock ()
    ngx.log(ngx.ERR, "local_demo cost time ========== " .. t2 - t1)
end

local_demo(): local_demo cost time ========== 0.03


local local_demo2
local_demo2 = function()
    local t1 =os.clock ()

    for i = 1, 100000000 do
        local str = string.format("%s,%s,%s", "monkey","monkey","monkey")
    end

    ngx.update_time()
    local t2 =os.clock ()
    ngx.log(ngx.ERR, "local_demo2 cost time ========== " .. t2 - t1)
end

local_demo2(): local_demo2 cost time ========== 0.05

使用通信框架 motan-openresty

  • https://github.com/weibocom/motan-openresty
  • 魔毯序列化有问题需要替换 https://github.com/lion2luo/motan-openresty-native

相关内容

    暂无相关文章