nginx吐数据的时候,添加定时器,打印间隔时间内的数据量信息

233 views
Skip to first unread message

学不可以已

unread,
Dec 9, 2014, 4:59:23 AM12/9/14
to open...@googlegroups.com
hello,各位

      想写一个模块,在nginx一边吐数据的时候,一边打印间隔时间内的数据量信息,大概思路是:
      在请求NGX_HTTP_POST_READ_PHASE阶段通过ngx_add_timer添加个定时器事件,比如1秒。1秒打印一次r-connection->sent数据。

      我的问题是:
       1.但是为什么在吐数据的时候,这个定时器是不工作的,或者说被阻塞了呢。
       2.各位有什么好的实现方式或者建议,或者帮我指正下思路,新手所以不确定原因出在什么地方,谢谢~

Yichun Zhang (agentzh)

unread,
Dec 9, 2014, 2:22:16 PM12/9/14
to openresty
Hello!

2014-12-09 1:59 GMT-08:00 学不可以已:
> 想写一个模块,在nginx一边吐数据的时候,一边打印间隔时间内的数据量信息,大概思路是:
>
> 在请求NGX_HTTP_POST_READ_PHASE阶段通过ngx_add_timer添加个定时器事件,比如1秒。1秒打印一次r-connection->sent数据。
>

貌似这种需求通过 systemtap 外部工具进行采样统计更直接、更简单也更高效。

> 我的问题是:
> 1.但是为什么在吐数据的时候,这个定时器是不工作的,或者说被阻塞了呢。

你 100% 确定你的请求在吐数据时需要很多秒这么久的时间么?如果真是如此,你一个请求得输出多大的数据量呀(考虑到系统发送缓冲区的默认大小)。

> 2.各位有什么好的实现方式或者建议,或者帮我指正下思路,新手所以不确定原因出在什么地方,谢谢~
>

见上。

Regards,
-agentzh

学不可以已

unread,
Dec 9, 2014, 9:57:30 PM12/9/14
to open...@googlegroups.com
春哥,
     非常谢谢你的回复,我先尝试用你推荐的工具system工具跟踪一下nginx 函数执行的流程。排查一下原因先。
     1、我是想着,如果我是一个非常大的文件,比如1G,在网络状况一般的情况下,吐数据的时间大概也会花个几秒甚至几十秒,那么我说的方案(在请求NGX_HTTP_POST_READ_PHASE阶段通过ngx_add_timer添加个定时器事件,比如1秒。1秒打印一次r-connection->sent数据。)是否可行呢?可是我尝试的时候,比如curl下载这个1G的文件,发现下载数据完后,定时器才开始工作的感觉。貌似定时器事件没有在NGX_HTTP_POST_READ_PHASE这个阶段的时候启动起来。感觉是在NGX_HTTP_LOG_PHASE阶段后才开始工作的。是不是在吐数据的时候,定时器事件被阻塞了?
     2、如果通过nginx模块的方法来实现我这个需求,你的建议是怎么实现好呢?谢谢~




在 2014年12月10日星期三UTC+8上午3时22分16秒,agentzh写道:

mem phis

unread,
Dec 10, 2014, 4:43:59 AM12/10/14
to open...@googlegroups.com
对指定的location直接限速不可以?

你是怕下载的太快,还是什么??

学不可以已

unread,
Dec 10, 2014, 4:48:39 AM12/10/14
to open...@googlegroups.com
你好,我不是想做这方面的限制,只是想拿到单位时间内吐的数据量,然后根据此数据做一些图表。

在 2014年12月10日星期三UTC+8下午5时43分59秒,mem phis写道:

学不可以已

unread,
Dec 10, 2014, 4:54:05 AM12/10/14
to open...@googlegroups.com
春哥,再问个问题哈,我用systemtap跟踪nginx函数执行过程,在打印的信息里有行这样的信息,
ngx_event_expire_timers(sentinel=? node=0x974f970)
ngx_rbtree_delete(tree=0x80c4820 node=0x974f970)
ngx_http_print_log(ev=0x974f95c)
ngx_http_print_log(timer=? ev=0x974f95c)
ngx_rbtree_insert(tree=0x80c4820 node=0x974f970)
ngx_rbtree_insert_timer_value(temp=0x9782e8c node=0x974f970 sentinel=0x80c44bc)
ngx_event_expire_timers(sentinel=? node=0x9782e8c)
ngx_event_expire_timers(sentinel=? node=0x974f970)
...
我想问下,timer=? sentinel=?    问号的地方,是表示我定时器函数什么地方传值传参的时候有错误吗?是这个原因造成我添加的定时器事件阻塞异常了吗?

在 2014年12月10日星期三UTC+8上午10时57分30秒,学不可以已写道:

Yichun Zhang (agentzh)

unread,
Dec 10, 2014, 7:14:50 PM12/10/14
to openresty
Hello!

