nginx中平滑升級:ngx_add_inherited_sockets

平滑升級取決於環境變量NGINX_VAR

ngx_pid_t
ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv)
{
    char             **env, *var;
    u_char            *p;
    ngx_uint_t         i, n;
    ngx_pid_t          pid;
    ngx_exec_ctx_t     ctx;
    ngx_core_conf_t   *ccf;
    ngx_listening_t   *ls;

    ngx_memzero(&ctx, sizeof(ngx_exec_ctx_t));

    ctx.path = argv[0];
    ctx.name = "new binary process";
    ctx.argv = argv;

    n = 2;
    env = ngx_set_environment(cycle, &n);
    if (env == NULL) {
        return NGX_INVALID_PID;
    }

    var = ngx_alloc(sizeof(NGINX_VAR)
                    + cycle->listening.nelts * (NGX_INT32_LEN + 1) + 2,
                    cycle->log);
    if (var == NULL) {
        ngx_free(env);
        return NGX_INVALID_PID;
    }

    p = ngx_cpymem(var, NGINX_VAR "=", sizeof(NGINX_VAR));

    ls = cycle->listening.elts;
    for (i = 0; i < cycle->listening.nelts; i++) {
        p = ngx_sprintf(p, "%ud;", ls[i].fd);
    }

    *p = '\0';

    env[n++] = var;

#if (NGX_SETPROCTITLE_USES_ENV)

    /* allocate the spare 300 bytes for the new binary process title */

    env[n++] = "SPARE=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
               "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
               "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
               "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
               "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";

#endif

    env[n] = NULL;

#if (NGX_DEBUG)
    {
    char  **e;
    for (e = env; *e; e++) {
        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, "env: %s", *e);
    }
    }
#endif

    ctx.envp = (char *const *) env;

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    if (ngx_rename_file(ccf->pid.data, ccf->oldpid.data) == NGX_FILE_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      ngx_rename_file_n " %s to %s failed "
                      "before executing new binary process \"%s\"",
                      ccf->pid.data, ccf->oldpid.data, argv[0]);

        ngx_free(env);
        ngx_free(var);

        return NGX_INVALID_PID;
    }

    pid = ngx_execute(cycle, &ctx);

    if (pid == NGX_INVALID_PID) {
        if (ngx_rename_file(ccf->oldpid.data, ccf->pid.data)
            == NGX_FILE_ERROR)
        {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          ngx_rename_file_n " %s back to %s failed after "
                          "an attempt to execute new binary process \"%s\"",
                          ccf->oldpid.data, ccf->pid.data, argv[0]);
        }
    }

    ngx_free(env);
    ngx_free(var);

    return pid;
}

 

獲取環境變量NGINX_VAR並執行ngx_add_inherited_sockets

src/core/nginx.c  
  
static ngx_int_t  
ngx_add_inherited_sockets(ngx_cycle_t *cycle)  
{  
    u_char           *p, *v, *inherited;  
    ngx_int_t         s;  
    ngx_listening_t  *ls;  
      
    //獲取環境變量 這裏的"NGINX_VAR"是宏定義,值爲"NGINX"  
    inherited = (u_char *) getenv(NGINX_VAR);  
  
    if (inherited == NULL) {  
        return NGX_OK;  
    }  
  
    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,  
                  "using inherited sockets from \"%s\"", inherited);  
      
    //初始化ngx_cycle.listening數組,而且數組中包含10個元素  
    if (ngx_array_init(&cycle->listening, cycle->pool, 10,  
                       sizeof(ngx_listening_t))  
        != NGX_OK)  
    {  
        return NGX_ERROR;  
    }  
      
    //遍歷環境變量  
    for (p = inherited, v = p; *p; p++) {  
      
        //環境變量的值以':'or';'分開  
        if (*p == ':' || *p == ';') {  
            //轉換十進制sockets  
            s = ngx_atoi(v, p - v);  
            if (s == NGX_ERROR) {  
                ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,  
                              "invalid socket number \"%s\" in " NGINX_VAR  
                              " environment variable, ignoring the rest"  
                              " of the variable", v);  
                break;  
            }  
  
            v = p + 1;  
            //返回新分配的數組指針地址(在參考的blog裏面這裏解釋可能有點錯誤)  
            ls = ngx_array_push(&cycle->listening);  
            if (ls == NULL) {  
                return NGX_ERROR;  
            }  
            //初始化內存空間  
            ngx_memzero(ls, sizeof(ngx_listening_t));  
            //保存socket文件描述符到數組中  
            ls->fd = (ngx_socket_t) s;  
        }  
    }   
  
    ngx_inherited = 1; //表示已經的獲得要繼承的socket  
      
    //接下來詳細講解的函數  
    return ngx_set_inherited_sockets(cycle);  
}  
/* 
根據上面的講解,大體能夠知道這個方法的用途: 
主要是讀取環境變量"NGINX" 將其中各個用分隔符":"or";"的數值, 
保存在ngx_cycel->listening數組中 
*/

 

ngx_set_inherited_sockets

src/core/ngx_connection.c  
  
