日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Nginx >内容正文

Nginx

Nginx的负载均衡 - 保持会话 (ip_hash)

發(fā)布時間:2025/3/21 Nginx 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Nginx的负载均衡 - 保持会话 (ip_hash) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

2019獨角獸企業(yè)重金招聘Python工程師標準>>>

算法介紹

?

ip_hash算法的原理很簡單,根據(jù)請求所屬的客戶端IP計算得到一個數(shù)值,然后把請求發(fā)往該數(shù)值對應的后端。

所以同一個客戶端的請求,都會發(fā)往同一臺后端,除非該后端不可用了。ip_hash能夠達到保持會話的效果。

ip_hash是基于round robin的,判斷后端是否可用的方法是一樣的。

?

第一步,根據(jù)客戶端IP計算得到一個數(shù)值。

hash1 = (hash0 * 113 + addr[0]) % 6271;

hash2 = (hash1 * 113 + addr[1]) % 6271;

hash3 = (hash2 * 113 + addr[2]) % 6271;

hash3就是計算所得的數(shù)值,它只和初始數(shù)值hash0以及客戶端的IP有關。

?

第二步,根據(jù)計算所得數(shù)值,找到對應的后端。

w = hash3 % total_weight;

while (w >= peer->weight) {

??? w -= peer->weight;

??? peer = peer->next;

??? p++;

}

total_weight為所有后端權重之和。遍歷后端鏈表時,依次減去每個后端的權重,直到w小于某個后端的權重。

選定的后端在鏈表中的序號為p。因為total_weight和每個后端的weight都是固定的,所以如果hash3值相同,

則找到的后端相同。

?

指令的解析函數(shù)

?

在一個upstream配置塊中,如果有ip_hash指令,表示使用ip_hash負載均衡算法。

ip_hash指令的解析函數(shù)為ngx_http_upstream_ip_hash,主要做了:

指定初始化此upstream塊的函數(shù)peer.init_upstream

指定此upstream塊中server指令支持的屬性

