阅读 653

Nginx系列:负载均衡的算法 | 八月更文挑战

这是我参与8月更文挑战的第27天,活动详情查看:8月更文挑战

前言

心态好了,就没那么累了。心情好了,所见皆是明媚风景。

“一时解决不了的问题,那就利用这个契机,看清自己的局限性,对自己进行一场拨乱反正。”正如老话所说,一念放下,万般自在。如果你正被烦心事扰乱心神,不妨学会断舍离。断掉胡思乱想,社区垃圾情绪,离开负面能量。心态好了,就没那么累了。心情好了,所见皆是明媚风景。

关于负载均衡,我们需要了解,对于要实现高性能集群,选择好负载均衡器很重要,同时针对不同的业务场景选择合适的负载均衡算法也是非常重要的。


一、源地址哈希算法

唯一不丢失策略的算法,但是负载均衡和源数据信息和哈希算法有很大关系。

源地址哈希法的思想是根据服务消费者请求客户端的 IP 地址,通过哈希函数计算得到一个哈希值,将此哈希值和服务器列表的大小进行取模运算,得到的结果便是要访问的服务器地址的序号。采用源地址哈希法进行负载均衡,相同的 IP 客户端,如果服务器列表不变,将映射到同一个后台服务器进行访问。

伪代码:

private static Map<String, Integer> serviceWeightMap = new HashMap<String, Integer>();

static {
    serviceWeightMap.put("192.168.1.100", 1);
    serviceWeightMap.put("192.168.1.101", 1);
    serviceWeightMap.put("192.168.1.102", 4);
    serviceWeightMap.put("192.168.1.103", 1);
   }

   public static String testConsumerHash(String remoteIp) {

       Map<String, Integer> serverMap = new HashMap<String, Integer>();
       serverMap.putAll(serviceWeightMap);

       //取得IP地址list
       Set<String> keySet = serverMap.keySet();
       ArrayList<String> keyList = new ArrayList<String>();
       keyList.addAll(keySet);

       int hashCode = remoteIp.hashCode();
       int pos = hashCode % keyList.size();

       return keyList.get(pos);
   }
复制代码

这段代码其实和它的解释很类似,通过哈希函数计算得到一个哈希值,将此哈希值和服务器列表的大小进行取模运算,得到的结果便是要访问的服务器地址的序号。


二、加权轮询算法

再来看一下加权轮询算法,我们先看一下在 Nginx 里面进行的权重配置:

http {
upstream cluster {
server a weight=1;
server b weight=2;
server c weight=3;
}
复制代码

假如 Nginx 每收到 6 个客户端的请求,会把其中的 1 个转发给后端 a,把其中的 2 个转发给后端 b,把其中的 3 个转发给后端 c。

加权轮询算法的结果,就是要生成一个服务器序列。每当有请求到来时,就依次从该序列中取出下一个服务器用于处理该请求。

加权轮询算法伪代码:

private static Map<String, Integer> serviceWeightMap = new HashMap<String, Integer>();

static {
    serviceWeightMap.put("192.168.1.100", 1);
    serviceWeightMap.put("192.168.1.101", 1);
    serviceWeightMap.put("192.168.1.102", 4);
    serviceWeightMap.put("192.168.1.103", 1);
   }


    public static String testWeightRoundRobin() {

        // 重新创建一个map,避免出现由于服务器上线和下线导致的并发问题
        Map<String, Integer> serverMap = new HashMap<String, Integer>();
        serverMap.putAll(serviceWeightMap);

        //取得IP地址list
        Set<String> keySet = serverMap.keySet();
        Iterator<String> it = keySet.iterator();

        List<String> serverList = new ArrayList<String>();

        while (it.hasNext()) {
            String server = it.next();
            Integer weight = serverMap.get(server);
            for (int i=0; i<weight; i++) {
                serverList.add(server);
            }
        }

        String server = null;

        synchronized (pos) {
            if (pos > serverList.size()) {
                pos = 0;
            }

            server = serverList.get(pos);
            pos++;
        }

        return server;
}
复制代码

其实在 加权轮询算法中,是有缺陷的,在某些特殊的权重下,加权轮询调度会生成不均匀的实例序列,这种不平滑的负载可能会使某些实例出现瞬时高负载的现象,导致系统存在宕机的风险。而为了解决这个调度的缺陷,就有了平滑加权轮询调度。


三、加权随机算法

加权随机法跟加权轮询法类似,根据后台服务器不同的配置和负载情况,配置不同的权重。不同的是,它是按照权重来随机选取服务器的,而非顺序。

private static Map<String, Integer> serviceWeightMap = new HashMap<String, Integer>();

    static {
        serviceWeightMap.put("192.168.1.100", 1);
        serviceWeightMap.put("192.168.1.101", 1);
        serviceWeightMap.put("192.168.1.102", 4);
        serviceWeightMap.put("192.168.1.103", 1);
       }

    public static String testWeightRandom() {
        // 重新创建一个map,避免出现由于服务器上线和下线导致的并发问题
        Map<String, Integer> serverMap = new HashMap<String, Integer>();
        serverMap.putAll(serviceWeightMap);

        //取得IP地址list
        Set<String> keySet = serverMap.keySet();
        List<String> serverList = new ArrayList<String>();
        Iterator<String> it = keySet.iterator();

        while (it.hasNext()) {
            String server = it.next();
            Integer weight = serverMap.get(server);
            for (int i=0; i<weight; i++) {
                serverList.add(server);
            }
        }

        Random random = new Random();
        int randomPos = random.nextInt(serverList.size());

        String server = serverList.get(randomPos);

        return server;
    }
复制代码

这里不同的地方就是服务器是通过随机算法获取。

其实我们可以想一个实例:比如说在以下场景:有一个集合 S,里面比如有 A,B,C,D 这四项。这时我们想随机从中抽取一项,但是抽取的概率不同,比如我们希望抽到 A 的概率是 50%,抽到 B 和 C 的概率是 20%,D 的概率是 10%。一般来说,我们可以给各项附一个权重,抽取的概率正比于这个权重。


四、HTTP 国际化

HTTP 报文中可以承载以任何语言表示的内容,就像它能承载图像、影片,或任何类型的 媒体那样。对 HTTP 来说,实体主体只是二进制信息的容器而已。

为了支持国际性的内容,服务器需要告知客户端每个文档的字母表和语言,这样客户端才 能正确地把文档中的信息解包为字符并把内容呈现给用户。

服务器通过 HTTP 协议的 Content-Type 首部中的 charset 参数和 Content-Language 首部告知客户端文档的字母表和语言。这些首部描述了实体主体的“信息盒子”里面装的是 什么,如何把内容转换成合适的字符以便显示在屏幕上以及里面的词语表示的是哪种语言。

同时,客户端需要告知服务器用户理解何种语言,浏览器上安装了何种字母表编码算法。客户端发送 Accept-Charset 首部和 Accept-Language 首部,告知服务器它理解哪些字 符集编码算法和语言以及其中的优先顺序。

有兴趣的话,大家可以去看《 HTTP 图解》和《HTTP 权威指南》。


🎉总结:

  • 更多参考精彩博文请看这里:《陈永佳的博客》

  • 喜欢博主的小伙伴可以加个关注、点个赞哦,持续更新嘿嘿!

文章分类
后端
文章标签