2014-12-09 18:57 GMT-08:00 学不可以已:
> 1、我是想着,如果我是一个非常大的文件,比如1G,在网络状况一般的情况下,吐数据的时间大概也会花个几秒甚至几十秒,那么我说的方案(在请求NGX_HTTP_POST_READ_PHASE阶段通过ngx_add_timer添加个定时器事件,比如1秒。1秒打印一次r-connection->sent数据。)是否可行呢?

可行。只要你的输出过程不是全阻塞的。只要 nginx 的事件循环有机会运行,你自己的 timer 就有机会 expire.

> 可是我尝试的时候,比如curl下载这个1G的文件,发现下载数据完后,定时器才开始工作的感觉。

由于你没有提供具体的代码和描述,我这里只能猜测了。有几种你犯错的可能:

1. 你错误地复用了 nginx 自己的 ngx_event_t,比如 r->connection->read 或
r->connection->write. 这些 event 上的 timer 会频繁地被改写。

2. 你的输出过程没有使用 non-buffered 模式,比如 ngx_proxy 模块有 non-buffered 模式(不过默认是全
buffered 模式)。

3. 你的 timer 应该在你自己的 requst pool cleanup 回调里进行回收释放。注意,不应在 request cleanup 里回收。

> 2、如果通过nginx模块的方法来实现我这个需求,你的建议是怎么实现好呢?谢谢~
>

你自己写 nginx 模块有 N 种犯错的可能性。我上面猜的只是你这里最可能犯的几种。

建议直接使用 ngx_lua 模块的 body_filter_by_lua 指令来作这种统计:

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

你可以在里面检查 ngx.now() 来判断时间。

Regards,
-agentzh

Yichun Zhang (agentzh)

unread,
Dec 10, 2014, 7:18:14 PM12/10/14
to openresty
Hello!

2014-12-10 1:54 GMT-08:00 学不可以已:
> 春哥,再问个问题哈,我用systemtap跟踪nginx函数执行过程,在打印的信息里有行这样的信息,
[...]
> 我想问下,timer=? sentinel=?
> 问号的地方,是表示我定时器函数什么地方传值传参的时候有错误吗?是这个原因造成我添加的定时器事件阻塞异常了吗?
>

这种问题一般是因为 C 编译器生成了错误的或不完整的或无法为 systemtap 工具链所正确理解的调试信息,尤其是对于那些内联了的 C
函数。如果你使用的 gcc 版本足够新的话,会好很多。

Regards,
-agentzh

sulon...@gmail.com

unread,
Dec 11, 2014, 3:50:20 AM12/11/14
to open...@googlegroups.com
你是想要获取发送了多少流量吧,这个在日志阶段可以取到的,你没必要搞这么复杂。
我之前做了个跟你类似的功能,就是把相关业务的流量定时发送到源主机上。
我当时实现了一个hashmap来保持流量,然后定时发送到源主机上。

在 2014年12月9日星期二UTC+8下午5时59分23秒,学不可以已写道:

学不可以已

unread,
Dec 11, 2014, 4:02:26 AM12/11/14
to open...@googlegroups.com
hello,

     我尝试你推荐的直接使用 ngx_lua 模块的 body_filter_by_lua 指令,简单的配置

    lua_shared_dict share_dict 5M;
   http{

        .....

        location / {
        body_filter_by_lua '
                local share_dict = ngx.shared.share_dict
                share_dict:set("sent", ngx.var.body_bytes_sent)
        ';
            root   html;
            index  index.html index.htm;
        }

location = /status {
                content_by_lua '
                local share_dict = ngx.shared.share_dict
                local body_sent = share_dict:get("sent")
                ngx.say(body_sent)
        ';
        }

.....

}

像这样通过location /status 来获取前面匹配到location /的发送的数据量,但是这个获取的应该是最后吐的数据量,如果想定时自动的获取,怎么实现呢?比如在下载文件比较大时,1秒间隔就打印一下已经吐的数据量。谢谢~~

在 2014年12月11日星期四UTC+8上午8时14分50秒,agentzh写道:

jie1...@gmail.com

unread,
Dec 11, 2014, 4:05:53 AM12/11/14
to openresty

你是想要获取发送了多少流量吧,这个在日志阶段可以取到的,你没必要搞这么复杂。
日志阶段得到的大小,跟实时的流量还是有些区别的吧。有一定的滞后性。比如用户下载一个1G的文件,持续1个小时,在日志阶段,这个流量是算在最后这一秒的。



学不可以已

unread,
Dec 11, 2014, 4:19:35 AM12/11/14
to open...@googlegroups.com
我是想获取下载这个文件,每一秒的发送量是多少?如果做最后流量统计,这个在日志里面有bytes_sent这个字段,是可以实现的。

在 2014年12月11日星期四UTC+8下午5时05分53秒,jie1...@gmail.com写道:

学不可以已

unread,
Dec 11, 2014, 4:28:50 AM12/11/14
to open...@googlegroups.com
访问日志是请求最后发送完数据的信息,那么不是实时的,如果实时获取每秒的吞吐量呢?

