负载均衡策略
nginx的负载均衡策略可以划分为两大类:内置策略和扩展策略。内置策略包含加权轮询和ip hash,在默认情况下这两种策略会编译进nginx内核,只需在nginx配置中指明参数即可。扩展策略有很多,如fair、通用hash、consistent hash等,默认不编译进nginx内核,是第三方模块。
nginx 的 upstream目前支持 4 种方式的分配 :
1)轮询(默认)
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。
2)weight
指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。
2)ip_hash
每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。
3)fair(第三方)
按后端服务器的响应时间来分配请求,响应时间短的优先分配。
4)url_hash(第三方)
Nginx默认采用round_robin加权算法。如果要选择其他的负载均衡算法,必须在upstream的配置上下文中通过配置指令ip_hash明确指定(该配置项最好放在其他server指令等的前面,以便检查server的配置选项是否合理)。比如采用Ip_hash的upstream配置如下所示:
- upstream load_balance{
- ip_hash;
- server localhost:8001;
- server localhost:8002;
- }
Load-blance模块中4个关键回调函数:
表1
回调指针
函数功能
round_robin模块
IP_hash模块
uscf->peer.init_upstream
解析配置文件过程中调用,根据upstream里各个server配置项做初始准备工作,另外的核心工作是设置回调指针us->peer.init。配置文件解析完后不再被调用
ngx_http_upstream_init_round_robin
设置:us->peer.init = ngx_http_upstream_init_round_robin_peer;
ngx_http_upstream_init_ip_hash
设置:us->peer.init = ngx_http_upstream_init_ip_hash_peer;
us->peer.init
在每一次Nginx准备转发客户端请求到后端服务器前都会调用该函数。该函数为本次转发选择合适的后端服务器做初始准备工作,另外的核心工作是设置回调指针r->upstream->peer.get和r->upstream->peer.free等
ngx_http_upstream_init_round_robin_peer
设置:r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;
r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;
ngx_http_upstream_init_ip_hash_peer
设置:r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer;
r->upstream->peer.free为空
r->upstream->peer.get
在每一次Nginx准备转发客户端请求到后端服务器前都会调用该函数。该函数实现具体的位本次转发选择合适的后端服务器的算法逻辑,即完成选择获取合适后端服务器的功能
ngx_http_upstream_get_round_robin_peer
加权选择当前权值最高的后端服务器
ngx_http_upstream_get_ip_hash_peer
根据IP哈希值选择后端服务器
r->upstream->peer.free
在每一次Nginx完成与后端服务器之间的交互后都会调用该函数。
ngx_http_upstream_free_round_robin_peer
更新相关数值,比如rrp->current
回调指针 |
函数功能 |
round_robin模块 |
IP_hash模块 |
uscf->peer.init_upstream |
解析配置文件过程中调用,根据upstream里各个server配置项做初始准备工作,另外的核心工作是设置回调指针us->peer.init。配置文件解析完后不再被调用 |
ngx_http_upstream_init_round_robin 设置:us->peer.init = ngx_http_upstream_init_round_robin_peer; |
ngx_http_upstream_init_ip_hash 设置:us->peer.init = ngx_http_upstream_init_ip_hash_peer; |
us->peer.init |
在每一次Nginx准备转发客户端请求到后端服务器前都会调用该函数。该函数为本次转发选择合适的后端服务器做初始准备工作,另外的核心工作是设置回调指针r->upstream->peer.get和r->upstream->peer.free等 |
ngx_http_upstream_init_round_robin_peer 设置:r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer; r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer; |
ngx_http_upstream_init_ip_hash_peer 设置:r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer; r->upstream->peer.free为空 |
r->upstream->peer.get |
在每一次Nginx准备转发客户端请求到后端服务器前都会调用该函数。该函数实现具体的位本次转发选择合适的后端服务器的算法逻辑,即完成选择获取合适后端服务器的功能 |
ngx_http_upstream_get_round_robin_peer 加权选择当前权值最高的后端服务器 |
ngx_http_upstream_get_ip_hash_peer 根据IP哈希值选择后端服务器 |
r->upstream->peer.free |
在每一次Nginx完成与后端服务器之间的交互后都会调用该函数。 |
ngx_http_upstream_free_round_robin_peer 更新相关数值,比如rrp->current |
- for (i = 0; i < umcf->upstreams.nelts; i++) {
- init = uscfp[i]->peer.init_upstream ? uscfp[i]->peer.init_upstream:
- ngx_http_upstream_init_round_robin;
- if (init(cf, uscfp[i]) != NGX_OK) {
- return NGX_CONF_ERROR;
- }
- }
加权轮询策略
全局准备工作
- typedef struct {
- ngx_addr_t *addrs;//指向存储IP地址的数组的指针,host信息(对应的是 ngx_url_t->addrs )
- ngx_uint_t naddrs;//与第一个参数配合使用,数组元素个数(对应的是 ngx_url_t->naddrs )
- ngx_uint_t weight;
- ngx_uint_t max_fails;
- time_t fail_timeout;
- unsigned down:1;
- unsigned backup:1;
- } ngx_http_upstream_server_t;
- us->peer.init = ngx_http_upstream_init_round_robin_peer;
us类型是ngx_http_upstream_srv_conf_t:
- typedef struct ngx_http_upstream_srv_conf_s ngx_http_upstream_srv_conf_t;
- struct ngx_http_upstream_srv_conf_s {
- ngx_http_upstream_peer_t peer;
- void **srv_conf;//在 ngx_http_upstream()函数中被设置,指向的是本层的srv_conf
- ngx_array_t *servers; /*array of ngx_http_upstream_server_t */
- ngx_uint_t flags;//调用函数时ngx_http_upstream_add() 指定的标记
- ngx_str_t host;//在函数 ngx_http_upstream_add() 中设置(e.g. upstream backend中的backend)
- u_char *file_name;//"/usr/local/nginx/conf/nginx.conf"
- ngx_uint_t line;//proxy在配置文件中的行号
- in_port_t port;//使用的端口号(ngx_http_upstream_add()函数中添加, 指向ngx_url_t-->port,通常在函数ngx_parse_inet_url()中解析)
- in_port_t default_port;//默认使用的端口号(ngx_http_upstream_add()函数中添加, 指向ngx_url_t-->default_port)
- ngx_uint_t no_port; /* unsigned no_port:1 */
- };
- typedef struct {
- //使用负载均衡的类型,默认采用 ngx_http_upstream_init_round_robin()
- ngx_http_upstream_init_pt init_upstream;
- //使用的负载均衡类型的初始化函数
- ngx_http_upstream_init_peer_pt init;
- //us->peer.data = peers; 指向的是 ngx_http_upstream_rr_peers_t(函数 ngx_http_upstream_init_round_robin()中设置)
- void *data;
- } ngx_http_upstream_peer_t;
ngx_http_upstream_init_peer_pt 是函数指针类型:
- typedef ngx_int_t (*ngx_http_upstream_init_peer_pt)(ngx_http_request_t *r,
- ngx_http_upstream_srv_conf_t *us);
- typedef struct {
- ngx_str_t url; //保存IP地址+端口信息(e.g. 192.168.124.129:8011 或 money.163.com)
- ngx_str_t host; //保存IP地址信息
- ngx_str_t port_text; //保存port字符串
- ngx_str_t uri; //uri部分,在函数ngx_parse_inet_url()中设置
- in_port_t port; //端口,e.g. listen指令中指定的端口(listen 192.168.124.129:8011)
- in_port_t default_port; //默认端口(当no_port字段为真时,将默认端口赋值给port字段, 默认端口通常是80)
- int family; //address family, AF_xxx
- unsigned listen:1; //是否为指监听类的设置
- unsigned uri_part:1;
- unsigned no_resolve:1; //根据情况决定是否解析域名(将域名解析到IP地址)
- unsigned one_addr:1; //等于1时,仅有一个IP地址
- unsigned no_port:1; //标识url中没有显示指定端口(为1时没有指定)
- unsigned wildcard:1; //标识是否使用通配符(e.g. listen *:8000;)
- socklen_t socklen; //sizeof(struct sockaddr_in)
- u_char sockaddr[NGX_SOCKADDRLEN]; //sockaddr_in结构指向它
- ngx_addr_t *addrs; //数组大小是naddrs字段;每个元素对应域名的IP地址信息(struct sockaddr_in),在函数中赋值(ngx_inet_resolve_host())
- ngx_uint_t naddrs; //url对应的IP地址个数,IP格式的地址将默认为1
- char *err; //错误信息字符串
- } ngx_url_t;
此函数会创建后端服务器列表,并且将非后备服务器与后备服务器分开进行各自单独的链表。每一个后端服务器用一个结构体ngx_http_upstream_rr_peer_t与之对应(ngx_http_upstream_round_robin.h):
- typedef struct {
- struct sockaddr *sockaddr;//后端服务器地址
- socklen_t socklen;//后端服务器地址长度
- ngx_str_t name;//后端名称
- ngx_int_t current_weight;//当前权重,nginx会在运行过程中调整此权重
- ngx_int_t effective_weight;
- ngx_int_t weight;//配置的权重
- ngx_uint_t fails;//已尝试失败次数
- time_t accessed;//检测失败时间,用于计算超时
- time_t checked;
- ngx_uint_t max_fails;//最大失败次数
- time_t fail_timeout;//多长时间内出现max_fails次失败便认为后端down掉了
- ngx_uint_t down; /* unsigned down:1; *///指定某后端是否挂了
- #if (NGX_HTTP_SSL)
- ngx_ssl_session_t *ssl_session; /* local to a process */
- #endif
- } ngx_http_upstream_rr_peer_t;
列表最前面需要带有一些head信息,用结构体ngx_http_upstream_rr_peers_t与之对应:
- typedef struct ngx_http_upstream_rr_peers_s ngx_http_upstream_rr_peers_t;
- struct ngx_http_upstream_rr_peers_s {