基于Openresty开发的应用路由性能调优思路

1406 views
Skip to first unread message

Guanglin Lv

unread,
Mar 24, 2016, 11:17:06 PM3/24/16
to openresty
Hi,

    基于openresty开发了一个应用路由模块,大致工作流程如下:

     1. 根据请求,通过REDIS查询路由信息,首次会将json解码后的结果保存在lrucache中
     2. 负载均衡出一台后端服务器
     3. 通过ngx.var AIP将一些信息传递,通过ngx.exec API内部跳转到@router_proxy location
     4. @router_proxy location中通过access_by_lua对后端服务器进行域名解析。通过proxy_pass转发到upstream
     5. upstream 中通过balancer_by_lua.set_current_peer设置
     6. @router_proxy location中通过header_filter_by_lua进行时延统计

    现准备测试其性能,使用的wrk(https://github.com/wg/wrk),与直接使用nginx的转发对比;

    只测试了单worker的对比情况:

    经过应用路由模块wrk压测结果
  
     
[17:47 root@aufs:~/workspace/wrk/wrk-4.0.1] # ./wrk -t12 -c100 -d30s http://9.91.39.77/scm/test
Running 30s test @ http://9.91.39.77/scm/test
  12 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    18.91ms    2.24ms  67.62ms   95.12%
    Req/Sec   424.97     40.53   484.00     66.94%
  152409 requests in 30.03s, 75.73MB read
Requests/sec:   5075.17
Transfer/sec:      2.52MB


  直接Nginx转发wrk压测结果

  
[18:18 root@aufs:~/workspace/wrk/wrk-4.0.1] # ./wrk -t12 -c100 -d30s http://9.91.39.77:9010/scm/test
Running 30s test @ http://9.91.39.77:9010/scm/test
  12 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.88ms    1.13ms  22.50ms   76.44%
    Req/Sec     1.65k   208.15     2.39k    68.78%
  590453 requests in 30.02s, 257.60MB read
Requests/sec:  19670.87
Transfer/sec:      8.58MB


   以上测试结果和使用ngx-rps.sxx看到的基本一致,而且CPU均已打满;

   另外值得说明的是,整个测试过程中只有第一次的路由信息需要从REDIS获取,后续都可以从lrucache命中。


   从对比测试结果,可以看到相比原生的Nginx转发差距非常大,想请教这是否合理呢?有啥具体的调优的思路呢?或者我应该最先从哪个方向进行分析?

  
   使用lj-lua-stacks.sxx看了下lua面的cpu:

    额,原谅我暂时传不了图片。
  

Thanks.

DeJiang Zhu

unread,
Mar 24, 2016, 11:35:54 PM3/24/16
to open...@googlegroups.com
Hello,

在 2016年3月25日 上午11:17,Guanglin Lv <guang...@gmail.com>写道:
Hi,

    基于openresty开发了一个应用路由模块,大致工作流程如下:

     1. 根据请求,通过REDIS查询路由信息,首次会将json解码后的结果保存在lrucache中
     2. 负载均衡出一台后端服务器
     3. 通过ngx.var AIP将一些信息传递,通过ngx.exec API内部跳转到@router_proxy location

不太明白为什么要有这次跳转,直接 proxy_pass 不行?
 
     4. @router_proxy location中通过access_by_lua对后端服务器进行域名解析。通过proxy_pass转发到upstream
     5. upstream 中通过balancer_by_lua.set_current_peer设置
     6. @router_proxy location中通过header_filter_by_lua进行时延统计

如果可以的话,可以把域名统计和时延统计统一放入 log_by_lua 这样可以省点 cpu
多一个 lua hook 肯定会多一点 cpu
 

   从对比测试结果,可以看到相比原生的Nginx转发差距非常大,想请教这是否合理呢?

几倍的差距应当是不合理的
 
有啥具体的调优的思路呢?或者我应该最先从哪个方向进行分析?
  
   使用lj-lua-stacks.sxx看了下lua面的cpu: 

    额,原谅我暂时传不了图片。

好吧,关键的信息木有了
sample-bt 看下 C 层面的火焰图也是很有意义的 

  

Thanks.

--
--
邮件来自列表“openresty”,专用于技术讨论!
订阅: 请发空白邮件到 openresty...@googlegroups.com
发言: 请发邮件到 open...@googlegroups.com
退订: 请发邮件至 openresty+...@googlegroups.com
归档: http://groups.google.com/group/openresty
官网: http://openresty.org/
仓库: https://github.com/agentzh/ngx_openresty
教程: http://openresty.org/download/agentzh-nginx-tutorials-zhcn.html

Guanglin Lv

unread,
Mar 25, 2016, 6:37:47 AM3/25/16
to openresty

搞了一下午,也没找到办法把火焰图传出来,原来我的世界吧。不过lua层面明显的ngx_http_lua_var_set/get这两个,业务层的反而没有;

A1. 因为我这里会几个不同的出口,缺省是@router_proxy,所以用ngx.exec做了次跳转。另外我改成不跳转直接proxy_pass性能没多大变化;
A2. header_filter_by_lua 里还有种cookie的逻辑,当然测试时该功能是关闭的,这里是响应的打点,最终结果是存在ngx.var,然后在access_log里直接体现了的;


另外,我伪造我的应用路由流程,写了一个简单基于lua的转发逻辑,upstream地址这些都是硬编码的;同样wrk压测了下,RPS也只有9000。

和原生的Nginx转发还是有一倍多的差距啊。

location = /router_proxy {
    internal;
    proxy_set_header Host $router_forward_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $http_x_forwarded_for;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Connection "";
    proxy_set_header X-Forwarded-Host $router_forward_http_host;
    proxy_http_version 1.1;
    
    proxy_pass http://${router_backend}${router_forward_uri};
}


local math_floor    = math.floor
local ngx_var       = ngx.var

local router_ctx = {}

-- we run the rewrite and access phase here
-- it's more lower cpu usage than using rewrite_by_lua_* and access_by_lua_*
--
require('router_rewrite_phase').rewrite_phase(router_ctx)
require('router_access_phase').access_phase(router_ctx)

local router_config = require('router_config')

local precision = function(double_num)
    return math_floor(double_num*10000)/10000
end

-- use nginx proxy_pass to bypass request
local function get_proxy_endpoint(app_type,request_mode)
    local route_type = app_type or "was" -- default route type is was
    if request_mode == "upgrade" then -- upgrade is the special case,ignore route_type
        return "/router_upgrade"
    end
    if route_type == "was" then
        return "/router_was"
    end
    -- others use the normal proxy output
    return "/router_proxy"
end

local router_request_mode = "proxy_pass"

local t_router_upstream_config = router_config.upstream
local ngx_upstream = require('lib.router_ngx_upstream')(t_router_upstream_config)
local t_used_upstream = {name = "testpro",list = {"test_pro_9.91.39.111_9075","test_pro_9.91.39.111_9076"},type = "pro"}
local t_upstream_server_infos = {
    ["test_pro_9.91.39.111_9075"] = {servers = {hostname = "9.91.39.111",port = 9075}},
    ["test_pro_9.91.39.111_9076"] = {servers = {hostname = "9.91.39.111",port = 9076}},
}
local used_upstream_name,used_upstream_idx,err = ngx_upstream:choose(t_used_upstream,nil)
local real_forward_host = ngx_var.host
local real_forward_http_host = ngx_var.http_host
local real_forward_uri = ngx_var.request_uri

if router_config.upstream.balancer.enable then
    -- set ngx.var for balancer_by_lua
    local router_backend = router_config.upstream.balancer.upstream
    local balancer_host = t_upstream_server_infos[used_upstream_name]["servers"]["hostname"]
    local balancer_prepare = require "router_balancer_prepare"
    ngx_var.router_balancer_host = balancer_prepare.prepare(router_backend,balancer_host)
    ngx_var.router_balancer_port = tonumber(t_upstream_server_infos[used_upstream_name]["servers"]["port"])
    ngx_var.router_backend = router_backend
else
    ngx_var.router_backend = used_upstream_name
end

ngx_var.router_sticky_key = nil
ngx_var.router_plant_key = nil
ngx_var.router_plant_val = nil
ngx_var.router_status_header = nil

-- save forward host and path
ngx_var.router_forward_host = real_forward_host
ngx_var.router_forward_http_host = real_forward_http_host
ngx_var.router_forward_uri = real_forward_uri

-- save router_sticky_config into nginx variables temporarily
if not router_sticky_config then router_sticky_config = {} end
ngx_var.router_sticky_type = router_sticky_config.type
ngx_var.router_sticky_create = router_sticky_config.create
ngx_var.router_sticky_lookup = router_sticky_config.lookup
ngx_var.router_sticky_expires = router_sticky_config.expires

local ups_start_time = ngx.now()
local ngx_elapsed_time = ups_start_time - ngx.req.start_time()

-- process time from accept to proxy
ngx_var.router_request_time = precision(ngx_elapsed_time)
ngx_var.router_ups_start_time =  tostring(ups_start_time)

local proxy_point = get_proxy_endpoint("tomcat",router_request_mode)
return ngx.exec(proxy_point)


所以,有些不明白是什么原因导致性能会降低这么多呢,或者说在用lua在openresty上开发时需要注意啥,使用什么样的方式来防止性能骤降呢?

我的openresty编译参数:

nginx version: openresty/1.9.7.4
built by gcc 4.1.2 20080704 (Red Hat 4.1.2-55)
built with OpenSSL 1.0.2g  1 Mar 2016
TLS SNI support enabled
configure arguments: --prefix=/opt/router/openresty/nginx --with-cc-opt=-O2 --add-module=../ngx_devel_kit-0.2.19 --add-module=../echo-nginx-module-0.58 --add-module=../xss-nginx-module-0.05 --add-module=../ngx_coolkit-0.2rc3 --add-module=../set-misc-nginx-module-0.30 --add-module=../form-input-nginx-module-0.11 --add-module=../encrypted-session-nginx-module-0.04 --add-module=../srcache-nginx-module-0.30 --add-module=../ngx_lua-0.10.2 --add-module=../ngx_lua_upstream-0.05 --add-module=../headers-more-nginx-module-0.29 --add-module=../array-var-nginx-module-0.05 --add-module=../memc-nginx-module-0.16 --add-module=../redis2-nginx-module-0.12 --add-module=../redis-nginx-module-0.3.7 --add-module=../rds-json-nginx-module-0.14 --add-module=../rds-csv-nginx-module-0.07 --with-ld-opt=-Wl,-rpath,/opt/router/openresty/luajit/lib --with-pcre=/mnt/compile/nginx/openresty-1.9.7.4/../pcre-8.36 --with-zlib=/mnt/compile/nginx/openresty-1.9.7.4/../zlib-1.2.8 --with-openssl=/mnt/compile/nginx/openresty-1.9.7.4/../openssl-1.0.2g --with-pcre-jit --add-module=/mnt/compile/nginx/openresty-1.9.7.4/../ngx_http_dyups_module-0.2.9+ --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --with-openssl-opt=enable-tlsext



在 2016年3月25日星期五 UTC+8上午11:35:54,doujiang写道:

Yichun Zhang (agentzh)

unread,
Mar 27, 2016, 3:27:14 PM3/27/16
to openresty
Hello!

2016-03-25 3:37 GMT-07:00 Guanglin Lv:
>
> 搞了一下午,也没找到办法把火焰图传出来,原来我的世界吧。不过lua层面明显的ngx_http_lua_var_set/get这两个,业务层的反而没有;
>

我看不到 on-CPU 和 off-CPU 火焰图,没法帮你分析问题哈。咱不能靠猜哈。

值得一提的是,你的 Lua 代码里面频繁使用 ngx.var 会产生性能问题。这个 API 的开销很大,应当尽量减少调用次数。这在官方文档里面也有交待:

"This API requires a relatively expensive metamethod call and it is
recommended to avoid using it on hot code paths."

https://github.com/openresty/lua-nginx-module#ngxvarvariable

Regards,
-agentzh

Guanglin Lv

unread,
Mar 27, 2016, 9:43:09 PM3/27/16
to openresty

嗯,是的。我正在尝试把ngx.var调用替换掉,业务流程上的信息传递是打算用lrucache来做。

时间点统计的信息,希望是通过log_format定义的格式直接被记录在access_log里的,所以暂时没办法;


待我优化后再测试下性能;

感谢~


在 2016年3月28日星期一 UTC+8上午3:27:14,agentzh写道:

Guanglin Lv

unread,
Apr 13, 2016, 9:11:35 PM4/13/16
to openresty

近期做了几点优化:

1. 统一一个location入口,直接proxy_pass走,避免调用ngx.exec
2. 优化ngx.var API的调用,上下文信息传递使用ngx.ctx
3. 使用lrucache来缓存结构化的路由表,避免json的解码操作

wrk压测结果

./wrk -t12 -c100 -d30s http://9.91.39.77/scm/test`

Running 30s test @ http://9.91.39.77/scm/test
  12 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    14.48ms    3.29ms 100.49ms   89.32%
    Req/Sec   557.23     72.63     1.04k    66.20%
  201007 requests in 30.07s, 99.85MB read
Requests/sec:   6683.57
Transfer/sec:      3.32MB

相比原生的nginx转发,还是差了好多:

./wrk -t12 -c100 -d30s http://9.91.39.77:9010/scm/test`

  12 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     5.79ms    1.94ms  51.76ms   85.56%
    Req/Sec     1.40k   220.43     1.94k    69.14%
  502658 requests in 30.02s, 219.30MB read
Requests/sec:  16741.50
Transfer/sec:      7.30MB


C/Lua层面的火焰图如附件所示。

大家帮忙分析下啊,看是哪里的瓶颈啊;

 
lua-land-cpu-flame-graph-1-worker.svg
c-land-cpu-flame-graph-1-worker.svg

DeJiang Zhu (doujiang)

unread,
Apr 14, 2016, 6:07:57 AM4/14/16
to open...@googlegroups.com
Hello,

1. 你的 access 阶段消耗好大,从火焰图看,在频繁的操作 shdict,table;按说你这个规则在 lrucache 命中应该很高才是,不应该有大量的操作 table(而且,你还有操作全局table:ngx_http_lua_create_new_globals_table)我感觉 access 阶段在火焰图应该降到 10% 以下才合理
2. header_filter 阶段消耗也不小,是否也可以优化一下呢
3. 还有一个通用一点的优化,log 可以开始 buffer,看起来 log 阶段耗时也不少

-- 
-- 
邮件来自列表“openresty”,专用于技术讨论!
订阅: 请发空白邮件到 openresty...@googlegroups.com
发言: 请发邮件到 open...@googlegroups.com
退订: 请发邮件至 openresty+...@googlegroups.com
归档: http://groups.google.com/group/openresty
官网: http://openresty.org/
仓库: https://github.com/agentzh/ngx_openresty
教程: http://openresty.org/download/agentzh-nginx-tutorials-zhcn.html
<lua-land-cpu-flame-graph-1-worker.svg><c-land-cpu-flame-graph-1-worker.svg>

Guanglin Lv

unread,
Apr 14, 2016, 8:38:19 PM4/14/16
to openresty
多谢doujiang的分析,不过我还是挺困惑的;

1、主流程就在access和header阶段,log阶段被我注释掉了;
2、严格来说每次请求会操作两次shdict,应该不算是频繁吧
3、我的lrucache应该是100%命中,我测试之前是先缓存好的,过期时间是1分钟。所以30s的压测时间内是都要命中的;
4、请求的access处理过程中,会生成一些临时表,最大的就是处理上下文信息,这个表会通过ngx.ctx传递到header、balancer阶段使用;
5、全局表操作应该没有的才对,除了access/header/balancer都是模块的调用,ngx_http_lua_create_new_globals_table这个是在哪看到的呀。
6、log阶段我没有调用的,而且整个过程也没记调试日志,只有access日志;

我再仔细分析下优化点,多谢啦。


在 2016年4月14日星期四 UTC+8下午6:07:57,doujiang写道:

DeJiang Zhu (doujiang)

unread,
Apr 14, 2016, 10:58:23 PM4/14/16
to open...@googlegroups.com
Hello

在 2016年4月15日,上午8:38,Guanglin Lv <anym...@qq.com> 写道:

多谢doujiang的分析,不过我还是挺困惑的;

1、主流程就在access和header阶段,log阶段被我注释掉了;

所以我说 log 阶段是通用的优化,与 ngx_lua 无关
抱歉,我上篇回帖有错别字,开始 -> 开启,开启 buffer 应该会更快,如果你的业务接受的话(我猜你现在应该没开,或者设置的略小)

2、严格来说每次请求会操作两次shdict,应该不算是频繁吧

我之前理解的是,应该应该不需要每请求操作,如果你需要,两次不算多
火焰图其实是帮助我们了解实际运行情况,如果实际跟我们理想的有区别,那就需要优化了
如果你的业务需要两次,那么也合理

3、我的lrucache应该是100%命中,我测试之前是先缓存好的,过期时间是1分钟。所以30s的压测时间内是都要命中的;
4、请求的access处理过程中,会生成一些临时表,最大的就是处理上下文信息,这个表会通过ngx.ctx传递到header、balancer阶段使用;

好吧,这样看来你的业务逻辑也还有点复杂
那么你可以对照 Lua land 的火焰图,看看是不是有出乎你想象的

5、全局表操作应该没有的才对,除了access/header/balancer都是模块的调用,ngx_http_lua_create_new_globals_table这个是在哪看到的呀。

确实占比很少,不好找,不过你可以在 vi 里打开 svg,搜索

DeJiang Zhu (doujiang)

unread,
Apr 14, 2016, 10:58:25 PM4/14/16
to open...@googlegroups.com
Hello

在 2016年4月15日,上午8:38,Guanglin Lv <anym...@qq.com> 写道:

多谢doujiang的分析,不过我还是挺困惑的;

1、主流程就在access和header阶段,log阶段被我注释掉了;
所以我说 log 阶段是通用的优化,与 ngx_lua 无关
抱歉,我上篇回帖有错别字,开始 -> 开启,开启 buffer 应该会更快,如果你的业务接受的话(我猜你现在应该没开,或者设置的略小)

2、严格来说每次请求会操作两次shdict,应该不算是频繁吧

我之前理解的是,应该应该不需要每请求操作,如果你需要,两次不算多
火焰图其实是帮助我们了解实际运行情况,如果实际跟我们理想的有区别,那就需要优化了
如果你的业务需要两次,那么也合理
3、我的lrucache应该是100%命中,我测试之前是先缓存好的,过期时间是1分钟。所以30s的压测时间内是都要命中的;
4、请求的access处理过程中,会生成一些临时表,最大的就是处理上下文信息,这个表会通过ngx.ctx传递到header、balancer阶段使用;

好吧,这样看来你的业务逻辑也还有点复杂
那么你可以对照 Lua land 的火焰图,看看是不是有出乎你想象的

5、全局表操作应该没有的才对,除了access/header/balancer都是模块的调用,ngx_http_lua_create_new_globals_table这个是在哪看到的呀。

确实占比很少,不好找,不过你可以在 vi 里打开 svg,搜索

Guanglin Lv

unread,
Apr 15, 2016, 3:59:33 AM4/15/16
to openresty


在 2016年4月15日星期五 UTC+8上午10:58:23,doujiang写道:
Hello

在 2016年4月15日,上午8:38,Guanglin Lv <anym...@qq.com> 写道:

多谢doujiang的分析,不过我还是挺困惑的;

1、主流程就在access和header阶段,log阶段被我注释掉了;

所以我说 log 阶段是通用的优化,与 ngx_lua 无关
抱歉,我上篇回帖有错别字,开始 -> 开启,开启 buffer 应该会更快,如果你的业务接受的话(我猜你现在应该没开,或者设置的略小)


  恩,我的确是没有设置,但原生转发时也没有设置的。

2、严格来说每次请求会操作两次shdict,应该不算是频繁吧

我之前理解的是,应该应该不需要每请求操作,如果你需要,两次不算多
火焰图其实是帮助我们了解实际运行情况,如果实际跟我们理想的有区别,那就需要优化了
如果你的业务需要两次,那么也合理

 
是的。

3、我的lrucache应该是100%命中,我测试之前是先缓存好的,过期时间是1分钟。所以30s的压测时间内是都要命中的;
4、请求的access处理过程中,会生成一些临时表,最大的就是处理上下文信息,这个表会通过ngx.ctx传递到header、balancer阶段使用;

好吧,这样看来你的业务逻辑也还有点复杂
那么你可以对照 Lua land 的火焰图,看看是不是有出乎你想象的


  哎,是觉得有点小复杂,因为有很多功能点,虽然这些功能点在压测时是通过开关关闭的,不会走到。
 
  access_by_lua:

               1、rewrite改写
               2、access流控
               3、查询路由,lrucache命中
               4、检查粘性
               5、转发改写
               6、负载均衡
               
balancer_by_lua:
              1、第一次,set_more_tries set_current_peer
              2、下一次,rebalance set_current_peer

header_filter_by_lua:
             1、设置粘性
             2、设置http cache
             3、计算响应时延




5、全局表操作应该没有的才对,除了access/header/balancer都是模块的调用,ngx_http_lua_create_new_globals_table这个是在哪看到的呀。

确实占比很少,不好找,不过你可以在 vi 里打开 svg,搜索


  哈,搜索了,是在c火焰图里,有三处(ngx_http_lua_access_by_chunk/ngx_http_lua_balancer_by_chunk/ngx_http_lua_header_filter_by_chunk)。貌似ngx_lua调用的。
 


Yichun Zhang (agentzh)

unread,
Apr 15, 2016, 3:40:03 PM4/15/16
to openresty
Hello!

2016-04-13 18:11 GMT-07:00 Guanglin Lv:
> 1. 统一一个location入口,直接proxy_pass走,避免调用ngx.exec
> 2. 优化ngx.var API的调用,上下文信息传递使用ngx.ctx

ngx.ctx 也应尽量减少使用,因为 ngx.ctx 也涉及元表操作。

>
> C/Lua层面的火焰图如附件所示。
>

从你的 C 级别火焰图上看,有如下的优化方法:

1. 尽量多地让你的热 Lua 代码路径被 JIT 编译。具体做法是:尽量使用 lua-resty-core 库,避免在热代码路径上使用
LuaJIT 的 NYI 原语 [1]. 从火焰图上看,你的绝大部分 Lua 代码都是被 LuaJIT 解释执行的,并没有被 JIT
编译(显示为那些 lj_BC_xxx 的函数帧)。

2. 尽量使用 table.new() 原语来预分配你的 Lua table,以避免 Lua table 的自动生长(火焰图中对应
rehashtab 和 lj_tab_resize 帧)。

3. 启用 luajit 的 dual number 模式,以避免昂贵的浮点数到字符串的转换(火焰图中对应 lj_strfmt_num
帧)。具体做法是使用下面的命令重新编译 OpenResty:

./configure --with-luajit-xcflags="-DLUAJIT_NUMMODE=2"

值得一提的是,最新的 LuaJIT 2.1 包含了一个优化过的浮点数字符串化的实现,而并没有再使用性能较差的 glibc
里面的实现。不过最新的 OpenResty 还没有包含这么新的 LuaJIT.

做到这些之后,你的 Lua 部分的损耗应该可以降低一半,甚至更多。

[1] http://wiki.luajit.org/NYI

Guanglin Lv

unread,
Apr 29, 2016, 3:04:20 AM4/29/16
to openresty


在 2016年4月16日星期六 UTC+8上午3:40:03,agentzh写道:
Hello!

2016-04-13 18:11 GMT-07:00 Guanglin Lv:
> 1. 统一一个location入口,直接proxy_pass走,避免调用ngx.exec
> 2. 优化ngx.var API的调用,上下文信息传递使用ngx.ctx

ngx.ctx 也应尽量减少使用,因为 ngx.ctx 也涉及元表操作。


  嗯,用的不多,不过我的用法是ngx.ctx.my_ctx是一个比较大的表,access阶段填充,后续balancer和header_filter阶段使用。

  不知道是否合理哈;
 
>
> C/Lua层面的火焰图如附件所示。
>

从你的 C 级别火焰图上看,有如下的优化方法:

1. 尽量多地让你的热 Lua 代码路径被 JIT 编译。具体做法是:尽量使用 lua-resty-core 库,避免在热代码路径上使用
LuaJIT 的 NYI 原语 [1]. 从火焰图上看,你的绝大部分 Lua 代码都是被 LuaJIT 解释执行的,并没有被 JIT
编译(显示为那些 lj_BC_xxx 的函数帧)。


  受教了,以前完全没考虑过这点,最近详细了解了下NYI,并做了下优化,几乎部分逻辑都重构了;(⊙﹏⊙)b
 
2. 尽量使用 table.new() 原语来预分配你的 Lua table,以避免 Lua table 的自动生长(火焰图中对应
 rehashtab 和 lj_tab_resize 帧)。

  嗯,已优化了; 有个疑问,loca t= {"a","b","c'}这种定义时就赋值好的表,是否也必要优化呢;  
 

3. 启用 luajit 的 dual number 模式,以避免昂贵的浮点数到字符串的转换(火焰图中对应 lj_strfmt_num
帧)。具体做法是使用下面的命令重新编译 OpenResty:

    ./configure --with-luajit-xcflags="-DLUAJIT_NUMMODE=2"

值得一提的是,最新的 LuaJIT 2.1 包含了一个优化过的浮点数字符串化的实现,而并没有再使用性能较差的 glibc
里面的实现。不过最新的 OpenResty 还没有包含这么新的 LuaJIT.


  目前我想到的浮点数到字符串转换的地方只有,我在header_filter阶段设置了两个处理耗时的响应头。
 
做到这些之后,你的 Lua 部分的损耗应该可以降低一半,甚至更多。

[1] http://wiki.luajit.org/NYI


  目前尽可能优化了NYI、table后,性能上升了1千左右,达到每秒7600多,火焰图如附件。


  另外请教一下,为何在init_by_lua阶段调用了require "resty.core"后性能会好非常多?而且在lua层面的火焰图里也几乎看不到业务层代码调用栈了。

  反之,性能会下降很多,而且不稳定。lua层面火焰图有几乎全部的业务代码调用栈。

   这是啥原理啊?是不是只在init阶段这样就好了,还是每个阶段都需要require "resty.core"

  Thanks.
   
 
lua_land_flame_graph.svg
c_land_flame_graph.svg

Yichun Zhang (agentzh)

unread,
Apr 29, 2016, 6:17:16 PM4/29/16
to openresty
Hello!

2016-04-29 0:04 GMT-07:00 Guanglin Lv:
> 另外请教一下,为何在init_by_lua阶段调用了require
> "resty.core"后性能会好非常多?而且在lua层面的火焰图里也几乎看不到业务层代码调用栈了。
>
> 反之,性能会下降很多,而且不稳定。lua层面火焰图有几乎全部的业务代码调用栈。
>
> 这是啥原理啊?是不是只在init阶段这样就好了,还是每个阶段都需要require "resty.core"
>

在 init_by_lua 阶段使用就好了。这一点在 lua-resty-core 的文档中有示例。

至于为什么 lua-resty-core 会让性能显著提升,它的文档里,以及我前面的邮件里都说的比较清楚了……

Regards,
-agentzh
Reply all
Reply to author
Forward
0 new messages