详解Nginx轮询算法底层实现的方法

轮询算法简介

在工作中很多人都使用到了nginx,对nginx得配置也是烂熟于心,今天我主要想介绍一下nginx轮询算法得几种底层实现方式。

简单轮询算法

这种算法比较简单,举个例子就是你有三台服务器

第一台服务器 192.168.1.1
第二台服务器 192.168.1.2
第三台服务器 192.168.1.3

第一个请求过来之后默认访问第一台,第二个请求过来访问第二台,第三次请求过来访问第三台,第四次请求过来访问第一台,以此类推。以下是我代码实现简单得算法:

public class SimplePolling {

  /**
   * key是ip
   */
  public static List <String> ipService = new LinkedList <>();
  static {
    ipService.add("192.168.1.1");
    ipService.add("192.168.1.2");
    ipService.add("192.168.1.3");
  }
  public static int pos = 0;
  public static String getIp(){
    if(pos >= ipService.size()){
      //防止索引越界
      pos = 0;
    }
    String ip = ipService.get(pos);
    pos ++;
    return ip;

  }

  public static void main(String[] args) {
    for (int i = 0; i < 4; i++) {
      System.out.println(getIp());

    }
  }
}

模拟执行4次执行结果是

此时如果我有一台服务器性能比较好(比如192.168.1.1),我想让这台服务器处理多一点请求,此时就涉及到了权重得概率,这种算法就不能实现,请看我后面描述的轮询升级版算法。

加权轮询算法

此时我需要把我前面3台服务器都设置权重,比如第一台设置5,第二台设置1,第三台设置1

第一台服务器 192.168.1.1 5
第二台服务器 192.168.1.2 1
第三台服务器 192.168.1.3 1

此时前5个请求都会访问到第一台服务器,第六个请求会访问到第二台服务器,第七个请求会访问到第三台服务器。

以下是我给出的代码案例:

public class WeightPolling {

  /**
   * key是ip,value是权重
   */
  public static Map<String, Integer> ipService = new LinkedHashMap<>();
  static {
    ipService.put("192.168.1.1", 5);
    ipService.put("192.168.1.2", 1);
    ipService.put("192.168.1.3", 1);
  }
  public static int requestId = 0;
  public static int getAndIncrement() {
    return requestId++;
  }

  public static String getIp(){
    //获取总的权重
    int totalWeight =0;
    for (Integer value : ipService.values()) {
      totalWeight+= value;
    }
    //获取当前轮询的值
    int andIncrement = getAndIncrement();
    int pos = andIncrement% totalWeight;
    for (String ip : ipService.keySet()) {
      if(pos < ipService.get(ip)){
        return ip;
      }
      pos -= ipService.get(ip);
    }
    return null;
  }

  public static void main(String[] args) {
    for (int i = 0; i < 7; i++) {
      System.out.println(getIp());
    }
  }

}

此时运行结果是

可以看的第一台服务器执行了5次,后面2台依次执行一次,依次类推。可能你觉得这种算法还不错。其实这种算法有一个缺点是,如果我第一台服务器设置权重过大可能我需要很多次请求都执行到第一台服务器上去,这样的情况分布是不均匀的,会造成某一台服务器压力过大导致崩溃。所以我后面要引入第三种算法来解决这个问题

平滑加权轮询算法

这种算法可能比较复杂,我第一次看也有点不太明白,后面看过相关资料在结合我自己的理解给大家图文解释一下,这里我举例的服务器配置和权重还是和上面一样

请求 当前权重 = 自身权重+选中后当前权重 总权重 当前最大权重 返回的ip 选中后当前权重=当前最大权重-总权重
1 {5,1,1} 7 5 192.168.1.1 {-2,1,1}
2 {3,2,2} 7 3 192.168.1.1 {-4,2,2}
3 {1,3,3} 7 3 192.168.1.2 {1,-4,3}
4 {6,-3,4} 7 6 192.168.1.1 {-1,-3,4}
5 {4,-2,5} 7 5 192.168.1.3 {4,-2,-2}
6 {9,-1,-1} 7 9 192.168.1.1 {2,-1,-1}
7 {7,0,0} 7 7 192.168.1.1 {0,0,0}

由上图可以看出第一台服务器虽然权重设置的是5,但并不是第五次请求过来都是第一台服务器执行,而是分散执行,调度序列是非常均匀的,且第 7 次调度时选中后当前权重又回到 {0, 0, 0},实例的状态同初始状态一致,所以后续可以一直重复调度操作。

可能有的人还不能清楚的明白上一张图表示的含义,我这里大概描述一下:

