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--;
        }  

這裏寫圖片描述

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