关于负载均衡的一切:总结与思考

关于负载均衡的一切:总结与思考

古人云,不患寡而患不均。

在计算机的世界,这就是大家耳熟能详的负载均衡(load balancing),所谓负载均衡,就是说如果一组计算机节点(或者一组进程)提供相同的(同质的)服务,那么对服务的请求就应该均匀的分摊到这些节点上。负载均衡的前提一定是“provide a single Internet service from multiple servers”, 这些提供服务的节点被称之为server farm、server pool或者backend servers。

这里的服务是广义的,可以是简单的计算,也可能是数据的读取或者存储。负载均衡也不是新事物,这种思想在多核CPU时代就有了,只不过在分布式系统中,负载均衡更是无处不在,这是分布式系统的天然特性决定的,分布式就是利用大量计算机节点完成单个计算机无法完成的计算、存储服务,既然有大量计算机节点,那么均衡的调度就非常重要。

负载均衡的意义在于,让所有节点以最小的代价、最好的状态对外提供服务,这样系统吞吐量最大,性能更高,对于用户而言请求的时间也更小。而且,负载均衡增强了系统的可靠性,最大化降低了单个节点过载、甚至crash的概率。不难想象,如果一个系统绝大部分请求都落在同一个节点上,那么这些请求响应时间都很慢,而且万一节点降级或者崩溃,那么所有请求又会转移到下一个节点,造成雪崩。

事实上,网上有很多文章介绍负载均衡的算法,大多都是大同小异。本文更多的是自己对这些算法的总结与思考。

一分钟了解负载均衡的一切

本章节的标题和内容都来自一分钟了解负载均衡的一切这一篇文章。当然,原文的标题是夸张了点,不过文中列出了在一个大型web网站中各层是如何用到负载均衡的,一目了然。

常见互联网分布式架构如上,分为客户端层、反向代理nginx层、站点层、服务层、数据层。可以看到,每一个下游都有多个上游调用,只需要做到,每一个上游都均匀访问每一个下游,就能实现“将请求/数据【均匀】分摊到多个操作单元上执行”。

(1)【客户端层】到【反向代理层】的负载均衡,是通过“DNS轮询”实现的
(2)【反向代理层】到【站点层】的负载均衡,是通过“nginx”实现的
(3)【站点层】到【服务层】的负载均衡,是通过“服务连接池”实现的
(4)【数据层】的负载均衡,要考虑“数据的均衡”与“请求的均衡”两个点,常见的方式有“按照范围水平切分”与“hash水平切分”。

数据层的负载均衡,在我之前的《带着问题学习分布式系统之数据分片》中有详细介绍。

算法衡量

在我看来,当我们提到一个负载均衡算法,或者具体的应用场景时,应该考虑以下问题

第一,是否意识到不同节点的服务能力是不一样的,比如CPU、内存、网络、地理位置

第二,是否意识到节点的服务能力是动态变化的,高配的机器也有可能由于一些突发原因导致处理速度变得很慢

第三,是否考虑将同一个客户端,或者说同样的请求分发到同一个处理节点,这对于“有状态”的服务非常重要,比如session,比如分布式存储

第四,谁来负责负载均衡,即谁充当负载均衡器(load balancer),balancer本身是否会成为瓶颈

下面会结合具体的算法来考虑这些问题

负载均衡算法

轮询算法(round-robin)

思想很简单,就是提供同质服务的节点逐个对外提供服务,这样能做到绝对的均衡。Python示例代码如下

SERVER_LIST = [

    '10.246.10.1',

    '10.246.10.2',

    '10.246.10.3',

]

def round_robin(server_lst, cur = [0]):

    length = len(server_lst)

    ret = server_lst[cur[0] % length]

    cur[0] = (cur[0] + 1) % length

    return ret

 

可以看到,所有的节点都是以同样的概率提供服务,即没有考虑到节点的差异,也许同样数目的请求,高配的机器CPU才20%,低配的机器CPU已经80%了

加权轮询算法(weight round-robin)

加权轮训算法就是在轮训算法的基础上,考虑到机器的差异性,分配给机器不同的权重,能者多劳。注意,这个权重的分配依赖于请求的类型,比如计算密集型,那就考虑CPU、内存;如果是IO密集型,那就考虑磁盘性能。Python示例代码如下

 

WEIGHT_SERVER_LIST = {

    '10.246.10.1': 1,

    '10.246.10.2': 3,

    '10.246.10.3': 2,

}

 

def weight_round_robin(servers, cur = [0]):

    weighted_list = []

    for k, v in servers.iteritems():

        weighted_list.extend([k] * v)

 

    length = len(weighted_list)

    ret = weighted_list[cur[0] % length]

    cur[0] = (cur[0] + 1) % length

    return ret

 

随机算法(random)

这个就更好理解了,随机选择一个节点服务,按照概率,只要请求数量足够多,那么也能达到绝对均衡的效果。而且实现简单很多

 

def random_choose(server_lst):

    import random

    random.seed()

    return random.choice(server_lst)

 

加权随机算法(random)

如同加权轮训算法至于轮训算法一样,也是在随机的时候引入不同节点的权重,实现也很类似。

def weight_random_choose(servers):

    import random

    random.seed()

    weighted_list = []

    for k, v in servers.iteritems():

        weighted_list.extend([k] * v)

    return random.choice(weighted_list)

 

当然,如果节点列表以及权重变化不大,那么也可以对所有节点归一化,然后按概率区间选择

 

def normalize_servers(servers):

    normalized_servers = {}

    total = sum(servers.values())

    cur_sum = 0

    for k, v in servers.iteritems():

        normalized_servers[k] = 1.0 * (cur_sum + v)

公司简介

 

自1996年以来,公司一直专注于域名注册、虚拟主机、服务器托管、网站建设、电子商务等互联网服务,不断践行"提供企业级解决方案,奉献个性化服务支持"的理念。作为戴尔"授权解决方案提供商",同时提供与公司服务相关联的硬件产品解决方案。
备案号: 豫ICP备05004936号-1

联系方式

地址:河南省郑州市经五路2号

电话:0371-63520088

QQ:76257322

网站:800188.com

电邮:该邮件地址已受到反垃圾邮件插件保护。要显示它需要在浏览器中启用 JavaScript。