2019独角兽企业重金招聘Python工程师标准>>>

算法介绍

来看一个简单的Nginx负载均衡配置。

  1. http {
  2. upstream cluster {
  3. server a weight=5;
  4. server b weight=1;
  5. server c weight=1;
  6. }
  7. server {
  8. listen 80;
  9. location / {
  10. proxy_pass http://cluster;
  11. }
  12. }
  13. }

当在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就是对端,指的是上游服务器端。

  1. struct ngx_http_upstream_rr_peer_s {
  2. struct sockaddr *sockaddr; /* 后端服务器的地址 */
  3. socklen_t socklen; /* 地址的长度*/
  4. ngx_str_t name; /* 后端服务器地址的字符串,server.addrs[i].name */
  5. ngx_str_t server; /* server的名称,server.name */
  6. ngx_int_t current_weight; /* 当前的权重,动态调整,初始值为0 */
  7. ngx_int_t effective_weight; /* 有效的权重,会因为失败而降低 */
  8. ngx_int_t weight; /* 配置项指定的权重,固定值 */
  9. ngx_uint_t conns; /* 当前连接数 */
  10. ngx_uint_t fails; /* "一段时间内",已经失败的次数 */
  11. time_t accessed; /* 最近一次失败的时间点 */
  12. time_t checked; /* 用于检查是否超过了"一段时间" */
  13. ngx_uint_t max_fails; /* "一段时间内",最大的失败次数,固定值 */
  14. time_t fail_timeout; /* "一段时间"的值,固定值 */
  15. ngx_uint_t down; /* 服务器永久不可用的标志 */
  16. ...
  17. ngx_http_upstream_rr_peer_t *next; /* 指向下一个后端,用于构成链表 */
  18. ...
  19. } ngx_http_upstream_rr_peer_t;

ngx_http_upstream_rr_peers_t

表示一组后端服务器,比如一个后端集群。

  1. struct ngx_http_upstream_rr_peers_s {
  2. ngx_uint_t number; /* 后端服务器的数量 */
  3. ...
  4. ngx_uint_t total_weight; /* 所有后端服务器权重的累加值 */
  5. unsigned single:1; /* 是否只有一台后端服务器 */
  6. unsigned weighted:1; /* 是否使用权重 */
  7. ngx_str_t *name; /* upstream配置块的名称 */
  8. ngx_http_upstream_rr_peers_t *next; /* backup服务器集群 */
  9. ngx_http_upstream_rr_peer_t *peer; /* 后端服务器组成的链表 */
  10. };

ngx_http_upstream_rr_peer_data_t

保存每个请求的负载均衡数据。

  1. typedef struct {
  2. ngx_http_upstream_rr_peers_t *peers; /* 后端集群 */
  3. ngx_http_upstream_rr_peer_t *current; /* 当前使用的后端服务器 */
  4. uintptr_t *tried; /* 指向后端服务器的位图 */
  5. uintptr_t data; /* 当后端服务器的数量较少时,用于存放其位图 */
  6. } ngx_http_upstream_rr_peer_data_t;

通用的数据结构

以下是所有负载均衡模块都会使用到的一些数据结构。

ngx_http_upstream_server_t