在 2014年12月11日星期四UTC+8下午5时05分53秒,jie1...@gmail.com写道:

学不可以已

unread,
Dec 11, 2014, 4:45:33 AM12/11/14
to open...@googlegroups.com
hello,
     好像,ngx.timer.at(),这个调用可以生成一个定时器,我再使用这个试一下,谢谢

在 2014年12月11日星期四UTC+8下午5时02分26秒,学不可以已写道:

DeJiang Zhu

unread,
Dec 11, 2014, 11:12:37 AM12/11/14
to open...@googlegroups.com
http://wiki.nginx.org/HttpLuaModule#body_filter_by_lua

仔细看看这个文档,body_filter_by_lua 本身就是做流式处理用的

每个流式输出的内容块,都会经过 body_filter_by_lua 处理,在lua里就是 ngx.arg[1]

你可以获取 ngx.arg[1] 的长度,就是每个内容块的长度


body_filter_by_lua '
                local share_dict = ngx.shared.share_dict

                share_dict:set("sent" .. ngx.time(), #ngx.arg[1])
        ';

这样就记录 body_filter 每一秒输出的大小


--
--
邮件来自列表“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

学不可以已

unread,
Dec 11, 2014, 10:48:44 PM12/11/14
to open...@googlegroups.com
doujiang,
      
      你好,我像这样配置
        location / {
        body_filter_by_lua '
                local share_dict = ngx.shared.share_dict
                share_dict:set("sent" .. ngx.time(), string.len(ngx.arg[1]))
        ';

            root   html;
            index  index.html index.htm;
        }


        location = /test {
                content_by_lua '
                local share_dict = ngx.shared.share_dict
                local keys = share_dict:get_keys(0)
                local len = table.getn(keys)
                for i=1,len do
                ngx.say("key:" .. keys[i] .. "value: " .. share_dict:get(keys[i]) .. "\\n")
                end
        ';
        }

这样通过location / 去往共享内存里面写数据,通过location /test来获取数据,但是ngx.arg[1]获取的是请求中的某一块的数据吧,那么如果一个请求,会使用多次body_filter_by_lua,对吧,相当于多块数据,那么共享内存里就有多个块的记录,是不是应该是共享内存里面多块数据加起来的数据长度就是请求的这个文件 的长度呢,但是我这样测试发现从/test里面读出来的数据相加和原始文件长度相差很大。请问什么地方有问题或者有什么建议,谢谢~~~




在 2014年12月12日星期五UTC+8上午12时12分37秒,doujiang写道:

学不可以已

unread,
Dec 12, 2014, 4:00:06 AM12/12/14
to open...@googlegroups.com
Agentzh,
        hello,
         想问下你,body_filter_by_lua这段对应的是指在body_filter的时候,将吐给用户的数据分成若干块,比如n块,每一块都经过这个body_filter处理,是不是说我一个请求,如果在经过body_filter_by_lua的时候,会被执行n次。这n块数据的和就是吐给用户的数据量?谢谢~

在 2014年12月12日星期五UTC+8上午11时48分44秒,学不可以已写道:

DeJiang Zhu

unread,
Dec 12, 2014, 4:07:51 AM12/12/14
to open...@googlegroups.com
在 2014年12月12日 上午11:48,学不可以已 <home...@gmail.com>写道:
doujiang,
      
      你好,我像这样配置
        location / {
        body_filter_by_lua '
                local share_dict = ngx.shared.share_dict
                share_dict:set("sent" .. ngx.time(), string.len(ngx.arg[1]))
        ';

            root   html;
            index  index.html index.htm;
        }

sorry, set 用错了, 应该用 incr, 这样得到的是整个 nginx 某一秒的量
 

这样通过location / 去往共享内存里面写数据,通过location /test来获取数据,但是ngx.arg[1]获取的是请求中的某一块的数据吧,那么如果一个请求,会使用多次body_filter_by_lua,对吧,相当于多块数据,那么共享内存里就有多个块的记录,是不是应该是共享内存里面多块数据加起来的数据长度就是请求的这个文件 的长度呢,但是我这样测试发现从/test里面读出来的数据相加和原始文件长度相差很大。请问什么地方有问题或者有什么建议,谢谢~~~


如果你希望定位到某个请求的话,那就得有个请求 id 吧,可以在 access_by_lua 等里面 生成id,放入 ngx.ctx
然后再 body_filter_by_lua 里,根据自己的需求来统计

DeJiang Zhu

unread,
Dec 12, 2014, 4:23:56 AM12/12/14
to open...@googlegroups.com
在 2014年12月12日 下午5:07,DeJiang Zhu <douji...@gmail.com>写道:

sorry, set 用错了, 应该用 incr, 这样得到的是整个 nginx 某一秒的量

sorry,再补充一下,incr 是要自己初始化的,详情请见文档
Reply all
Reply to author
Forward
0 new messages