Nginx的负载均衡 - 加权轮询 (Weighted Round Robin) 上篇
Nginx版本:1.9.1
我的博客:http://blog.csdn.net/zhangskd
算法介绍
来看一个简单的Nginx负载均衡配置。
http {
upstream cluster {
server a weight=5;
server b weight=1;
server c weight=1;
} server {
listen 80; location / {
proxy_pass http://cluster;
}
}
}
当在upstream配置块中没有指定使用的负载均衡算法时,默认使用的是加权轮询。
按照上述配置,Nginx每收到7个客户端的请求,会把其中的5个转发给后端a,把其中的1个转发给后端b,
把其中的1个转发给后端c。
这就是所谓的加权轮询,看起来很简单,但是最早使用的加权轮询算法有个问题,就是7个请求对应的
后端序列是这样的:{ c, b, a, a, a, a, a },会有5个连续的请求落在后端a上,分布不太均匀。
目前使用的加权轮询叫做平滑的加权轮询(smooth weighted round-robin balancing),它和前者的区别是:
每7个请求对应的后端序列为 { a, a, b, a, c, a, a },转发给后端a的5个请求现在分散开来,不再是连续的。
摘录此算法的描述:
On each peer selection we increase current_weight of each eligible peer by its weight,
select peer with greatest current_weight and reduce its current_weight by total number
of weight points distributed among peers.
To preserve weight reduction in case of failures the effective_weight variable was introduced,
which usually matches peer's weight, but is reduced temoprarily on peer failures.[1]
每个后端peer都有三个权重变量,先解释下它们的含义。
(1) weight
配置文件中指定的该后端的权重,这个值是固定不变的。
(2) effective_weight
后端的有效权重,初始值为weight。
在释放后端时,如果发现和后端的通信过程中发生了错误,就减小effective_weight。
此后有新的请求过来时,在选取后端的过程中,再逐步增加effective_weight,最终又恢复到weight。
之所以增加这个字段,是为了当后端发生错误时,降低其权重。
(3) current_weight
后端目前的权重,一开始为0,之后会动态调整。那么是怎么个动态调整呢?
每次选取后端时,会遍历集群中所有后端,对于每个后端,让它的current_weight增加它的effective_weight,
同时累加所有后端的effective_weight,保存为total。
如果该后端的current_weight是最大的,就选定这个后端,然后把它的current_weight减去total。
如果该后端没有被选定,那么current_weight不用减小。
弄清了三个weight字段的含义后,加权轮询算法可描述为:
1. 对于每个请求,遍历集群中的所有可用后端,对于每个后端peer执行:
peer->current_weight += peer->effecitve_weight。
同时累加所有peer的effective_weight,保存为total。
2. 从集群中选出current_weight最大的peer,作为本次选定的后端。
3. 对于本次选定的后端,执行:peer->current_weight -= total。
上述描述可能不太直观,来看个例子。
现在使用以下的upstream配置块:
upstream backend {
server a weight=4;
server b weight=2;
server c weight=1;
}
按照这个配置,每7个客户端请求中,a会被选中4次、b会被选中2次、c会被选中1次,且分布平滑。
我们来算算看是不是这样子的。
initial current_weight of a, b, c is { 0, 0, 0 }
通过上述过程,可得以下结论:
1. 7个请求中,a、b、c分别被选取了4、2、1次,符合它们的权重值。
2. 7个请求中,a、b、c被选取的顺序为a, b, a, c, a, b, a,分布均匀,权重大的后端a没有被连续选取。
3. 每经过7个请求后,a、b、c的current_weight又回到初始值{ 0, 0, 0 },因此上述流程是不断循环的。
这个平滑的加权轮询算法背后应该有数学论证,这里就不继续研究了:)
本模块的数据结构
ngx_http_upstream_rr_peer_t
表示一台后端服务器。peer就是对端,指的是上游服务器端。
struct ngx_http_upstream_rr_peer_s {
struct sockaddr *sockaddr; /* 后端服务器的地址 */
socklen_t socklen; /* 地址的长度*/
ngx_str_t name; /* 后端服务器地址的字符串,server.addrs[i].name */
ngx_str_t server; /* server的名称,server.name */ ngx_int_t current_weight; /* 当前的权重,动态调整,初始值为0 */
ngx_int_t effective_weight; /* 有效的权重,会因为失败而降低 */
ngx_int_t weight; /* 配置项指定的权重,固定值 */ ngx_uint_t conns; /* 当前连接数 */ ngx_uint_t fails; /* "一段时间内",已经失败的次数 */
time_t accessed; /* 最近一次失败的时间点 */
time_t checked; /* 用于检查是否超过了"一段时间" */ ngx_uint_t max_fails; /* "一段时间内",最大的失败次数,固定值 */
time_t fail_timeout; /* "一段时间"的值,固定值 */
ngx_uint_t down; /* 服务器永久不可用的标志 */
...
ngx_http_upstream_rr_peer_t *next; /* 指向下一个后端,用于构成链表 */
...
} ngx_http_upstream_rr_peer_t;
ngx_http_upstream_rr_peers_t
表示一组后端服务器,比如一个后端集群。
struct ngx_http_upstream_rr_peers_s {
ngx_uint_t number; /* 后端服务器的数量 */
...
ngx_uint_t total_weight; /* 所有后端服务器权重的累加值 */ unsigned single:1; /* 是否只有一台后端服务器 */
unsigned weighted:1; /* 是否使用权重 */ ngx_str_t *name; /* upstream配置块的名称 */ ngx_http_upstream_rr_peers_t *next; /* backup服务器集群 */
ngx_http_upstream_rr_peer_t *peer; /* 后端服务器组成的链表 */
};
ngx_http_upstream_rr_peer_data_t
保存每个请求的负载均衡数据。
typedef struct {
ngx_http_upstream_rr_peers_t *peers; /* 后端集群 */
ngx_http_upstream_rr_peer_t *current; /* 当前使用的后端服务器 */
uintptr_t *tried; /* 指向后端服务器的位图 */
uintptr_t data; /* 当后端服务器的数量较少时,用于存放其位图 */
} ngx_http_upstream_rr_peer_data_t;
通用的数据结构
以下是所有负载均衡模块都会使用到的一些数据结构。
ngx_http_upstream_server_t
表示upstream配置块中的一条server指令。
typedef struct {
ngx_str_t name; /* 服务器的名称 */
ngx_addr_t *addrs; /* 服务器地址的数组,因为同一个域名可能解析为多个IP */
ngx_uint_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;
server指令
Syntax: server address [parameters];
Context: upstream
Defines the address and other parameters of a server.
The address can be specified as domain name or IP address, with an optional port, or...
If a port is not specified, the port 80 is used. A domain name that resolves to serveral IP
addresses defines multiple servers at once.
server指令支持如下参数
weight = number
sets the weight of the server, by default 1.
max_fails = number
By default, the number of unsuccessful attempts is set to 1.
The zero value disables the accounting of attempts.
fail_timout = number
By default it is set to 10 seconds.
backup
marks the server as a backup server.
down
marks the server as permanently unavailable.
ngx_peer_connection_t
表示本机和后端的连接,也叫主动连接,用于upstream机制。
struct ngx_peer_connection_s {
ngx_connection_t *connection; /* 后端连接 */ struct sockaddr *sockaddr; /* 后端服务器的地址 */
socklen_t socklen; /* 后端服务器地址的长度 */
ngx_str_t *name; /* 后端服务器的名称 */ ngx_uint_t tries; /* 对于一个请求,允许尝试的后端服务器个数 */
ngx_event_get_peer_pt get; /* 负载均衡模块实现,用于选取一个后端服务器 */
ngx_event_free_peer_pt free; /* 负载均衡模块实现,用于释放一个后端服务器 */
void *data; /* 请求的负载均衡数据,一般指向ngx_http_upstream_<name>_peer_data_t */
...
ngx_addr_t *local; /* 本机地址 */
int rcvbuf; /* 套接字接收缓冲区的大小 */
ngx_log_t *log; unsigned cached:1;
unsigned log_error:2;
};
ngx_peer_connection_t *pc;
pc->get 就是负载均衡模块中,用于选取后端服务器的函数。
当选定一台后端服务器时,把它的地址信息保存在pc->sockaddr、pc->socklen、pc->name。
pc->tries表示对于一个请求,最多能尝试多少个后端。当尝试一个后端失败时,会调用pc->free,
一个主要目的就是更新pc->tries,比如pc->tries--。如果pc->tries降到0,就不再尝试了。
在请求的负载均衡数据初始化函数peer.init中,会给该请求创建一个ngx_http_upstream_<name>_peer_data_t实例,
用于保存该请求的负载均衡数据,pc->data就是该实例的地址。
ngx_http_upstream_peer_t
保存upstream块的数据,是负载均衡中一个很重要的结构体。
typedef struct {
/* upstream块的初始化函数,ngx_http_upstream_module创建main配置时调用。
* 针对每个upstream块。
*/
ngx_http_upstream_init_pt init_upstream; /* request在初始化upstream机制时调用,初始化该请求的负载均衡数据。
* 针对每个request。
*/
ngx_http_upstream_init_peer_pt init; void *data; /* 保存upstream块的数据 */
} ngx_http_upstream_peer_t;
upstream块的数据,在解析配置文件时就创建和初始化了。
如果写了一个新的负载均衡模块,则需要在它的指令解析函数中指定init_upstream的值,
用来创建和初始化包含该指令的upstream配置块的数据。
ngx_http_upstream_srv_conf_t
ngx_http_upstream_module的server块。
struct ngx_http_upstream_srv_conf_s {
ngx_http_upstream_peer_t peer; /* upstream块的数据 */
void **srv_conf; /* 所有HTTP模块的server conf */
ngx_array_t *server; /* upstream块的server数组,元素类型为ngx_http_upstream_server_t */
ngx_uint_t flags; /* upstream块的server指令支持的参数 */
ngx_str_t host; /* upstream块的名称 */
u_char *file_name;
ngx_uint_t line;
in_port_t port; /* 使用的端口 */
in_port_t default_port; /* 默认的端口 */
ngx_uint_t no_port;
...
}; #define ngx_http_conf_upstream_srv_conf(uscf, module) uscf->srv_conf[module.ctx_index]
Reference
[1]. https://github.com/phusion/nginx/commit/27e94984486058d73157038f7950a0a36ecc6e35
Nginx的负载均衡 - 加权轮询 (Weighted Round Robin) 上篇的更多相关文章
- Nginx的负载均衡 - 加权轮询 (Weighted Round Robin) 下篇
Nginx版本:1.9.1 我的博客:http://blog.csdn.net/zhangskd 上篇blog讲述了加权轮询算法的原理.以及负载均衡模块中使用的数据结构,接着我们来看看加权轮询算法的具 ...
- 【Nginx】负载均衡-加权轮询策略剖析
转自:江南烟雨 本文介绍的是客户端请求在多个后端服务器之间的均衡,注意与客户端请求在多个nginx进程之间的均衡相区别. 如果Nginx是以反向代理的形式配置运行,那么对请求的实际处理需要转发到后端服 ...
- nginx负载均衡 加权轮询和ip_hash
下面给大家总结了几种真正的nginx负载均衡的功能了,在此我们加了一个权重判断法就是根据nginx负载的状态实现分配访问用户到权重值少的机器了,具体配置如下. nginx为后端web服务器(apach ...
- Nginx 负载均衡-加权轮询策略剖析
本文介绍的是客户端请求在多个后端服务器之间的均衡,注意与客户端请求在多个nginx进程之间的均衡相区别(Nginx根据每个工作进程的当前压力调整它们获取监听套接口的几率,那些当前比较空闲的工作进程有更 ...
- Java实现负载均衡算法--轮询和加权轮询
1.普通轮询算法 轮询(Round Robin,RR)是依次将用户的访问请求,按循环顺序分配到web服务节点上,从1开始到最后一台服务器节点结束,然后再开始新一轮的循环.这种算法简单,但是没有考虑到每 ...
- 负载均衡算法WeightedRoundRobin(加权轮询)简介及算法实现
Nginx的负载均衡默认算法是加权轮询算法,本文简单介绍算法的逻辑,并给出算法的Java实现版本. 本文参考了Nginx的负载均衡 - 加权轮询 (Weighted Round Robin). ...
- 【转】Nginx学习---负载均衡的原理、分类、实现架构,以及使用场景
[原文]https://www.toutiao.com/i6593604356799463944/ [原文]https://www.toutiao.com/i6592741060194075143/ ...
- Dubbo加权轮询负载均衡的源码和Bug,了解一下?
本文是对于Dubbo负载均衡策略之一的加权随机算法的详细分析.从2.6.4版本聊起,该版本在某些情况下存在着比较严重的性能问题.由问题入手,层层深入,了解该算法在Dubbo中的演变过程,读懂它的前世今 ...
- 2017-5-5/PHP实现负载均衡的加权轮询
1. 负载均衡算法有哪些? 轮询法:将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务器实际的连接数和当前的系统负载. 随机法:通过系统的随机算法,根据后端服务器的列表 ...
随机推荐
- JDK和tomcat的安装配置
一.JDK8安装与配置 分别配置如下三个系统变量 JAVA_HOME设置变量值为java JDK的安装目录例如: C:\Program Files\Java\jdk1.8.0 PATH添加变量值 %J ...
- thymeleaf-迭代
迭代 1.迭代基础 要在/WEB-INF/templates/product/list.html页面中显示产品,将使用一个表格.都将显示在一行(一个<tr>元素),所以对于我们的模板,我们 ...
- [HNOI 2011]数学作业
Description 小 C 数学成绩优异,于是老师给小 C 留了一道非常难的数学作业题: 给定正整数 N 和 M,要求计算 Concatenate (1 .. N) Mod M 的值,其中 Con ...
- 【BZOJ4034】【HAOI2015】树上操作
题目请自行查阅传送门. 典型的树剖题,线段树维护操作,记一下子树在线段树内范围即可. 时间复杂度:\( O(m \log^{2} n) \) #include <stdio.h> #def ...
- bzoj2096[Poi2010]Pilots 单调队列
2096: [Poi2010]Pilots Time Limit: 30 Sec Memory Limit: 162 MBSubmit: 983 Solved: 513[Submit][Statu ...
- Unix文件系统的主要特点是什么?
1. 树型层次结构 2. 可安装拆卸的文件系统 3. 文件是无结构的字符流式文件 4. Unix文件系统吧外部设备和文件目录作为文件处理
- 如何在Windows系统中设置Python程序定时运行
文章出处:http://blog.csdn.net/wwy11/article/details/51100432 首先,我们肯定是要用到Windows下的[计划任务]功能 之后点击右侧的[创建基本任务 ...
- Requests库介绍
Requests 是用Python语言编写,基于 urllib,采用 Apache2 Licensed 开源协议的 HTTP 库.它比 urllib 更加方便,可以节约我们大量的工作,完全满足 HTT ...
- 谷歌Chrome浏览器之No Sandbox
想着还是要把这个分享出来,前两天,早上过来,Chrome打开后,输入网址回车,毫无反应,不加载,不跳转,打不开浏览器设置.总之就是除了能打开Chrome浏览器,不能进行任何其他操作,关闭重开也是这样 ...
- angular2+ionic2架构介绍
不要用angular的语法去写angular2,有人说二者就像Java和JavaScript的区别. 1. 项目所用:angular2+ionic2+typescript 2. 项目结构 3. S ...