Nginx事件模块小结

这两天着重看nginx事件模块的处理,这一部分跟网络框架比较大。跟现在工作内容相关性也比较大,总结一下它在这方面的处理方式,下次写代码的时候可以用一下。得到了2点启发:

1. 防止epoll惊群,可以采用锁的方式处理,但是不能等epoll响应的事件处理完再释放锁,这样锁的占用时间会比较长,从而使得其他的进程无法epoll wait,拖慢效率。可以先把事件加入连接池,释放锁,然后再处理。
2. 负载均衡的粗略实现。下面有描述。

同时也有些许疑问。

1. 这些listen的处理可以理解,但当普通连接,他转发给server之后,如何做处理还不清楚,这点需要更层次的阅读代码来理解。
2. ngx_process_events_and_timers 代码当中参杂了一些定时器的实现,这个定时器与cache manage process有关,需要理清楚一些。

  1. 首先nginx整个流程处理依赖的是一个cycle的数据结构,这个数据结构当中保存了一个ngx_listening_t链表,纪录的是nginx监听的信息。在init_cycle的时候会根据配置文件来初始化这个链表,并且调用ngx_open_listening_sockets函数来bind和listen。

  2. 注意这个init_cycle是在主进程内进行初始化的,所以在spawn worker process的时候会复制一份给worker process。

  3. 现在来看ngx_event.c :ngx_event_process_init 的部分代码,这部分代码包含了许多东西,下面来看下这代码,他预先分配了连接池和读事件,写事件。

cycle->connections =ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log); 
//预先分配connection_n个连接
cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,cycle->log);
//预先分配connection_n个读事件和写事件
i = cycle->connection_n;
    next = NULL;
    do {
        i--;

        c[i].data = next;
        c[i].read = &cycle->read_events[i];
        c[i].write = &cycle->write_events[i];
        c[i].fd = (ngx_socket_t) -1;

        next = &c[i];
    } while (i);
    //这个循环将预先分配的读事件和写事件分配到连接。
    cycle->free_connections = next;
    cycle->free_connection_n = cycle->connection_n;
    //构造空闲连接池。
  1. 继续往下阅读ngx_event.c :ngx_event_process_init 的部分代码。下面这部分代码就跟监听事件注册到epoll 有关了。值得注意的一点,这边会有TCP和UDP两种协议,以下讨论的是TCP协议,UDP不予考虑。
 ls = cycle->listening.elts;
    for (i = 0; i < cycle->listening.nelts; i++) {
        c = ngx_get_connection(ls[i].fd, cycle->log);
        // 根据监听端口的fd,从以上的空闲connection池获取连接
        if (c == NULL) {
            return NGX_ERROR;
        }

        c->type = ls[i].type;
        c->log = &ls[i].log;

        c->listening = &ls[i];
        ls[i].connection = c;

        rev = c->read;

        rev->log = c->log;
        rev->accept = 1;
        rev->handler = (c->type == SOCK_STREAM) ? ngx_event_accept
                                                : ngx_event_recvmsg;
       // 根据typeTCP还是UDP来分配回调函数是accept还是recvmsg。
  1. 上面讲的是epoll add的部分,所以现在自然而然要讨论epoll wait的部分了。但是呢这边先讨论一个”惊群” 现象的处理了。其实思想比较简单,就是加锁,但是呢,因为加锁的会堵塞,影响性能, 所以先判断然后锁。
    代码位置为ngx_event:ngx_process_events_and_timers
    这边撸代码会比较多, 所以只列一些关键函数, 并说明其做的事情。
ngx_trylock_accept_mutex(); //获取锁,获取不到就直接返回。

ngx_process_events();// 这是一个宏,根据配置文件,定义处理的方法,如果是epoll module 则做3件事情,
                     // epoll wait()
                     // 根据epoll的返回,添加事件到队列
                     // 根据read事件的标志位accept来区分什么accept事件还是recv事件,从而添加到不同的队列

 ngx_event_process_posted(cycle, &ngx_posted_accept_events);
 //处理accept事件队列的数组,值得注意,这时候不unlock锁,至于为什么?现在不明
 ngx_event_process_posted(cycle, &ngx_posted_events);
  1. nginx如何做负载均衡的呢?
    用下面的ngx_accept_disabled 变量来控制负载的均衡,ngx_accept_disabled 表示空闲连接数的7/8,
    如果小于0.则直接处理事件池的事件了。
if (ngx_accept_disabled > 0) {
            ngx_accept_disabled--;
        }  

这里写图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章