9.7 定时器事件

Nginx实现了自己的定时器触发机制,它与epoll等事件驱动模块处理的网络事件不同:在网络事件中,网络事件的触发是由内核完成的,内核如果支持epoll就可以使用ngx_epoll_module模块驱动事件,内核如果仅支持select那就得使用ngx_select_module模块驱动事件;定时器事件则完全是由Nginx自身实现的,它与内核完全无关。那么,所有事件的定时器是如何组织起来的呢?在事件超时后,定时器是如何触发事件的呢?读者将在9.7.2节中看到定时器事件的设计,但首先需要弄清楚Nginx的时间是如何管理的。Nginx与一般的服务器不同,出于性能的考虑(不需要每次获取时间都调用gettimeofday方法),Nginx使用的时间是缓存在其内存中的,这样,在Nginx模块获取时间时,只是获取内存中的几个整型变量而已。这个缓存的时间是如何更新的呢?又是在什么时刻更新的呢?这些问题读者会在9.7.1节中获得答案。

9.7.1 缓存时间的管理

Nginx中的每个进程都会单独地管理当前时间,下面来看一下缓存的全局时间变量是什么。ngx_time_t结构体是缓存时间变量的类型,如下所示。


typedef struct{

//格林威治时间1970年1月1日凌晨0点0分0秒到当前时间的秒数

time_t sec;

//sec成员只能精确到秒,msec则是当前时间相对于sec的毫秒偏移量

ngx_uint_t msec;

//时区

ngx_int_t gmtoff;

}ngx_time_t;


可以看到,ngx_time_t是精确到毫秒的。当然,ngx_time_t结构用起来并不是那么方便,作为Web服务器,很多时候要用到可读性较强的规范的时间字符串,因此,Nginx定义了以下全局变量用于缓存时间,代码如下。


//格林威治时间1970年1月1日凌晨0点0分0秒到当前时间的毫秒数

volatile ngx_msec_t ngx_current_msec;

//ngx_time_t结构体形式的当前时间

volatile ngx_time_t*ngx_cached_time;

/用于记录error_log的当前时间字符串,它的格式类似于:"1970/09/28 12:00:00"/

volatile ngx_str_t ngx_cached_err_log_time;

/用于HTTP相关的当前时间字符串,它的格式类似于:"Mon,28 Sep 1970 06:00:00 GMT"/

volatile ngx_str_t ngx_cached_http_time;

/用于记录HTTP日志的当前时间字符串,它的格式类似于:"28/Sep/1970:12:00:00+0600"/

volatile ngx_str_t ngx_cached_http_log_time;

//以ISO 8601标准格式记录下的字符串形式的当前时间

volatile ngx_str_t ngx_cached_http_log_iso8601;


Nginx为用户提供了6种当前时间的表示形式,这已经足够用了。Nginx缓存时间的操作方法见表9-4所示。

ngx_tm_t是标准的tm类型时间,下面先看一下tm时间是什么样的,代码如下。

9.7.1 缓存时间的管理 - 图1


struct tm{

//秒-取值区间为[0,59]

int tm_sec;

//分-取值区间为[0,59]

int tm_min;

//时-取值区间为[0,23]

int tm_hour;

//一个月中的日期-取值区间为[1,31]

int tm_mday;

//月份(从一月开始,0代表一月)-取值区间为[0,11]

int tm_mon;

//年份,其值等于实际年份减去1900

int tm_year;

//星期-取值区间为[0,6],其中0代表星期天,1代表星期一,依此类推

int tm_wday;

/从每年的1月1日开始的天数-取值区间为[0,365],其中0代表1月1日,1代表1月2日,依此类推/

int tm_yday;

/夏令时标识符。在实行夏令时的时候,tm_isdst为正;不实行夏令时的时候,tm_isdst为0;在不了解情况时,tm_isdst为负/

int tm_isdst;

};


ngx_tm_t与tm用法是完全一致的,如下所示。


typedef struct tm

ngx_tm_t;

define ngx_tm_sec

tm_sec

define ngx_tm_min

tm_min

define ngx_tm_hour

tm_hour

define ngx_tm_mday

tm_mday

define ngx_tm_mon

tm_mon

define ngx_tm_year

tm_year

define ngx_tm_wday

tm_wday

define ngx_tm_isdst

tm_isdst


可以看到,ngx_tm_t中类似ngx_tm_sec这样的成员与tm_sec是完全一致的。

这个缓存时间什么时候会更新呢?对于worker进程而言,除了Nginx启动时更新一次时间外,任何更新时间的操作都只能由ngx_epoll_process_events方法(参见9.6.3节)执行。回顾一下ngx_epoll_process_events方法的代码,当flags参数中有NGX_UPDATE_TIME标志位,或者ngx_event_timer_alarm标志位为1时,就会调用ngx_time_update方法更新缓存时间。