【Dubbo】Dubbo 实现服务治理 --负载均衡、集群容错、服务降级

在前面的文章我们介绍了如何使用 Dubbo 进行服务间远程通信,本篇我们就来看看 Dubbo 的另一核心功能 – 服务治理。

1.负载均衡

到目前为止,dubbo 集成 zookeeper 解决了服务注册以及服务动态感知的问题。那么当服务端存在多个节点的集群时,zookeeper 上会维护不同集群节点,对于客户端而言,他需要一种负载均衡机制来实现目标服务的请求负载。

  • 通过负载均衡,可以让每个服务器节点获得适合自己处理能力的负载
  • 负载均衡可以分为软件负载和硬件负载
    • 在实际开发中,我们基础软件负载比较多, 比如nginx
    • 硬件负载现在用得比较少而且有专门的人来维护

Dubbo 里面默认就集成了负载均衡的算法和实现,默认提供了4中负载均衡实现。

1.1 负载均衡算法

RoundRobinLoadBalance

加权轮询算法

所谓轮询是指将请求轮流分配给每台服务器。举个例子,有三台服务器 A、 B、C。 将第一个请求分配给服务器 A,第二个请求分配给服务器 B,第三个请求分配给服务器 C,第四个请求再次分配给服务器 A。这个过程就叫做轮询。轮询是一种无状态负载均衡算法,实现简单,适用于每台服务器性能相近的场景下。

但现实情况下, 我们并不能保证每台服务器性能均相近。如果我们将等量的请求分配给性能较差的服务器,这显然是不合理的。因此,这个时候我们需要对轮询过程进行加权,以调控每台服务器的负载。

经过加权后,每台服务器能够得到的请求数比例,接近或等于他们的权重比。比如服务器 A、B、C 权重比为 5:2:1。那么在 8 次请求中,服务器 A 将 收到其中的 5 次请求,服务器 B 会收到其中的 2 次请求,服务器 C 则收到其中的 1次请求

RandomLoadBalance

权重随机算法:根据权重值进行随机负载

它的算法思想很简单。假设我们有一组服务器 servers = [A, B, C],他们对应的权重为 weights = [5, 3, 2],权重总和10

  1. 把这些权重值平铺在一维坐标值上,[0, 5) 区间属于服务器 A,[5, 8) 区间属于服务器 B,[8, 10) 区间属于服务器 C
  2. 通过随机数生成器生成一个范围在 [0, 10) 之间的随机数,然后计算这个随机数会落到哪个区间上
  3. 比如数字3会落到服务器 A 对应的区间上,此时返回服务器 A 即可

权重越大的机器,在坐标轴上对应的区间范围就越大,因此随机数生成器生成的数字就会有更大的概率落到此区间内。只要随机数生成器产生的随机数分布性很好,在经过多次选择后,每个服务器被选中的次数比例接近其权重比例

LeastActiveLoadBalance

最少活跃调用数算法:活跃调用数越小,表明该服务提供者效率越高,单位时间内可处理更多的请求这个是比较科学的负载均衡算法。

每个服务提供者对应一个活跃数 active。

  1. 初始情况下,所有服务提供者活跃数均为0。
  2. 每收到一个请求,活跃数加 1,完成请求后则将活跃数减 1。
  3. 在服务运行一段时间后, 性能好的服务提供者处理请求的速度更快,因此活跃数下降的也越快,此时这样的服务提供者能够优先获取到新的服务请求

ConsistentHashLoadBalance

hash一致性算法:相同参数的请求总是发到同一提供者

当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者, 不会引起剧烈变动

1.2 Dubbo配置

配置的的负载均衡算法名称如下:random、roundrobin、leastactive、consistenthash既可以在服务端配置,也可以在客户端配置负载均衡策略,若两者都配置了则以客户端为准。

负载均衡是客户端访问时发生,那为什么还能在服务端配置?因为客户端访问的 url 中包含了所有配置信息,包括服务端配置的负载均衡策略)

  • xml形式配置
<!-- Provider发布服务时配置 -->
<dubbo:service interface="..." loadbalance="roundrobin" /> 

<!-- Consumer远程调用服务时配置 -->
<dubbo:reference interface="..." loadbalance="roundrobin" /> 
  • 注解形式
@Service(loadbalance = "roundrobin") 
public class HelloServiceImpl implements IHelloService

@Reference(loadbalance = "random") 
IHelloService helloService; 

2.集群容错

在分布式网络通信中,容错能力是必须要具备的,什么叫容错呢? 从字面意思来看: 容:是容忍, 错:是错误。 就是容忍错误的能力。

我们知道网络通信会有很多不确定因素,比如网络延迟、网络中断、服务异常等,会造成当前这次请求出现失败。 当服务通信出现这个问题时,需要采取一定的措施应对。 而 Dubbo 中提供了容错机制来优雅处理这种错误,在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。

