nginx源码学习 非http服务器的负载均衡实现

负载均衡 
说明:这里只分析源码,具体每种模式工作原理网上有很详细的解释。这里没有分析http容器的负载均衡,只分析了upstream的负载均衡,二者有些许不同。
nginx支持四种复杂均衡模式,与实现文件对应如下:
round robin模式:即循环模式,对应文件ngx_stream_upstream_round_robin.c/.h
least conn模式:即最少连接模式,对应文件ngx_stream_upstream_least_conn_module.c
hash模式:就是哈希模式,对应文件ngx_stream_upstream_hash_module.c
zone模式:域模式,对应文件ngx_stream_upstream_zone_module.c(未完全实现)
只有round robin有头文件,它是最基本的模式。
下面分析各个模式的实现:
1、round robin
ngx_int_t ngx_stream_upstream_init_round_robin(ngx_conf_t *cf, ngx_stream_upstream_srv_conf_t *us);
这是系统默认的负载均衡模式,其他模式都是基于该模式实现,默认配置文件没有参数指明其他模式时,就采用该模式。在ngx_stream_upstream_init_main_conf(ngx_stream_upstream.c 695行)调用。
0)给us.peer.init赋值函数ngx_stream_upstream_init_round_robin_peer,在ngx_stream_proxy_handler(ngx_stream_proxy_module.c 349行)和ngx_http_upstream_init_request(ngx_http_upstream.c 524行)调用;
1)ngx_stream_upstream_server_t数组是否已经创建好;
2)是,取出数组server,遍历它,统计其中非备份服务端数量(n)和权重总和(w);
3)如果n===0,说明没有server,退出;
4)创建ngx_stream_upstream_rr_peers_t结构体peers,保存服务端统计信息,它的peer属性保存了下一步要创建的n个peer的链表;
5)创建n个ngx_stream_upstream_rr_peer_t结构体peer,遍历server,保存每个服务端的详细信息到每个peer;
6)把赋值好的peers保存到入参us中的peer.data属性;
7)遍历server,统计备份服务端,执行类似的4)5)6)步;
8)把赋值好的备份链接作为第四步peers的下一个结点,返回操作成功;
9)如果第二步是否,说明server数组没有创建好,那么proxy_pass隐式创建一个server数组给us;隐式创建的server不包含备份服务端;
10)根据us保存的host和port,解析域名得到若干个服务端地址,执行类似的4)5)6)步;
11)返回成功。


ngx_int_t ngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s,
    ngx_stream_upstream_resolved_t *ur);
在ngx_stream_proxy_handler(ngx_stream_proxy_module.c 349行)中调用
根据ngx_stream_upstream_resolved_t *ur,即主机名解析结果,创建resolved的peers链表,过程和上一个函数基本类似。


ngx_int_t
ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data);
取一个服务端点。在多个地方都有调用,就不一一列举了。主要作用是遍历data(其实是ngx_stream_upstream_rr_peer_data_t *rrp),取出一个可以做负载均衡的服务端(调用ngx_stream_upstream_get_peer)的地址和名字属性,赋值给pc对应属性,由pc负责具体的连接工作。


static ngx_stream_upstream_rr_peer_t *
ngx_stream_upstream_get_peer(ngx_stream_upstream_rr_peer_data_t *rrp)
取出rrp中的一个服务端。
1)获取当前时间,now = ngx_time();
2)遍历rrp中的peer结点
3)测试该peer结点是否tried==1,是则跳过,意思是该结点已经被获取过;
4)测试peer.down,是则跳过,代表该结点已停止服务;
5)测试peer.max_fails peer.fails peer.checked peer.fail_timeout,是则跳过,代表该结点发送错误已达上限,或该结点响应超时;
6)测试peer.max_conns,是则跳过,代表该结点连接已达上限;
7)累加peer权重,即peer->current_weight += peer->effective_weight;如果effective_weight<peer.weight,则effective_weight++,这样可以一直递增该结点权重直到该结点权重上限,当权重足够大时就可以使用该结点;
8)和当前最佳结点做比较,如果当前没有最佳结点,或者最佳结点当前权重小于peer的当前权重,则peer作为最佳结点;
9)继续执行3)-8),循环结束时,即可找到最佳结点best,如果未找到就返回空;
10)设置rrp.current = best,即设置“当前”结点为最佳结点;
11)设置最佳rrp.tried中最佳结点对应为1,表示该结点已被使用;
12)降低最佳结点的权重total值,total的值是已遍历可用结点的有效权重(effective_weight)累加和;
13)设置最佳结点的检查时间为now;
14)返回最佳结点。
该函数其实就是该模式的核心,如果没有配置文件没有设置权重,那么weight effective_weight current_weight一直是0,就简单的循环可用服务器结点,如果有权重,则等差累加,直到权重上限,如果权重上限偏低,则短时间扔不会被选中,可以有效的按权重上限高低分配服务器。


