nginx + lua环境下时间戳更新不及时的问题

发布于 作者 shisongran留下评论

 

先说现象:

local calTimeStart = ngx.now()
-- do something
local calTimeEnd = ngx.now()
ngx.say(calTimeEnd - calTimeStart)

结果输出是0;

解决方案:请在使用时间函数前使用以下函数

ngx.update_time()

代码修改如下:

local calTimeStart = ngx.now()
-- do something
ngx.update_time()
local calTimeEnd = ngx.now()
ngx.say(calTimeEnd - calTimeStart)

这个时候时间就能够正常的输出了时间了。


接下来讲原因:

nginx的lua module的中的ngx.now函数,实际上是调用了ngx中的ngx_timeofday()函数,定义如下:

static int
ngx_http_lua_ngx_now(lua_State *L)
{
    ngx_time_t *tp;
    tp = ngx_timeofday();
    lua_pushnumber(L, (lua_Number) (tp->sec + tp->msec / 1000.0L));
    return 1;
}

ngx_timeofday()在ngx中,并不是一个函数,实际上编译器访问的下面这个变量:

#define ngx_timeofday() (ngx_time_t *) ngx_cached_time

也就是说,实际上我们每次访问ngx_timeofday()函数,是直接读取了ngx_cached_time的指针,并没有调用系统函数获取当前的值。那么如果ngx_cached_time的内容在执行lua代码的两次之前没有被修改过,我们自然拿出来的时间就相同的了,这当然不是我们希望的结果。

那么先看看它什么时候会被更新,一共有两处。
一处是在nginx初始化的时候,nginx直接对这个值进行了赋值:

void
ngx_time_init(void)
{
    ngx_cached_err_log_time.len = sizeof("1970/09/28 12:00:00") - 1;
    ngx_cached_http_time.len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1;
    ngx_cached_http_log_time.len = sizeof("28/Sep/1970:12:00:00 +0600") - 1;
    ngx_cached_http_log_iso8601.len = sizeof("1970-09-28T12:00:00+06:00") - 1;
    ngx_cached_time = &cached_time[0];
    ngx_time_update();
}

另一处是在ngx_time_update(void)函数里面:

    ...
    ngx_memory_barrier();
    ngx_cached_time = tp;
    ngx_cached_http_time.data = p0;
    ngx_cached_err_log_time.data = p1;
    ngx_cached_http_log_time.data = p2;
    ngx_cached_http_log_iso8601.data = p3;
    ...
    

通过指针值的变更,改变了实际指向的变量;grep代码发现,nginx中大量使用了ngx_time_update(void)来更新时间信息,在主事件循环中也是如此。

for ( ;; ) {
    ...
    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend");
    sigsuspend(&set);
    ngx_time_update();
    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
    "wake up, sigio %i", sigio);
    ...
    if (ngx_noaccept) {
        ngx_noaccept = 0;
        ngx_noaccepting = 1;
        ngx_signal_worker_processes(cycle,
        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
    }
}

OK,现在我们知道是由于我们代码执行过程中nginx一个循环尚且没有结束,进而导致ngx.now的时间还没有来得及更新;那不用ngx.now可不可以呢?lua自带os.clock()虽然能够得到时间,然而他的精度远远不够;而有时候我们的确需要知道精确的时间,比如要统计一个lua扩展中的运行时间。

接着,我在lua module发现了下面这个函数:

static int
ngx_http_lua_ngx_update_time(lua_State *L)
{
    ngx_time_update();
    return 0;
}

该函数同样被注册到了lua下面,对应的是update_time函数:

void
ngx_http_lua_inject_time_api(lua_State *L)
{
    ...
    lua_pushcfunction(L, ngx_http_lua_ngx_update_time);
    lua_setfield(L, -2, "update_time");
    ...
}

从代码上来看,该函数在lua代码中可以强制刷新nginx的缓存时间,接着我从ngx的文档上得到了佐证。

ngx.update_time

syntax: ngx.update_time()

context: init_worker_by_lua*, set_by_lua*, rewrite_by_lua*, access_by_lua*, content_by_lua*, header_filter_by_lua*, body_filter_by_lua*, log_by_lua*, ngx.timer.*, balancer_by_lua*, ssl_certificate_by_lua*

Forcibly updates the Nginx current time cache. This call involves a syscall and thus has some overhead, so do not abuse it.

This API was first introduced in v0.3.1rc32.

早干嘛了,早点查文档不就得了,折腾这么大一圈。OTZ

 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注