2.1 容错策略

Failover Cluster

失败自动切换,当出现失败,重试其它服务器。(缺省)

  • 通常用于读操作,但重试会带来更长延迟。
  • 可通过 retries=“2” 来设置重试次数(不含第一次),因此一般尝试的次数是3。

Failfast Cluster

快速失败,只发起一次调用,失败立即报错。 通常用于非幂等性的写操作,比如新增记录。

Failsafe Cluster

失败安全,出现异常时,直接忽略。 通常用于写入审计日志等操作。

Failback Cluster

失败自动恢复,后台记录失败请求,定时重发。 通常用于消息通知操作。

Forking Cluster

并行调用多个服务器,只要一个成功即返回。

  • 通常用于实时性要求较高的读操作,但需要浪费更多服务资源。
  • 可通过 forks=“2” 来设置最大并行数。

Broadcast Cluster

广播调用所有提供者,逐个调用,任意一台报错则报错。(2.1.0开始支持) 通常用于通知所有提供者更新缓存或日志等本地资源信息。

=> 在实际应用中:

  • 查询语句容错策略建议使用默认 Failover Cluster。建议在设计接口时候把查询接口方法单独做一个接口提供查询。
  • 增删改建议使用 Failfast Cluster 或者 使用 Failover Cluster(retries=”0”) 策略防止出现数据 重复 添加等等问题

2.2 Dubbo配置

一般配置在服务端,通过cluster属性配置

@Service(loadbalance = "random", cluster = "failsafe") 

3.服务降级

当某个非关键服务出现错误时,可以通过降级功能来临时屏蔽这个服务。

3.1 降级方案

降级可以有几个层面的分类: 自动降级和人工降级; 按照功能可以分为:读服务降级和写服务降级;

  1. 人工降级
    • 对一些非核心服务进行人工降级
    • 在大促之前通过降级开关关闭哪些推荐内容、评价等对主流程没有影响的功能
  2. 故障降级
    • 比如调用的远程服务挂了,网络故障、或者 RPC 服务返回异常。 那么可以直接降级
    • 降级的方案比如设置默认值、采用兜底数据(系统推荐的行为广告挂了,可以提前准备静态页面做返回)等等
  3. 限流降级
    • 在秒杀这种流量比较集中并且流量特别大的情况下,因为突发访问量特别大可能会导致系统支撑不了。这个时候可以采用限流来限制访问量。
    • 当达到阀值时,后续的请求被降级,比如进入排队页面,比如跳转到错误页(活动太火爆,稍后重试等)

PS:容错与降级都是异常处理,区别在于对象不同:容错一般用于服务间调用,降级一般用于客户端请求服务端

3.2 Dubbo配置

Dubbo中提供了一个mock的配置,可以通过 mock来实现当服务提供方出现网络异常或者挂掉以后,客户端不抛出异常,而是通过 Mock数据返回自定义的数据

// 相当于重新写了个预备服务Bean
public class MockSayHelloService implements IHelloService { 
    @Override 
    public String sayHello() { 
        return "Sorry, 服务端发生异常,被降级啦!"; 
    } 
} 

修改客户端的注解,增加mock配置

@Reference( 
        mock = "com.springboot.practice.springbootdubboclient.MockSayHelloService", // 降级策略
        timeout =1000, // 超时为1s,便于模拟失败场景
        cluster = "failfast")  // 默认的failover会发起3次重试,等待时间长
private IHelloService helloService; 

4.几个问题

4.1 启动时检查

Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check=“true”。 可以通过 check=“false” 关闭检查,比如测试时,有些服务不关心,或者出现了循环依赖,必须有一方先启动

@Reference( 
        loadbalance = "random", 
        mock = "com.springboot.practice.springbootdubboclient.MockSayHelloService", 
        timeout =1000, 
        cluster = "failfast", 
        check=false) // 关闭检查
IHelloService helloService; 

注:registry、reference、consumer都可以配置check这个属性

4.2 多版本支持

当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间 不引用。 可以按照以下的步骤进行版本迁移:

  1. 在低压力时间段,先升级一半提供者为新版本
  2. 再将所有消费者升级为新版本
  3. 然后将剩下的一半提供者升级为新版本

4.3 主机绑定IP

默认的主机绑定方式:

  1. 通过 LocalHost.getLocalHost() 获取本机地址
  2. 如果是127.* 等 loopback(环路地址)地址,则扫描各网卡,获取网卡 IP。

另外,还可以手动配置主机 host:

  1. 如果是 springboot,修改配置:dubbo.protocol.host=” ”
  2. 如果注册地址获取不正确,可以通过在 dubbo.xml 中加入主机地址的配置 <dubbo:protocol host="205.182.23.201">
已标记关键词 清除标记
表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页