void ngx_stream_upstream_free_round_robin_peer(ngx_peer_connection_t *pc,
    void *data, ngx_uint_t state);
释放一个服务端结点的连接。如果结点发送错误,累加peer.fails,设置accessed checked为now,降低有效权重,如果有效权重小于0,则置为0;如果结点正常,并且访问时间小于检查时间,则fails=0,表示没发生过错误。最后减少peer的连接数1。


2、least conn模式
static char *
ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
主要工作是根据读取的配置生成server结点(ngx_stream_upstream_srv_conf_t),设置server.peer的初始化回调函数,server的标志。


static ngx_int_t
ngx_stream_upstream_init_least_conn(ngx_conf_t *cf,
    ngx_stream_upstream_srv_conf_t *us)
其实是调用了ngx_stream_upstream_init_round_robin来初始化peers链表,区别是peer结点初始化函数赋值是ngx_stream_upstream_init_least_conn_peer,即按照本模式来初始化peer结点。


static ngx_int_t
ngx_stream_upstream_init_least_conn_peer(ngx_stream_session_t *s,
    ngx_stream_upstream_srv_conf_t *us)
其实是调用了ngx_stream_upstream_init_round_robin_peer初始化peer链表,区别是peer.get赋值为函数ngx_stream_upstream_get_least_conn_peer,即用本模式的get函数来获取服务端结点。


static ngx_int_t
ngx_stream_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)
本模式返回结点的核心方法,主题和上一模式相同,这里分析不同点。
上一模式结点是用权重累加的方式找最大权重结点,本模式是用权重乘积的方式,如下:
peer->conns * best->weight < best->conns * peer->weight则为最佳结点,即当前peer的已连接数乘以当前最佳结点的权重,小于当前最佳结点的已连接数乘以当前peer的权重,也就是取结果小者,也即连接数最少模式(其实是加权最少)如果是相等,则many为1,表示有多个合适的结点。如果没有设置权重,即weight=0,则必然竟然many=1的判断。
循环结束后,如果many=1,则回到第一种方式,按权重最大者为最佳结点。


3、hash模式
static ngx_int_t
ngx_stream_upstream_init_hash(ngx_conf_t *cf,
    ngx_stream_upstream_srv_conf_t *us)
和上一个模式相同。


static ngx_int_t
ngx_stream_upstream_init_hash_peer(ngx_stream_session_t *s,
    ngx_stream_upstream_srv_conf_t *us)
和前两个模式不同的地方是,需要创建ngx_stream_upstream_hash_peer_data_t hp结点,用于保存hash值。初始化时hash和rehash都是0。通过ngx_stream_complex_value初始化hp.key,也就是hash的key。


static ngx_int_t
ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)
本模式返回结点的核心方法。本函数hash值计算和http中不同。
data其实是ngx_stream_upstream_hash_peer_data_t *hp。
1)初始化变量hash值,ngx_crc32_init(hash);初始化后,hash=0xffffffff
2)如果rehash>0,调用ngx_crc32_update更新hash值
3)用hp->key更新hash值
4)ngx_crc32_final(hash)结束hash计算
5)hash = (hash >> 16) & 0x7fff;最终hash值
6)累加当前hp.hash += hash;
7)累加hp.rehash++
8)根据当前hp.hash和总权重,计算权重w
9)遍历hp的服务器结点,找到权重比w大的结点作为当前结点


以上就是三种模式(非http服务)的工作流程。

猜你喜欢

转载自blog.csdn.net/blwinner/article/details/56014864
今日推荐