Nginx源码阅读笔记-事件处理模块

栏目: 服务器 · Nginx · 发布时间: 5年前

内容简介:大概做高性能服务器的,都绕不开事件处理模块来,一般一个事件模块,会分为以下几部分:下面就这三部分展开Nginx事件处理模块的分析。描述事件的数据结构,一般至少需要以下几部分数据:

大概做高性能服务器的,都绕不开事件处理模块来,一般一个事件模块,会分为以下几部分:

  • 如何定义一个描述事件的数据结构。
  • 如何在事件模块中支持定时器。
  • 如果需要支持多平台,事件模块需要考虑如何统一以及区分各平台的具体实现。

下面就这三部分展开Nginx事件处理模块的分析。

ngx_event_t

描述事件的数据结构,一般至少需要以下几部分数据:

  • 用于保存用户相关的数据。
  • 用于保存事件触发之后的回调函数。
  • 用于表示事件状态、类型的数据。

nginx中,描述事件采用的数据结构是ngx_event_t中,其内部成员就是按照前面的三部分来划分了。

  • void *data:事件相关的数据。
  • ngx_event_handler_pt handler:事件被触发时的回调函数。
  • 第三类数据,ngx_event_t中划分的比较仔细:
    • unsigned write:1:可写标志位
    • unsigned active:1:活跃标志位
    • unsigned disabled:1:禁用标志位
    • unsigned eof:1:为1表示字节流已经结束
    • unsigned error:1:处理事件出错
    • unsigned timedout:1:事件超时
    • unsigned timer_set:1:为1表示这是一个超时事件
    • unsigned deferred_accept:1:为1表示需要延迟接收TCP连接
  • 除了以上三部分,还有其他一些重要的数据:
    • ngx_rbtree_node_t timer:红黑树节点,用于实现定时器的,下面讨论定时器再展开。
    • ngx_queue_t queue:延迟队列,如果事件不在轮询循环中直接处理,而是之后被处理,就放在这个队列中。

总体来看,event这个结构体为了涵盖所有可能的事件,做的大而全,不只是用来描述一般的IO事件,还包括了定时器事件,还包括了接收连接相关的数据。

定时器的实现

Nginx内部使用红黑树来实现定时器,目的在于能够快速的查询到哪些定时器超时了。不同的事件结构中,这部分实现采用的数据结构不一样,libevent、libuv采用的是最小堆,redis比较挫,这部分采用的是链表。

在一个事件循环中,因为既要考虑到一般的IO事件,又要考虑到定时器事件,所以都会以一个最近被触发的定时器来做为查询IO事件被触发的时间,即以下的伪代码:

查询最近将被触发的定时器超时时间返回t
将t做为epoll_wait之类的查询IO事件的超时时间,即最长等待t时间看有没有IO事件被触发
遍历定时器,查询已经超时的定时器进行回调处理

从这里可以看出,“迅速查询到距离当前最近被触发的定时器时间”以及“迅速查询到当前哪些定时器超时”,是这个定时器模块速度的关键。

由于红黑树、最小堆这种平衡数据结构,每次查询都排除掉当前一半的元素,可以做到时间复杂度O(logn),所以就常用来实现定时器了。

事件模块的实现

由于nginx需要跑在多个平台下面,而不同平台使用的事件机制又不一样,比如 linux 是epoll,bsd是kqueue等,需要实现事件模块的时候,既需要统一事件模块的共性部分,又需要区分不同平台的差异部分。

这看上去又是一个面向对象的设计问题了:基类负责实现共性的部分,子类具体再来实现各平台相关的部分。

前面 分析libuv 的时候提到过,libuv多使用宏来模拟C++中的继承,不是很认可这个代码风格,来看看nginx类似场景的实现。

nginx中,将事件相关的操作函数统一放在结构体ngx_event_actions_t中,可以把这部分类比于子类需要实现的函数接口:

typedef struct {
  ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
  ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

  ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
  ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);

  ngx_int_t  (*add_conn)(ngx_connection_t *c);
  ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);

  ngx_int_t  (*notify)(ngx_event_handler_pt handler);

  ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
    ngx_uint_t flags);

  ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
  void       (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;

前面在分析到nginx如何解析配置的时候提到过,nginx中的配置是分层次的,event模块做为一个顶层的core模块,内部又有子模块,而这里的事件模块就是event模块中的子模块:

typedef struct {
  ngx_str_t              *name;

  void                 *(*create_conf)(ngx_cycle_t *cycle);
  char                 *(*init_conf)(ngx_cycle_t *cycle, void *conf);

  ngx_event_actions_t     actions;
} ngx_event_module_t;

在具体实现中,每个平台的事件模块创建自己的ngx_event_module_t结构,在create_conf、init_conf中完成对事件模块的初始化,然后填充模块的actions结构体。

最后,具体调用actions结构体中的函数,封装到宏里面,毕竟虽然有多平台的实现,最后也只能用上一个而已:

#define ngx_process_events   ngx_event_actions.process_events
#define ngx_done_events      ngx_event_actions.done

#define ngx_add_event        ngx_event_actions.add
#define ngx_del_event        ngx_event_actions.del
#define ngx_add_conn         ngx_event_actions.add_conn
#define ngx_del_conn         ngx_event_actions.del_conn

#define ngx_notify           ngx_event_actions.notify

而前面提到的事件处理部分共性的地方,全都放在函数ngx_process_events_and_timers里,那个函数里面再通过宏ngx_process_events调用具体事件模块的处理函数。

这里有个细节,其实前面的分析也提到过,nginx的事件模块里,不一定在检查到事件触发之后就会被马上调用回调函数来处理,而是可能放在一个post队列中,在轮询完所有事件之后再进行回调:

if (flags & NGX_POST_EVENTS) {
		// 有NGX_POST_EVENTS标志位的情况,将accept事件放到ngx_posted_accept_events队列中
		// 等待后续被回调
		queue = rev->accept ? &ngx_posted_accept_events
												: &ngx_posted_events;

		ngx_post_event(rev, queue);

} else {
		// 否则直接处理
		rev->handler(rev);
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

网易一千零一夜

网易一千零一夜

网易杭研项目管理部 / 电子工业出版社 / 2016-9-1 / 46

本书是网易杭州研究院项目管理部多年来丰富的项目管理实践总结与干货分享。字字句句凝结了网易项目经理的甘与苦、汗与泪。 全书围绕项目管理体系,从敏捷实践、项目立项、需求管理、沟通管理,到计划进度管理、风险管理,真实反映了网易面向互联网产品项目管理实战经验与心路历程。 不论你是项目管理新手,还是资深项目经理,都可以从本书中获得启发与借鉴。一起来看看 《网易一千零一夜》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码