面试官:负载均衡的算法你了解不?

上一篇文章我讲了关于负载均衡的三种算法,轮询法,随机法,最小连接法,这三种负载均衡的算法,但是关于负载均衡还有其他的算法,我们也需要你去看,而且在面试的过程中,很有可能是会问到的呦。

对于要实现高性能集群,选择好负载均衡器很重要,同时针对不同的业务场景选择合适的负载均衡算法也是非常重要的。之前已经罗列出几个了,接下来在说剩下的几个算法,

1. 源地址哈希算法

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

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

还是之前的操作,伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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);
   }
   

这段代码来自Will.Shun所写,我当时看到的时候也不是很明白什么意思,后来看了一下,其实和它的解释很类似,通过哈希函数计算得到一个哈希值,将此哈希值和服务器列表的大小进行取模运算,得到的结果便是要访问的服务器地址的序号。

2. 加权轮询算法

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

1
2
3
4
5
6
7
http {
upstream cluster {
server a weight=1;
server b weight=2;
server c weight=3;
}

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

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

加权轮训算法伪代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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;
}

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

3. 加权随机算法

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    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%。一般来说,我们可以给各项附一个权重,抽取的概率正比于这个权重。

4.HTTP国际化

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

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

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

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

关于 HTTP 我就给大家说这么多了,如果又兴趣的话,大家可以去看 《 HTTP图解》,《HTTP权威指南》。

我是懿,一个正在被打击却努力前进的码农。

Java Geek Tech wechat
欢迎订阅 Java 极客技术,这里分享关于 Java 的一切。