表示upstream配置块中的一条server指令。

  1. typedef struct {
  2. ngx_str_t name; /* 服务器的名称 */
  3. ngx_addr_t *addrs; /* 服务器地址的数组,因为同一个域名可能解析为多个IP */
  4. ngx_uint_t naddrs; /* 服务器地址数组的元素个数 */
  5. ngx_uint_t weight; /* 服务器的权重 */
  6. ngx_uint_t max_fails; /* 一段时间内,访问失败的次数超过此值,判定服务器不可用 */
  7. time_t fail_timeout; /* 上述“一段时间”的长度 */
  8. unsigned down:1; /* 服务器不可用的标志 */
  9. unsigned backup:1; /* 服务器为备用的标志 */
  10. } 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机制。

  1. struct ngx_peer_connection_s {
  2. ngx_connection_t *connection; /* 后端连接 */
  3. struct sockaddr *sockaddr; /* 后端服务器的地址 */
  4. socklen_t socklen; /* 后端服务器地址的长度 */
  5. ngx_str_t *name; /* 后端服务器的名称 */
  6. ngx_uint_t tries; /* 对于一个请求,允许尝试的后端服务器个数 */
  7. ngx_event_get_peer_pt get; /* 负载均衡模块实现,用于选取一个后端服务器 */
  8. ngx_event_free_peer_pt free; /* 负载均衡模块实现,用于释放一个后端服务器 */
  9. void *data; /* 请求的负载均衡数据,一般指向ngx_http_upstream_<name>_peer_data_t */
  10. ...
  11. ngx_addr_t *local; /* 本机地址 */
  12. int rcvbuf; /* 套接字接收缓冲区的大小 */
  13. ngx_log_t *log;
  14. unsigned cached:1;
  15. unsigned log_error:2;
  16. };

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块的数据,是负载均衡中一个很重要的结构体。

  1. typedef struct {
  2. /* upstream块的初始化函数,ngx_http_upstream_module创建main配置时调用。
  3. * 针对每个upstream块。
  4. */
  5. ngx_http_upstream_init_pt init_upstream;
  6. /* request在初始化upstream机制时调用,初始化该请求的负载均衡数据。
  7. * 针对每个request。
  8. */
  9. ngx_http_upstream_init_peer_pt init;
  10. void *data; /* 保存upstream块的数据 */
  11. } ngx_http_upstream_peer_t;

upstream块的数据,在解析配置文件时就创建和初始化了。

如果写了一个新的负载均衡模块,则需要在它的指令解析函数中指定init_upstream的值,

用来创建和初始化包含该指令的upstream配置块的数据。

ngx_http_upstream_srv_conf_t

ngx_http_upstream_module的server块。

  1. struct ngx_http_upstream_srv_conf_s {
  2. ngx_http_upstream_peer_t peer; /* upstream块的数据 */
  3. void **srv_conf; /* 所有HTTP模块的server conf */
  4. ngx_array_t *server; /* upstream块的server数组,元素类型为ngx_http_upstream_server_t */
  5. ngx_uint_t flags; /* upstream块的server指令支持的参数 */
  6. ngx_str_t host; /* upstream块的名称 */
  7. u_char *file_name;
  8. ngx_uint_t line;
  9. in_port_t port; /* 使用的端口 */
  10. in_port_t default_port; /* 默认的端口 */
  11. ngx_uint_t no_port;
  12. ...
  13. };
  14. #define ngx_http_conf_upstream_srv_conf(uscf, module) uscf->srv_conf[module.ctx_index]

Reference

[1]. https://github.com/phusion/nginx/commit/27e94984486058d73157038f7950a0a36ecc6e35

转载于:https://my.oschina.net/zhangjie830621/blog/653092