[java] view plain copy
  • 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塊的函數(shù)?*/??
  • ????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:檢查是否重復創(chuàng)建,以及必要的參數(shù)是否填寫

    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塊

    ?

    執(zhí)行完指令的解析函數(shù)后,緊接著會調(diào)用所有HTTP模塊的init main conf函數(shù)。

    在執(zhí)行ngx_http_upstream_module的init main conf函數(shù)時,會調(diào)用所有upstream塊的初始化函數(shù)。

    對于使用ip_hash的upstream塊,其初始化函數(shù)(peer.init_upstream)就是上一步中指定的

    ngx_http_upstream_init_ip_hash。它主要做了:

    調(diào)用默認的初始化函數(shù)ngx_http_upstream_init_round_robin來創(chuàng)建和初始化后端集群,保存該upstream塊的數(shù)據(jù)

    指定初始化請求的負載均衡數(shù)據(jù)的函數(shù)peer.init

    ?

    因為臟活累活都讓默認的函數(shù)給干了,所以ngx_http_upstream_init_ip_hash的代碼就幾行:)

    [java] view plain copy
  • 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;?/*?初始化請求負載均衡數(shù)據(jù)的函數(shù)?*/??
  • ????return?NGX_OK;??
  • }??
  • ?

    初始化請求的負載均衡數(shù)據(jù)?

    ?

    收到一個請求后,一般使用的反向代理模塊(upstream模塊)為ngx_http_proxy_module,

    其NGX_HTTP_CONTENT_PHASE階段的處理函數(shù)為ngx_http_proxy_handler,在初始化upstream機制的

    ngx_http_upstream_init_request函數(shù)中,調(diào)用在第二步中指定的peer.init,主要用于初始化請求的負載均衡數(shù)據(jù)。

    對于ip_hash,peer.init實例為ngx_http_upstream_init_ip_hash_peer,主要做了:

    調(diào)用round robin的per request負載均衡初始化函數(shù),創(chuàng)建和初始化其per request負載均衡數(shù)據(jù),即iphp->rrp。

    重新指定peer.get,用于從集群中選取一臺后端服務器。

    保存客戶端的地址,初始化ip_hash的per request負載均衡數(shù)據(jù)。

    ?

    ip_hash的per request負載均衡數(shù)據(jù)的結構體為ngx_http_upstream_ip_hash_peer_data_t。

    [java] view plain copy
  • typedef?struct?{??
  • ????ngx_http_upstream_rr_peer_data_t?rrp;?/*?round?robin的per?request負載均衡數(shù)據(jù)?*/??
  • ????ngx_uint_t?hash;?/*?根據(jù)客戶端IP計算所得的hash值?*/??
  • ????u_char?addrlen;?/*?使用客戶端IP的后三個字節(jié)來計算hash值?*/??
  • ????u_char?*addr;?/*?客戶端的IP?*/??
  • ????u_char?tries;?/*?已經(jīng)嘗試了多少次?*/??
  • ????ngx_event_get_peer_pt?get_rr_peer;?/*?round?robin算法的peer.get函數(shù)?*/??
  • }?ngx_http_upstream_ip_hash_peer_data_t;??
  • [java] view plain copy
  • 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;??
  • ??
  • ????/*?創(chuàng)建ip_hash的per?request負載均衡數(shù)據(jù)的實例?*/??
  • ????iphp?=?ngx_palloc(r->pool,?sizeof(ngx_http_upstream_ip_hash_peer_data_t));??
  • ????if?(iphp?==?NULL)??
  • ????????return?NGX_ERROR;??
  • ???
  • ???/*?首先調(diào)用round?robin的per?request負載均衡數(shù)據(jù)的初始化函數(shù),?
  • ????*?創(chuàng)建和初始化round?robin的per?request負載均衡數(shù)據(jù)實例,即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的后三個字節(jié)來計算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函數(shù)?*/??
  • }??
  • ?

    選取一臺后端服務器

    ?

    一般upstream塊中會有多臺后端,那么對于本次請求,要選定哪一臺后端呢?

    這時候第三步中r->upstream->peer.get指向的函數(shù)就派上用場了:

    采用ip_hash算法,從集群中選出一臺后端來處理本次請求。 選定后端的地址保存在pc->sockaddr,pc為主動連接。

    函數(shù)的返回值:

    NGX_DONE:選定一個后端,和該后端的連接已經(jīng)建立。之后會直接發(fā)送請求。

    NGX_OK:選定一個后端,和該后端的連接尚未建立。之后會和后端建立連接。

    NGX_BUSY:所有的后端(包括備份集群)都不可用。之后會給客戶端發(fā)送502(Bad Gateway)。

    [java] view plain copy
  • 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;?/*?請求的負載均衡數(shù)據(jù)?*/??
  • ????time_t?now;??
  • ????ngx_int_t?w;??
  • ????uintptr_t?m;??
  • ????ngx_uint_t?i,?n,?p,?hash;??
  • ????ngx_http_upstream_rr_peer_t?*peer;??
  • ????...??
  • ????/*?如果只有一臺后端,或者嘗試次數(shù)超過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?(?;?;?)?{??
  • ????????/*?根據(jù)客戶端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++;??
  • ????????}??
  • ??
  • ????????/*?檢查第此后端在狀態(tài)位圖中對應的位,為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;??
  • ??
  • ????????/*?在一段時間內(nèi),如果此后端服務器的失敗次數(shù),超過了允許的最大值,那么不允許使用此后端了?*/??
  • ????????if?(peer->max_fails?&&?peer->fails?>=?peer->max_fails?&&??
  • ????????????now?-?peer->checked?<=?peer->fail_timeout)??
  • ????????????goto?next;??
  • ??
  • ????????break;??
  • ??
  • ????next:??
  • ????????/*?增加已嘗試的次數(shù),如果超過20次,則使用輪詢的方式來選取后端?*/??
  • ????????if?(++iphp->tries?>?20)??
  • ????????????return?iphp->get_rr_peer(pc,?&iphp->rrp);??
  • ????}??
  • ??
  • ????iphp->rrp.current?=?peer;?/*?選定的可用后端?*/??
  • ??
  • ????/*?保存選定的后端服務器的地址,之后會向這個地址發(fā)起連接?*/??
  • ????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:??
  • }??
  • 轉載于:https://my.oschina.net/zhangjie830621/blog/653088

    《新程序員》:云原生和全面數(shù)字化實踐50位技術專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

    總結

    以上是生活随笔為你收集整理的Nginx的负载均衡 - 保持会话 (ip_hash)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。