负载均衡

在分布式的环境下,单个模块往往会部署多个,当调用方通过注册发现组件获得了被调用方的网络地址时,会获得多个地址,这时候就要通过负载均衡组件来确定是调用哪一个具体的组件。

我们可以根据负载均衡策略是否关心请求中携带的信息,即请求是否有状态,将负载均衡策略分为无状态的负载均衡、半状态的负 载均衡和全状态的负载均衡。

负载均衡的关键点

负载均衡需要考虑到各个实例性能差异的情况, 让每一个实例都能充分发挥它的能力,不要出现一些实例负载比较高,而另一些实例的负载却 非常低的情况,这样会造成资源浪费。所以负载均衡的第一个关键点是公平性。即要关注被调用服务组之间的公平性,不能旱的旱死,涝的涝死。

负载均衡需要确保外部对后端服务的请求,一定能被路由到可以提供正确服务的实例上。如果后端是有状态的,那么我们就要考虑在请求上携带状态信息,然后根据状态将请求发送到对应的路由上,所以第二个关键点是正确性,即对于有状态的服务来说,负载均 衡需要关心请求的状态,将请求调度到能处理它的后端实例上,不要出现不能处理和错误处理 的情况。

无状态的负载均衡

无状态的负载均衡指的是所有后端实例都是对等的,不管请求发送到哪个实力上,都会得到正确的结果,所以无状态的负载均衡不需要关心请求的状态。

如果这些无状态的实例需要处理像存储数据这种状态,则需要将这些状态信息都交由一个中心存储负责,比如MySQL或者Redis,他们不会在本地磁盘存储任何状态信息。以下是两种具体的方案

轮询

轮询的负载均衡策略非常简单,只需要将请求按顺序分配给多个实例,不用再做其他的处理。轮询在路由时,不利用请求的状态信息,属于无状态的负载均衡策略,所以它不能用于有状态 实例的负载均衡器。

权重轮询

权重轮询的负载均衡策略是将每一个后端实例分配一个权重,分配请求的数量和实例的权重成 正比轮询。例如有两个实例 A,B,假设我们设置 A 的权重为 20,B 的权重为 80,那么负载 均衡会将 20% 的请求数量分配给 A,80 % 的请求数量分配给 B。

半状态的负载均衡

半状态的负载均衡指的是,虽然负载均衡策略利用请求的状态信息进行路由,但是仅仅进行简单的规则处理,比如 Hash 运算加求模来路由请求,它不保证路由的正确性,这个正确性由后端实例来保证。

一些实例会在内存中存储一些状态数据来提升系统性能,如果一个请求被分配到错误的路由中,可以通过中心存储来读取所需要的数据。

半状态的负载均衡将请求按一定的策略进行路由,后端实例可以利用路 由规则来进行优化。

Hash

Hash 负载均衡策略是指将请求的状态信息,按一定的 Hash 算法固定分配到一个实例上,例 如,按请求的来源 IP 地址或者用户的 ID,将同一个来源 IP 地址或者用户 ID 的请求固定到一 个实例上。

存在的问题:如果机器数量发生改变,则请求和实例的分配关系则会发生变化,影响正确性。

一致性Hash

一致性 Hash可以解决Hash模式下的问题。

假设Hash环的大小为2^32,那么我们先将0~2^32均匀的分布在Hash环上,然后将所有的实例按照唯一标识来计算他们在环上的位置。

对于每个请求,也采用同样的方式,都是对2^32取模,然后计算位置。如果该位置没有节点,那么就顺时针往下走,知道找到第一个有节点的位置,该请求就交由该节点执行。

这样的好处是始终对2^32取模,不管有多少个节点变动,始终不影响结果。

全状态的负载均衡

全状态的负载均衡是指,负载均衡策略不仅利用请求的状态信息进行路由,并且在后端实例有 状态的情况下,依然会保证路由的正确性。

全状态的负载均衡一般以路由服务的形式存在,在路由服务里面,都会存储后端实例 ID 和状 态信息的索引,在进行请求路由的时候,路由服务从请求的状态信息中获得索引的标识,通过 查询索引获得后端实例的 ID,然后再进行路由。

参考

《深入浅出分布式技术原理》