Nginx的负载均衡 - 加权轮询 (Weighted Round Robin) 上篇相关推荐

  1. nginx负载均衡 加权轮询和ip_hash

    下面给大家总结了几种真正的nginx负载均衡的功能了,在此我们加了一个权重判断法就是根据nginx负载的状态实现分配访问用户到权重值少的机器了,具体配置如下. nginx为后端web服务器(apach ...

  2. 负载均衡--加权轮询算法(Weight Round)

    加权轮询算法:不同的后端服务器,在机器的配置和当前系统的负载方面,可能并不相同.因此,它们的抗压能力也不相同.给配置高.负载低的机器配置更高的权重,让其处理更多的请求:给配置低.负载高的机器分配较低的 ...

  3. Nginx学习之十二-负载均衡-加权轮询策略剖析

    本文介绍的是客户端请求在多个后端服务器之间的均衡,注意与客户端请求在多个nginx进程之间的均衡相区别(Nginx根据每个工作进程的当前压力调整它们获取监听套接口的几率,那些当前比较空闲的工作进程有更 ...

  4. 负载均衡算法 — 轮询

    负载均衡算法 - 轮询 目录 概述 简单轮询 加权轮询 平滑加权轮询 1. 概述 在分布式系统中,为了实现负载均衡,必然会涉及到负载调度算法,如 Nginx 和 RPC 服务发现等场景.常见的负载均衡 ...

  5. 负载均衡算法--轮询法(Round Robin)

    在分布式系统中,为了实现系统的高性能.高并发.高可用,在构架中都会进行负载均衡设计,它是分布式系统的核心和中枢,负载均衡的好坏直接影响着整个系统的性能.负载均衡分为软件均衡和硬件均衡两类,比如apac ...

  6. spring boot robin 负载均衡之轮询策略

    轮询策略 robin的负载均衡默认的是轮询策略,假如微服务有A.B 2个节点,第一次请求时,访问A节点,第二次访问B节点,第三次访问A节点..... 实现思路 记录请求的次数为a,微服务节点个数为b, ...

  7. 负载均衡算法-轮询均衡

    ①轮询均衡( Round Robin):每一次来自网络的请求轮流分配给内部服务器.从1至N然后重新开始.此种均衡算法适合于服务器组中的所有服务器都有相同的软.硬件配置并且平均服务请求量相对均衡的情况.

  8. sofa-rpc负载均衡之轮询算法分析(RoundRobin)

    2019独角兽企业重金招聘Python工程师标准>>> 注意:我们分析的sofa-rpc版本是5.4.0. 图1 RoundRobinLoadBalancer的类继承图 1.一般的R ...

  9. Nginx+Tomcat负载均衡、动静分离,4层代理,7层代理

    一:7层反向代理 Nginx 服务器:192.168.52.200:80 Tomcat服务器1:192.168.52.201:80 Tomcat服务器2:192.168.52.108:8080 192 ...

  10. 加权轮询算法PHP,PHP实现负载均衡的加权轮询方法分析

    本文实例讲述了PHP实现负载均衡的加权轮询方法.分享给大家供大家参考,具体如下: 1. 负载均衡算法有哪些? 轮询法:将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端的每一台服务器,而不关心服务 ...

最新文章

  1. Nature:拟南芥根系微生物组的结构
  2. 相机位姿估计2:[应用]实时位姿估计与三维重建相机姿态
  3. MySQL数据库备份之主从同步配置
  4. [算法 笔记]2014年去哪儿网 开发笔试(续)第一题BUG修正
  5. shell 循环删除进程
  6. Bootstrap3 Font Awesome 字体图标带边框的图标
  7. python获取命令行参数,使用getopt获取命令行执行时指定的参数
  8. MFC开发IM-第四篇、mfc 对话框dialog的属性意思
  9. [Es] Rejecting mapping update to [xxx] as the final mapping would have more than 1 type [xxx xxx]
  10. 用libconfig读取配置文件
  11. vue 数组转集合_思想实验:如何在Vue中使localStorage具有响应式?
  12. ie8 html5上传,兼容IE8的file单文件上传(jquery.form+formdata)
  13. 海康/大华实现web直播和回放,也可以直接对接摄像头
  14. python 字符串转字节_【转】python中的字符串和字节串
  15. 百度飞桨—— 车牌识别学习与修改
  16. 基于MATLAB的AM信号调制解调代码,代码)基于MATLAB的AM调制解调系统仿真报告
  17. Excel的文件打开特别慢,xls文件特别大解决一例
  18. jquery可爱的小黑猫
  19. Android 性能优化之线程优化
  20. mysql中的mmr

热门文章

  1. spring mvc实现ajax 分页
  2. .NetCF 绘制半透明效果
  3. css 百分比继承关系的探讨
  4. #Leetcode# 141. Linked List Cycle
  5. 连接虚机中的mysql服务
  6. CoreData数据库版本迁移
  7. aspose.cells 模版
  8. 最土团购程序一些常见的数据库操作
  9. linux平台 oracle 数据库 安装文档
  10. 1990-2000年事务处理流程图和数据流图试题分析