<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                Nginx版本:1.9.1 我的博客:[http://blog.csdn.net/zhangskd](http://blog.csdn.net/zhangskd) ? **算法介紹** ? ip_hash算法的原理很簡單,根據請求所屬的客戶端IP計算得到一個數值,然后把請求發往該數值對應的后端。 所以同一個客戶端的請求,都會發往同一臺后端,除非該后端不可用了。ip_hash能夠達到保持會話的效果。 ip_hash是基于round robin的,判斷后端是否可用的方法是一樣的。 ? 第一步,根據客戶端IP計算得到一個數值。 hash1 = (hash0 * 113 + addr[0]) % 6271; hash2 = (hash1 * 113 + addr[1]) % 6271; hash3 = (hash2 * 113 + addr[2]) % 6271; hash3就是計算所得的數值,它只和初始數值hash0以及客戶端的IP有關。 ? 第二步,根據計算所得數值,找到對應的后端。 w = hash3 % total_weight; while (w >= peer->weight) { ??? w -= peer->weight; ??? peer = peer->next; ??? p++; } total_weight為所有后端權重之和。遍歷后端鏈表時,依次減去每個后端的權重,直到w小于某個后端的權重。 選定的后端在鏈表中的序號為p。因為total_weight和每個后端的weight都是固定的,所以如果hash3值相同, 則找到的后端相同。 ? **指令的解析函數** ? 在一個upstream配置塊中,如果有ip_hash指令,表示使用ip_hash負載均衡算法。 ip_hash指令的解析函數為ngx_http_upstream_ip_hash,主要做了: 指定初始化此upstream塊的函數peer.init_upstream 指定此upstream塊中server指令支持的屬性 ~~~ static char *ngx_http_upstream_ip_hash (ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_upstream_srv_conf_t *uscf; /* 獲取對應的upstream配置塊 */ uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module); if (uscf->peer.init_upstream) ngx_conf_log_error(NGX_LOG_WARN, cf, 0, "load balancing method redefined"); /* 指定初始化此upstream塊的函數 */ uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash; /* 指定此upstream塊中server指令支持的屬性 */ uscf->flags = NGX_HTTP_UPSTREAM_CREATE | NGX_HTTP_UPSTREAM_WEIGHT | NGX_HTTP_UPSTREAM_MAX_FAILS | NGX_HTTP_UPSTREAM_FAIL_TIMEOUT | NGX_HTTP_UPSTREAM_DOWN; return NGX_CONF_OK; } ~~~ 以下是upstream塊中server指令可支持的屬性 NGX_HTTP_UPSTREAM_CREATE:檢查是否重復創建,以及必要的參數是否填寫 NGX_HTTP_UPSTREAM_WEIGHT:server指令支持weight屬性 NGX_HTTP_UPSTREAM_MAX_FAILS:server指令支持max_fails屬性 NGX_HTTP_UPSTREAM_FAIL_TIMEOUT:server指令支持fail_timeout屬性 NGX_HTTP_UPSTREAM_DOWN:server指令支持down屬性 NGX_HTTP_UPSTREAM_BACKUP:server指令支持backup屬性 ? **初始化upstream塊** ? 執行完指令的解析函數后,緊接著會調用所有HTTP模塊的init main conf函數。 在執行ngx_http_upstream_module的init main conf函數時,會調用所有upstream塊的初始化函數。 對于使用ip_hash的upstream塊,其初始化函數(peer.init_upstream)就是上一步中指定的 ngx_http_upstream_init_ip_hash。它主要做了: 調用默認的初始化函數ngx_http_upstream_init_round_robin來創建和初始化后端集群,保存該upstream塊的數據 指定初始化請求的負載均衡數據的函數peer.init ? 因為臟活累活都讓默認的函數給干了,所以ngx_http_upstream_init_ip_hash的代碼就幾行:) ~~~ static ngx_int_t ngx_http_upstream_init_ip_hash (ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us) { if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) return NGX_ERROR; us->peer.init = ngx_http_upstream_init_ip_hash_peer; /* 初始化請求負載均衡數據的函數 */ return NGX_OK; } ~~~ ? **初始化請求的負載均衡數據**? ? 收到一個請求后,一般使用的反向代理模塊(upstream模塊)為ngx_http_proxy_module, 其NGX_HTTP_CONTENT_PHASE階段的處理函數為ngx_http_proxy_handler,在初始化upstream機制的 ngx_http_upstream_init_request函數中,調用在第二步中指定的peer.init,主要用于初始化請求的負載均衡數據。 對于ip_hash,peer.init實例為ngx_http_upstream_init_ip_hash_peer,主要做了: 調用round robin的per request負載均衡初始化函數,創建和初始化其per request負載均衡數據,即iphp->rrp。 重新指定peer.get,用于從集群中選取一臺后端服務器。 保存客戶端的地址,初始化ip_hash的per request負載均衡數據。 ? ip_hash的per request負載均衡數據的結構體為ngx_http_upstream_ip_hash_peer_data_t。 ~~~ typedef struct { ngx_http_upstream_rr_peer_data_t rrp; /* round robin的per request負載均衡數據 */ ngx_uint_t hash; /* 根據客戶端IP計算所得的hash值 */ u_char addrlen; /* 使用客戶端IP的后三個字節來計算hash值 */ u_char *addr; /* 客戶端的IP */ u_char tries; /* 已經嘗試了多少次 */ ngx_event_get_peer_pt get_rr_peer; /* round robin算法的peer.get函數 */ } ngx_http_upstream_ip_hash_peer_data_t; ~~~ ~~~ static ngx_int_t ngx_http_upstream_init_ip_hash_peer (ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us) { struct sockaddr_in *sin; ... ngx_http_upstream_ip_hash_peer_data_t *iphp; /* 創建ip_hash的per request負載均衡數據的實例 */ iphp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_ip_hash_peer_data_t)); if (iphp == NULL) return NGX_ERROR; /* 首先調用round robin的per request負載均衡數據的初始化函數, * 創建和初始化round robin的per request負載均衡數據實例,即iphp->rrp。 */ r->upstream->peer.data = &iphp->rrp; if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) return NGX_ERROR: /* 重新指定peer.get,用于從集群中選取一臺后端服務器 */ r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer; /* 客戶端的地址類型 */ switch(r->connection->sockaddr->sa_family) { case AF_INET: sin = (struct sockaddr_in *) r->connection->sockaddr; iphp->addr = (u_char *) &sin->sin_addr.s_addr; /* 客戶端的IP */ iphp->addrlen = 3; /* 使用客戶端IP的后三個字節來計算hash值 */ break; #if (NGX_HAVE_INET6) ... #endif default: iphp->addr = ngx_http_upstream_ip_hash_pseudo_addr; iphp->addrlen = 3; } iphp->hash = 89; iphp->tries = 0; iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer; /* 保存round robin的peer.get函數 */ } ~~~ ? **選取一臺后端服務器** ? 一般upstream塊中會有多臺后端,那么對于本次請求,要選定哪一臺后端呢? 這時候第三步中r->upstream->peer.get指向的函數就派上用場了: 采用ip_hash算法,從集群中選出一臺后端來處理本次請求。 選定后端的地址保存在pc->sockaddr,pc為主動連接。 函數的返回值: NGX_DONE:選定一個后端,和該后端的連接已經建立。之后會直接發送請求。 NGX_OK:選定一個后端,和該后端的連接尚未建立。之后會和后端建立連接。 NGX_BUSY:所有的后端(包括備份集群)都不可用。之后會給客戶端發送502(Bad Gateway)。 ~~~ static ngx_int_t ngx_http_upstream_get_ip_hash_peer (ngx_peer_connection_t *pc, void *data) { ngx_http_upstream_ip_hash_peer_data_t *iphp = data; /* 請求的負載均衡數據 */ time_t now; ngx_int_t w; uintptr_t m; ngx_uint_t i, n, p, hash; ngx_http_upstream_rr_peer_t *peer; ... /* 如果只有一臺后端,或者嘗試次數超過20次,則使用輪詢的方式來選取后端 */ if (iphp->tries > 20 || iphp->rrp.peers->single) { return iphp->get_rr_peer(pc, &iphp->rrp); } now = ngx_time(); pc->cached = 0; pc->connection = NULL; hash = iphp->hash; /* 本次選取的初始hash值 */ for ( ; ; ) { /* 根據客戶端IP、本次選取的初始hash值,計算得到本次最終的hash值 */ for (i = 0; i < (ngx_uint_t) iphp->addrlen; i++) hash = (hash * 113 + iphp->addr[i]) % 6271; /* total_weight和weight都是固定值 */ w = hash % iphp->rrp.peers->total_weight; peer = iphp->rrp.peers->peer; /* 第一臺后端 */ p = 0; while (w >= peer->weight) { w -= peer->weight; peer = peer->next; p++; } /* 檢查第此后端在狀態位圖中對應的位,為1時表示不可用 */ n = p / (8 * sizeof(uintptr_t)); m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t)); if (iphp->rrp.tried[n] & m) goto next; /* 檢查后端是否永久不可用 */ if (peer->down) goto next; /* 在一段時間內,如果此后端服務器的失敗次數,超過了允許的最大值,那么不允許使用此后端了 */ if (peer->max_fails && peer->fails >= peer->max_fails && now - peer->checked <= peer->fail_timeout) goto next; break; next: /* 增加已嘗試的次數,如果超過20次,則使用輪詢的方式來選取后端 */ if (++iphp->tries > 20) return iphp->get_rr_peer(pc, &iphp->rrp); } iphp->rrp.current = peer; /* 選定的可用后端 */ /* 保存選定的后端服務器的地址,之后會向這個地址發起連接 */ pc->sockaddr = peer->sockaddr; pc->socklen = peer->socklen; pc->name = &peer->name; peer->conns++; /* 更新checked時間 */ if (now - peer->checked > peer->fail_timeout) peer->checked = now; iphp->rrp.tried[n] |= m; /* 對于此請求,如果之后需要再次選取后端,不能再選取這個后端了 */ iphp->hash = hash; /* 保存hash值,下次可能還會用到 */ return NGX_OK: } ~~~ ?
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看