ngx_int_t  
ngx_set_inherited_sockets(ngx_cycle_t *cycle)  
{  
    size_t                     len;  
    ngx_uint_t                 i;  
    ngx_listening_t           *ls;  
    socklen_t                  olen;  
#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)  
    ngx_err_t                  err;  
    struct accept_filter_arg   af;  
#endif  
#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)  
    int                        timeout;  
#endif  
    //取出cycle->listening數組中的數據地址  
    ls = cycle->listening.elts;  
    //遍歷數組  
    //要記得以前講過數組當中存放的是ngx_listening_t結構體  
    for (i = 0; i < cycle->listening.nelts; i++) {  
        //ls的fd已經在以前賦值了  
        //sockaddr分配內存空間  
        ls[i].sockaddr = ngx_palloc(cycle->pool, NGX_SOCKADDRLEN);  
        if (ls[i].sockaddr == NULL) {  
            return NGX_ERROR;  
        }  
           
        ls[i].socklen = NGX_SOCKADDRLEN;  
        //獲取socket名字,要用於判斷是否有效  
        if (getsockname(ls[i].fd, ls[i].sockaddr, &ls[i].socklen) == -1) {  
            ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,  
                          "getsockname() of the inherited "  
                          "socket #%d failed", ls[i].fd);  
            ls[i].ignore = 1;  
            continue;  
        }  
        //查看sockaddr 地址族類型 根據類型設置最大長度  
        switch (ls[i].sockaddr->sa_family) {  
  
#if (NGX_HAVE_INET6)  
        case AF_INET6:  
             ls[i].addr_text_max_len = NGX_INET6_ADDRSTRLEN;  
             len = NGX_INET6_ADDRSTRLEN + sizeof(":65535") - 1;  
             break;  
#endif  
  
#if (NGX_HAVE_UNIX_DOMAIN)  
        case AF_UNIX:  
             ls[i].addr_text_max_len = NGX_UNIX_ADDRSTRLEN;  
             len = NGX_UNIX_ADDRSTRLEN;  
             break;  
#endif  
  
        case AF_INET:  
             ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN;  
             len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;  
             break;  
  
        default:  
            ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,  
                          "the inherited socket #%d has "  
                          "an unsupported protocol family", ls[i].fd);  
            ls[i].ignore = 1;  
            continue;  
        }  
  
        ls[i].addr_text.data = ngx_pnalloc(cycle->pool, len);  
        if (ls[i].addr_text.data == NULL) {  
            return NGX_ERROR;  
        }  
        //以前的長度主要爲了下面的轉換作準備  
        //將socket綁定的地址轉換爲文本格式(ipv4和ipv6的不相同)  
        len = ngx_sock_ntop(ls[i].sockaddr, ls[i].addr_text.data, len, 1);  
        if (len == 0) {  
            return NGX_ERROR;  
        }  
  
        ls[i].addr_text.len = len;  
        //這裏設置類每一個監聽的socket的backlog爲511  
        ls[i].backlog = NGX_LISTEN_BACKLOG;  
  
        olen = sizeof(int);  
        //獲取文件描述符的接受緩衝區大小,並用rcvbuf保存,而且指定rcvbuf大小olen  
        if (getsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF, (void *) &ls[i].rcvbuf,  
                       &olen)  
            == -1)  
        {  
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,  
                          "getsockopt(SO_RCVBUF) %V failed, ignored",  
                          &ls[i].addr_text);  
  
            ls[i].rcvbuf = -1;  
        }  
  
        olen = sizeof(int);  
        //獲取文件描述符發送緩衝區大小,並用sndbuf保存,而且指定sndbuf大小olen  
        if (getsockopt(ls[i].fd, SOL_SOCKET, SO_SNDBUF, (void *) &ls[i].sndbuf,  
                       &olen)  
            == -1)  
        {  
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,  
                          "getsockopt(SO_SNDBUF) %V failed, ignored",  
                          &ls[i].addr_text);  
  
            ls[i].sndbuf = -1;  
        }  
  
#if 0  
        /* SO_SETFIB is currently a set only option */  
  
#if (NGX_HAVE_SETFIB)  
  
        if (getsockopt(ls[i].setfib, SOL_SOCKET, SO_SETFIB,  
                       (void *) &ls[i].setfib, &olen)  
            == -1)  
        {  
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,  
                          "getsockopt(SO_SETFIB) %V failed, ignored",  
                          &ls[i].addr_text);  
  
            ls[i].setfib = -1;  
        }  
  
