Dubbo中的知识点总结

Dubbo的基础知识:线程模型、协议、序列化方式、负载均衡策略、集群容错模式等等

所有内容,以Dubbo官方的2.7.5 release版本为基础。

Dubbo中的同步调用/异步调用

Dubbo框架中的默认缺省协议:Dubbo协议(DubboProtocol)采用的是单一长连接,底层默认使用的是Netty的NIO异步通信。这种协议适用于小数据量大并发的服务调用。基于这种机制,Dubbo主要提供了以下几种调用方式:

  • one way(客户端发送消息后,不需要接受响应)
  • sync(同步,平常用的最多的)
  • future(异步回调,future.get()阻塞当前线程)
  • callback(异步回调,由回调线程处理,不阻塞当前线程)

可以通过 sent 设置是否等待消息发出:

  • sent="true" 等待消息发出,消息发送失败将抛出异常。
  • sent="false" 不等待消息发出,将消息放入 IO 队列,即刻返回。
<dubbo:method name="findFoo" async="true" sent="true" />

如果想做到one way这样的形式,奖方法标记为async,return设置为false即可,不会创建 CompletableFuture 对象。

<dubbo:method name="findFoo" async="true" return="false" />

Dubbo中的线程模型(dispatcher)

Dubbo中的线程大体可以分为IO线程和业务线程两类,它们要处理请求,响应,心跳,连接事件,断开事件等消息。Dubbo提供了五种不同的派发策略(dispatcher),我们可以根据自己的实际场景选择不同的派发策略、线程池类型、线程池大小。

  • all 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等。
  • direct 所有消息都不派发到线程池,全部在 IO 线程上直接执行。
  • message 只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在 IO 线程上执行。
  • execution 只有请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在 IO 线程上执行。
  • connection 在 IO 线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。

默认使用的派发策略是 all ,线程池是 fixed ,大小为 200

官方对派发策略的推荐是:

如果事件处理的逻辑能迅速完成,并且不会发起新的 IO 请求,比如只是在内存中记个标识,则直接在 IO 线程上处理更快,因为减少了线程池调度。

但如果事件处理逻辑较慢,或者需要发起新的 IO 请求,比如需要查询数据库,则必须派发到线程池,否则 IO 线程阻塞,将导致不能接收其它请求。

可以根据不同的业务场景选择不同的策略,也可以自定义扩展 Dispatcher

Dubbo中的支持的协议,序列化工具等等

序列化工具

Dubbo默认使用Hessian2作为序列化工具,同时Dubbo还支持FastJson、Java序列化、Protostuff(不用写.proto的protobuff)等等。

可以实现 org.apache.dubbo.common.serialize.Serialization 进行自定义扩展。

调用协议

默认使用Dubbo协议,Dubbo协议使用TCP传输,采用单一长连接+NIO 异步通讯。除此之外,还支持RMI这种阻塞式短连接(远程调用中JDK的标准实现),贡献自DubboX的rest协议。Dubbo还支持gRPC这样高性能的协议以及http普遍通用的远程调用。

可以实现 org.apache.dubbo.rpc.Protocol 进行自定义扩展。

注册中心

推荐使用ZooKeeper作为注册中心,和ZooKeeper类似的etcd也已支持。还支持Multicast这样广播的形式,Nacos、Redis、Consul等等都是支持的。

可以实现 org.apache.dubbo.registry.Registry 进行自定义扩展。

Dubbo2.7.2以后支持了多注册中心。

dubbo支持多注册,多消费 可以将你们的Provider为每个注册中心都注册一份数据,消费方消费多个注册中心服务,目前dubbo是按照配置注册中心的循序进行消费,比如是配置了redis, zk注册中心,优先消费redis服务,如果redis没有服务会自动去消费zk服务

负载均衡策略

  • Random LoadBalance

    • 随机,按权重设置随机概率。dubbo默认的负责均衡策略。
    • 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重
  • RoundRobin LoadBalance

    • 轮询,按公约后的权重设置轮询比率。
    • 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
  • LeastActive LoadBalance

    • 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
    • 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
  • ConsistentHash LoadBalance

    • 一致性Hash,相同参数的请求总是发到同一提供者。
    • 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
    • 缺省只对第一个参数Hash,修改请配置
    • 缺省用160 份虚拟节点,修改请配置

集群的容错模式

  • FailOver Cluster

    故障转移模式,dubbo默认的集群容错模式。

    1. 失败自动切换,当出现失败时,重试其他服务器

    2. 通常用于读操作,但重试会带来更长延迟

    3. 可通过设置retries=2来设置重试次数(不含第一次)

    <dubbo:service retries="2" cluster="failover"/><dubbo:reference retries="2" cluster="failover"/>
    
  • FailFast Cluster

    快速失败模式,只发起一次调用,失败立即报错

    通常用于非幂等性的写操作,比如新增记录。

    <dubbo:service cluster="failfast" />
    <dubbo:reference cluster="failfast" />
    
  • FailSafe Cluster

    安全失败模式,出现异常,直接忽略。

    通常用于写入审计日志等操作。

    <dubbo:service cluster="failsafe" />
    <dubbo:reference cluster="failsafe" />
    
  • Failback Cluster

    失败后自动恢复,后台记录失败请求,定时重发

    通常用于消息通知操作

    <dubbo:service cluster="failback" />
    <dubbo:reference cluster="failback" />
    
  • Forking Cluster

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

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

    <dubbo:service cluster="forking" forks="2"/>
    <dubbo:reference cluster="forking" forks="2"/>
    
  • Broadcast Cluster

    广播调用所有提供者,逐个调用,任意一台报错则报错。

    通常用于通知所有提供者更新缓存或日志等本地资源信息。

通信框架

Dubbo默认使用的Netty4作为通信框架,除此之外Dubbo还支持netty3、mina、grizzly。

通信框架主要负责远程传输和通讯,相关扩展接口如下:

  • org.apache.dubbo.remoting.Transporter
  • org.apache.dubbo.remoting.Server
  • org.apache.dubbo.remoting.Client

动态代理策略

Dubbo中默认采用 Javassist 作为动态代理工具,此外还支持 JDK 。为什么选择 Javassit ,Dubbo的开发者【梁飞】在博客 《动态代理方案性能对比》 中说主要是出于性能的考虑。Javassit 提供字节码 bytecode 生成方式和动态代理接口两种方式。Javassit 动态代理接口的方式,效率比JDK自带的还差,所以Dubbo使用的是它的 bytecode 生成方式。

常见的实现动态代理的几种方案:

  • jdk 动态代理
  • cglib 动态代理
  • javassist 动态代理
  • ASM 字节码
  • javassist 字节码

##Dubbo中的SPI机制

Dubbo中的限流(Sentinel)

1.原生的TpsLimitFilter

2.Sentinel