1.首先总权重不会变,默认就是当前设置的权重之和

2.在第一次请求进来的时候我默认初始化当前权重选中值是{0,0,0},所以当前权重的值就是{5+0,1+0,1+0},这里的5,1,1就是我们前面每台服务器设置的权重。

3.这里我们可以得出第一次请求过来的最大权重是5。然后返回第一台服务器ip

4.然后我们设置选中后当前权重,这里就是当前最大权重减去总权重(5-7),没有选中的权重不变,这时候得到当前权重选中权重的值{5-7,1,1}

5.在第二次请求过来的时候我们延续上面的2,3,4步骤执行.

如果这里还有不懂得我下面会提供我自己用java代码实现的算法:

public class Polling {

  /**
   * key是ip,value是权重
   */
  public static Map <String,Integer> ipService = new LinkedHashMap <>();
  static {
    ipService.put("192.168.1.1",5);
    ipService.put("192.168.1.2",1);
    ipService.put("192.168.1.3",1);
  }
  private static Map<String,Weight> weightMap = new LinkedHashMap <>();

  public static String getIp(){
    //计算总的权重
     int totalWeight = 0;
    for (Integer value : ipService.values()) {
      totalWeight+=value;
    }
    //首先判断weightMap是否为空
    if(weightMap.isEmpty()){
      ipService.forEach((ip,weight)->{
        Weight weights = new Weight(ip, weight,0);
        weightMap.put(ip,weights);
      });
    }
    //给map中得对象设置当前权重
    weightMap.forEach((ip,weight)->{
      weight.setCurrentWeight(weight.getWeight() + weight.getCurrentWeight());
    });

    //判断最大权重是否大于当前权重,如果为空或者小于当前权重,则把当前权重赋值给最大权重
    Weight maxWeight = null;
    for (Weight weight : weightMap.values()) {
      if(maxWeight ==null || weight.getCurrentWeight() > maxWeight.getCurrentWeight()){
        maxWeight = weight;
      }
    }
    //最后把当前最大权重减去总的权重
    maxWeight.setCurrentWeight(maxWeight.getCurrentWeight() - totalWeight);
    //返回
    return maxWeight.getIp();
  }

  public static void main(String[] args) {
    //模拟轮询7次取ip
    for (int i = 0; i < 7; i++) {
      System.out.println(getIp());
    }
  }

}

class Weight{
  /**
   * ip
   */
  private String ip;
  /**
   * 设置得权重
   */
  private int weight;
  /**
   * 当前权重
   */
  private int currentWeight;

  public Weight(String ip, int weight,int currentWeight) {
    this.ip = ip;
    this.weight = weight;
    this.currentWeight = currentWeight;
  }

  public String getIp() {
    return ip;
  }

  public void setIp(String ip) {
    this.ip = ip;
  }

  public int getWeight() {
    return weight;
  }

  public void setWeight(int weight) {
    this.weight = weight;
  }

  public int getCurrentWeight() {
    return currentWeight;
  }

  public void setCurrentWeight(int currentWeight) {
    this.currentWeight = currentWeight;
  }
}

这里代码得执行结果是:

可以看出此处执行结果和表格里描述得结果一致。

总结