#endif  
#endif  
/* 
當支持accept filter時,經過SO_ACCEPTFILTER選項取得socket的accept_filter表 
保存在對應項的accept_filter中; 
下面是SO_ACCEPTFILTER的解釋(由於個人書裏沒有因此上網找的) 
 
SO_ACCEPTFILTER 是socket上的輸入過濾,他在接手前 
將過濾掉傳入流套接字的連接,功能是服務器不等待 
最後的ACK包而僅僅等待攜帶數據負載的包 
 
*/  
#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)  
  
        ngx_memzero(&af, sizeof(struct accept_filter_arg));  
        olen = sizeof(struct accept_filter_arg);  
  
        if (getsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, &af, &olen)  
            == -1)  
        {  
            err = ngx_errno;  
  
            if (err == NGX_EINVAL) {  
                continue;  
            }  
  
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, err,  
                          "getsockopt(SO_ACCEPTFILTER) for %V failed, ignored",  
                          &ls[i].addr_text);  
            continue;  
        }  
  
        if (olen < sizeof(struct accept_filter_arg) || af.af_name[0] == '\0') {  
            continue;  
        }  
  
        ls[i].accept_filter = ngx_palloc(cycle->pool, 16);  
        if (ls[i].accept_filter == NULL) {  
            return NGX_ERROR;  
        }  
  
        (void) ngx_cpystrn((u_char *) ls[i].accept_filter,  
                           (u_char *) af.af_name, 16);  
#endif  
/* 
若是當前操做系統TCP層支持TCP_DEFER_ACCEPT, 
則試圖獲取TCP_DEFER_ACCEPT的timeout值。Timeout大於0時, 
則將socket對應deferred_accept標誌設爲1 
詳細解釋卸寫在錄裏面了哦!!! 
*/  
  
  
#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)  
  
        timeout = 0;  
        olen = sizeof(int);  
  
        if (getsockopt(ls[i].fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, &olen)  
            == -1)  
        {  
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, ngx_errno,  
                          "getsockopt(TCP_DEFER_ACCEPT) for %V failed, ignored",  
                          &ls[i].addr_text);  
            continue;  
        }  
  
        if (olen < sizeof(int) || timeout == 0) {  
            continue;  
        }  
  
        ls[i].deferred_accept = 1;  
#endif  
    }  
  
    return NGX_OK;  
}

 

總結:

能夠看出html

ngx_add_inherited_sockets:主要是經過環境變量,獲取到fd的值,而後存在數組當中;nginx

ngx_set_inherited_sockets:主要是對數組中的每個元素進行判斷是否有效,而後進行初始化操做。數組

 

附錄:服務器

TCP_DEFER_ACCEPT
我 們首先考慮的第1個選項是TCP_DEFER_ACCEPT(這是Linux系統上的叫法,其餘一些操做系統上也有一樣的選項但使用不一樣的名字)。爲了理 解TCP_DEFER_ACCEPT選項的具體思想,咱們有必要大體闡述一下典型的HTTP客戶/服務器交互過程。請回想下TCP是如何與傳輸數據的目標創建鏈接的。在網絡上,在分離的單元之間傳輸的信息稱爲IP包(或IP 數據報)。一個包總有一個攜帶服務信息的包頭,包頭用於內部協議的處理,而且它也能夠攜帶數據負載。服務信息的典型例子就是一套所謂的標誌,它把包標記表明TCP/IP協議棧內的特殊含義,例如收到包的成功確認等等。一般,在通過「標記」的包裏攜帶負載是徹底可能的,但有時,內部邏輯迫使TCP/IP協議 棧發出只有包頭的IP包。這些包常常會引起討厭的網絡延遲並且還增長了系統的負載,結果致使網絡性能在總體上下降。網絡


如今服務器建立了一個套接字同時等待鏈接。TCP/IP式的鏈接過程就是所謂「3次握手」。首先,客戶程序發送一個設置SYN標誌並且不帶數據負載的TCP包(一個SYN包)。服務器則以發出帶SYN/ACK標誌的數據包(一個SYN/ACK包)做爲剛纔收到包的確認響應。客戶隨後發送一個ACK包確認收到了第2個包從而結束鏈接 過程。在收到客戶發來的這個SYN/ACK包以後,服務器會喚醒一個接收進程等待數據到達。當3次握手完成後,客戶程序即開始把「有用的」的數據發送給服務器。一般,一個HTTP請求的量是很小的並且徹底能夠裝到一個包裏。可是,在以上的狀況下,至少有4個包將用來進行雙向傳輸,這樣就增長了可觀的延遲時間。此外,你還得注意到,在「有用的」數據被髮送以前,接收方已經開始在等待信息了。socket


爲了減輕這些問題所帶來的影響,Linux(以及其餘的 一些操做系統)在其TCP實現中包括了TCP_DEFER_ACCEPT選項。它們設置在偵聽套接字的服務器方,該選項命令內核不等待最後的ACK包並且在第1個真正有數據的包到達才初始化偵聽進程。在發送SYN/ACK包以後,服務器就會等待客戶程序發送含數據的IP包。如今,只須要在網絡上傳送3個包 了,並且還顯著下降了鏈接創建的延遲,對HTTP通訊而言尤爲如此。函數

 

對於那些支持deffered accept的操做系統,nginx會設置這個參數來加強功能,設置了這個參數,在accept的時候,只有當實際收到了數據,才喚醒在accept等待的進程,能夠減小一些無聊的上下文切換。性能

 

後半部分轉自:ui

http://www.cnblogs.com/h2-database/archive/2012/05/23/2583263.htmlspa

相關文章
相關標籤/搜索