配置解析阶段
init_main阶段
在init_upstream时,因为upstream的每个server在DNS后可能存在多个IP,所以init_upstream生成的peer是所有解析结果的集合,例如,一个server的权值为10,解析后由5个IP,那么,将申请5个rrp,每个的权值都是10。
调用kcf->original_init_upstream(ngx_http_upstream_init_round_robin):
生成ngx_http_upstream_rr_peers_t结构体,全局,根据所有后端服务器信息,生成一张表,包含地址信息、权重、最大连接数、最大失败数等。
调用uscf->peer.init_upstream(ngx_http_upstream_init_keepalive):
初始化cache和free队列,其中cache保存已建立的连接,全局,free保存已释放的连接,申请max_cached个节点,挂在free队列。
连接初始化阶段
调用kcf->original_init_peer(ngx_http_upstream_init_round_robin_peer):
初始化ngx_http_upstream_rr_peer_data_t结构体(r->upstream->peer.data,最终保存在kp->data),请求私有数据,用户保存所有可用后端peers、目前选择后端current、后端是否连接过的位图,初始化get和free方法。
typedef struct {
ngx_uint_t config;
ngx_http_upstream_rr_peers_t *peers; //指向全局的rrps
ngx_http_upstream_rr_peer_t *current; //目前使用的rrp
uintptr_t *tried; //位图指针,位数为rrp的个数,代表该连接是否尝试过这个rrp
uintptr_t data; //位图
} ngx_http_upstream_rr_peer_data_t;
调用uscf->peer.init(ngx_http_upstream_init_keepalive_peer)
初始化ngx_http_upstream_keepalive_peer_data_t结构体(kp)(r->upstream->peer.data),请求私有数据,初始化get和free方法。
typedef struct {
ngx_http_upstream_keepalive_srv_conf_t *conf; //配置
ngx_http_upstream_t *upstream; //保存连接的upstream结构体
void *data; //保存ngx_http_upstream_rr_peer_data_t
ngx_event_get_peer_pt original_get_peer;
ngx_event_free_peer_pt original_free_peer;
} ngx_http_upstream_keepalive_peer_data_t;
连接获取阶段
调用kp->original_get_peer(ngx_http_upstream_get_round_robin_peer):
(1)获取最优后端;
(2)当最优后端获取失败时,尝试从backup中获取后端。
struct ngx_http_upstream_rr_peer_s {
ngx_str_t server; //解析前域名
ngx_int_t current_weight; //该rrp当前权值
ngx_int_t effective_weight; //该rrp当前有效权值
ngx_int_t weight; //定权值
ngx_uint_t conns; //该rrp目前连接数
ngx_uint_t max_conns; //该rrp最大连接数
ngx_uint_t fails; //“一段时间”内失败次数
time_t accessed; //最近一次失败时间
time_t checked; //用于检查是否超过了“一段时间”,上一次判断时间
ngx_uint_t max_fails; //最大失败次数
time_t fail_timeout; //“一段时间”
ngx_uint_t down; //该server不参与负载均衡
};
round_robin的负载均衡算法:A、B、C三个rrp,weight分别为1、2、3,那么初始effective_weight也是1、2、3,current_weight初始为0,当进行一次负载均衡算法后,current_weight分别为1、2、3,C的current_weight最大为3,此时将C选为best,并将C的current_weight
= 3 - (1 + 2 + 3) = -3,那么后续的负载均衡,即使每次给C加上current_weight = 3,其current_weight也不是所有rrp中最大值。可以理解为current_weight是一个排序好的队列,每次选取best都是取队列头,同时减小其current_weight并重新排序。当某个rrp后端连接失败时,其effective_weight也会被减小。
当fails大于max_fails时,该rrp将不可用,直到不可用持续时间超过fail_timeout。另外,每worker都拥有自己的rrps内存空间,即不使用共享内存,所以类似current_weight、fails数据每worker都不相同,且keepalive指定的数量为每worker数量。
调用r->upstream->peer.get(ngx_http_upstream_get_keepalive_peer):
(1)从cache队列中查找最优后端是否有已经建立的连接。
连接释放阶段
调用r->upstream->peer.free(ngx_http_upstream_free_keepalive_peer):
(1)如果free队列为空,那么从cache中获取队尾节点,用于存储该连接,原节点中缓存的连接将被释放。注意,这里keepalive节点数并不是该upstream支持的连接上限,只是已建立的、未被使用的上游连接缓存数。
(2)如果free队列不为空。则从free队列获取节点。
调用kp->original_free_peer(ngx_http_upstream_free_round_robin_peer):
(1)如果该后端peer在连接时失败,则修改peer的部分参数值,包括失败次数fails、有效权重effective_weight等;
(2)减少peer中连接个数conns。