可能第三种算法理解起来有点复杂,如果看不懂图表得意思可以先执行下代码,debugger一步步调试后还是很好理解。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C#实现Nginx平滑加权轮询算法

    本文实例为大家分享了C#实现Nginx平滑加权轮询算法的具体代码,供大家参考,具体内容如下 代码很简单,算法很经典! 1. 定义实体类 public struct ServerConfig { //初始权重 public int Weight {get;set;} //当前权重 public int Current {get;set;} //服务名称 public string Name {get;set;} } 2. 算法 public static int NextServerIndex(S

  • 详解Nginx轮询算法底层实现的方法

    轮询算法简介 在工作中很多人都使用到了nginx,对nginx得配置也是烂熟于心,今天我主要想介绍一下nginx轮询算法得几种底层实现方式. 简单轮询算法 这种算法比较简单,举个例子就是你有三台服务器 第一台服务器 192.168.1.1 第二台服务器 192.168.1.2 第三台服务器 192.168.1.3 第一个请求过来之后默认访问第一台,第二个请求过来访问第二台,第三次请求过来访问第三台,第四次请求过来访问第一台,以此类推.以下是我代码实现简单得算法: public class Sim

  • 详解Nginx服务器中配置超时时间的方法

    一.啥时候用到 用来设置请求资源和服务器返回的时间,保证一个请求占用固定时间,超出后报504超时!这样可以保证一个请求占用过长时间. 二.主要参数 使用nginx服务器如果遇到timeou情况时可以如下设置参数,使用fastcgi: fastcgi_connect_timeout 75;  链接 fastcgi_read_timeout 600;   读取 fastcgi_send_timeout 600;   发请求 这两个选项.          fastcgi_read_timeout是指

  • 详解Nginx几种常见实现301重定向方法上的区别

    Nginx下常见的301跳转有以下三种,虽然都能达到同样的目的.但是三种写法上还是有区别的,主要的区别是在正则匹配的性能上. 第一种:使用rewrite指令,通过正则匹配所有的URI后再去掉开头第一个/(反斜线). rewrite ^/(.*)$ https://www.hi-linux.com/$1; 第二种:同样使用rewrite指令,不同的是通过$request_uri变量匹配所有的URI. rewrite ^ https://www.hi-linux.com$request_uri? p

  • 详解nginx服务器绑定域名和设置根目录的方法

    nginx服务器绑定域名以及设置根目录非常方便,首先进入nginx安装目录,然后执行vim conf/nginx.conf打开nginx的配置文件,找到 server { ..... ..... } 这个代码段,这段代码就是用来配置对应站点的,首先我们应该在域名控制面板将域名解析到我们服务器的IP地址,然后绑定才可以生效 首先在我们的代码段中找到server_name这一项然后把后面的域名改成我们要绑定的域名即可 root这一项就是指定的根目录,设置成我们指定的目录即可 如果我们想绑定多个域名怎

  • Java实现平滑加权轮询算法之降权和提权详解

    目录 前言 1.两个关键点 2.代码实现 2.1.服务节点类 2.2.平滑轮询算法降权和提权 3.分析结果 4.结论 前言 上一篇讲了普通轮询.加权轮询的两种实现方式,重点讲了平滑加权轮询算法,并在文末留下了悬念:节点出现分配失败时降低有效权重值:成功时提高有效权重值(但不能大于weight值). 本文在平滑加权轮询算法的基础上讲,还没弄懂的可以看上一篇文章. 现在来模拟实现:平滑加权轮询算法的降权和提权 1.两个关键点 节点宕机时,降低有效权重值: 节点正常时,提高有效权重值(但不能大于wei

  • Go 实现 Nginx 加权轮询算法的方法步骤

    目录 一,Nginx 负载均衡的轮询 (round-robin) 1. nginx 中的配置 2. 简单介绍 3. 特点 4. 实现 (这里使用golang模拟实现) 5. 测试 二,Nginx 负载均衡的加权轮询 (weighted-round-robin) 1. nginx 配置 2. 加权算法简介-特点 3. 算法说明 4. 简单举例 5. 代码实现 6. 测试验证 最近在看一些 getway 相关的资料,发现有关 Nginx 负载均衡的算法有点多,但是有点乱,所以整理下...如有不对地方

  • 详解Nginx 虚拟主机配置的三种方式(基于IP)

    Nginx配置虚拟主机支持3种方式:基于IP的虚拟主机配置,基于端口的虚拟主机配置,基于域名的虚拟主机配置. 详解Nginx 虚拟主机配置的三种方式(基于端口) https://www.jb51.net/article/14977.htm 详解Nginx 虚拟主机配置的三种方式(基于域名) https://www.jb51.net/article/14978.htm 1.基于IP的虚拟主机配置 如果同一台服务器有多个IP,可以使用基于IP的虚机主机配置,将不同的服务绑定在不同的IP上. 1.1

  • 详解 Nginx 负载均衡和反向代理配置和优化

    Nginx 负载均衡和反向代理配置和优化 DNS 轮询方式: 介绍: DNS 轮询是指一个域名可以绑定到多个的 ip 服务器上, 用户在访问的时候 dns轮询访问这几个 ip 的服务器, 达到负载均衡的目的. 可以使用 linux 命令 dig domain 来查看情况. 缺点: 1. 可靠性低. 如果某一个服务器宕机了, 那么dns 在轮询到这个服务器的话是不会有响应的,即使去掉此 ip , 那么个电信服务商的 dns 是存在缓存, 在一定的时间内也是可以访问到此服务器的.尽管在一定程度上解决

  • 详解nginx配置location总结及rewrite规则写法

    location正则写法 一个示例: location = / { # 精确匹配 / ,主机名后面不能带任何字符串 [ configuration A ] } location / { # 因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求 # 但是正则和最长字符串会优先匹配 [ configuration B ] } location /documents/ { # 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索 # 只有后面的正则表达式没有匹配到时,这

随机推荐