Dubbo

Dubbo 基础

Dubbo角色

  • provider:服务提供方
  • consumer:服务消费方
  • registry:注册中心
  • moniotr:监控中心
  • container: 服务运行容器

协议

  • dubbo: 单一长连接,NIO异步通信
  • rmi: 阻塞式短连接

负载均衡机制

  • random: 随机选择,默认
  • round robin: 轮询
  • constant hash: 一致性Hash策略,用虚拟节点解决热点节点的问题
  • least active: 最不活跃

集群容错方案

  • failover: 失败自动切换,默认
  • failfast: 失败直接报错
  • failback: 失败自动恢复,定时重发
  • failsafe: 失败安全,忽略异常
  • forking: 并行调用多个,有一个成功就行
  • broadcast: 广播逐个调用,忽略每个节点的异常

读操作采用failover, 默认重试两次
写操作采用failfast, 失败就报错

当一个接口有多个实现

  • 通过group来分组,provider和consumer配置相同的group

版本兼容

  • 通过version, 多个不同的版本可以注册到注册中心

调用方式

  • 默认同步
  • 支持异步调用,Reference(async = true)

服务降级

  • 含义:告诉consumer,调用服务时要做哪些动作,不操作provider
  • 设置consumer的mock参数
  • 向注册中心写入:override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=consumer_app&mock=force:return+null
  • force:return+null 消费方调用服务时直接返回null
  • fail:return+null 消费方在调用失败后再返回null

Dubbo 线程模型

原理

  • Netty提供io线程处理请求,Dubbo提供业务/工作线程池
  • SPI接口Dispatcher,提供方法dispatch(ChannelHandler)
  • 默认采用AllDispatcher

AllDispatcher

  • 全部放入工作线程池

DirectDispatcher

  • 全部不放入工作线程池,在IO线程上处理

ConnectionOrderedDispatcher

  • Connected/Disconnected放入有序队列(线程池),Request和Response放入工作线程池

MessageOnlyDispatcher

  • 只有Request和Response事件放入工作线程池

ExecutionDispatcher

  • 只有Request事件放入工作线程池

Dubbo 线程池

ThreadPool

  • 定义SPI接口ThreadPool
  • 提供方法getExecutor获取线程池
  • 默认采用FixedThreadPool,其他:LimitedThreadPool, CachedThreadPool, EagerThreadPool

FixedThreadPool

  • 线程池中线程的数量固定不变;线程池满了就放队列,队列满了就拒绝;
  • 线程数通过参数threads配置,默认200;
  • 等待队列大小通过参数queues配置,默认0,使用SynchronousQueue;正数使用固定容量的LinkedBlockingQueue;负数使用无容量限制的LinkedBlockingQueue;
  • 拒绝策略是Abort,抛出异常;
  • 拒绝策略有4种:Abort, CallerRuns, Discard, DiscardOldest

LimitedThreadPool

  • 线程池中线程的数量只能增加不能减少,但是有一个上限;先放队列,队列满了新建线程;线程池满了就拒绝;
  • core线程数通过corethreads配置,默认为0;最大线程数通过threads配置,默认200;
  • keepAlive无穷大,线程被创建之后就不会被回收;
  • 等待队列与拒绝策略与FixedThreadPool相同;

CachedThreadPool

  • 线程池的容量无穷大,但是空闲线程会被过期回收;先放队列,队列满了新建线程;
  • core线程数通过corethreads配置,默认为0;最大线程数通过threads配置,默认无穷大;
  • keepAlive通过alive配置,默认1分钟;
  • 等待队列与拒绝策略与FixedThreadPool相同;

EagerThreadPool

  • core线程都busy时,新建线程来处理task,而不是放入等待队列;有空闲线程时则放入等待队列,等待线程处理;
  • 扩展出EagerThreadPoolExecutor和TaskQueue;
  • core线程数通过corethreads配置,默认为0;最大线程数通过threads配置,默认无穷大;
  • keepAlive通过alive配置,默认1分钟;
  • 等待队列大小通过queues配置,默认为1;

EagerThreadPoolExecutor

  • 重载execute方法;
  • 统计已提交的task数量;
  • 被拒绝了,尝试直接放入等待队列;

TaskQueue

  • 重载offer方法;
  • 如果有空闲线程(已提交的task数量小于线程池中线程的数量),则把task插入队列,等待空闲线程来处理;
  • 如果线程池中线程的数量小于线程池的上限,则task不插入队列,而是新建线程来处理;
  • 如果线程池中线程的数量已经达到线程池的上限,则把task插入队列;

Dubbo SPI

Dubbo 自适应拓展机制原理与实例

Dubbo的拓展类(Extension)是通过SPI机制加载的:

  • 对于某个SPI接口,加载指定目录下(META-INF/dubbo)名称为SPI接口全限名的配置文件
  • 对于配置文件里的每一行键值对,加载SPI接口的实现类,也就是拓展类,然后创建实例

有时候我们不希望在Dubbo启动阶段就加载所有的拓展类,而是希望在用到某个拓展类时才加载,这就需要借助于自适应拓展机制。

  • 对于某个SPI接口Car,生成一个自适应拓展类Car$Adaptive,并创建实例
  • 调用该实例的方法时(SPI接口中包含Adaptive注解),会加载拓展类并创建拓展实例,然后调用它的方法

Dubbo Filter 原理

Dubbo的Filter职责链有点绕:

  • 在Invoker#invoke方法里调用Filter#invoke
  • 在Filer#invoke方法的最后一步调用下一个Invoker的inovke方法
  • 如此递归调用
  • 在Invoker#invoke的最后一步调用Filter#onResponse
  • 如此递归返回

Filter#invoke:调用的准备工作,需要执行inovker.inovke(invocation)
Filter#onRespone:调用的结束工作,需要返回result

Dubbo 反射和代理

Dubbo Wrapper

Dubbo Wrapper 可以认为是一种反射机制。它既可以读写目标实例的字段,也可以调用目标实例的方法。比如

  • Car 是接口;RaceCar 是实现类,实现了 Car;ferrari 和 porsche 是 RaceCar 的实例
  • 我们可以为接口 Car 生成一个 Warpper 子类,比如 Wrapper0;然后创建 Wrapper0 的实例 wrapper0
  • 可以通过 wrapper0#getPropertyValue 来读取 ferrari 的字段,也可以读取 porsche 的字段
  • 可以通过 wrapper0#setPropertyValue 来修改 ferrari 的字段,也可以修改 porsche 的字段
  • 可以通过 wrapper0#invokeMethod 来调用 ferrari 的方法,也可以调用 porsche 的方法
  • 优点:通过一个 Wrapper 实例就可以操作目标接口的所有实例

Dubbo Proxy 原理与实例

Dubbo代理机制与JDK的代理机制不同。比如我们有一个接口Car,

  • JDK通过 Proxy#newProxyInstance(ClassLoader, Class[], InvocationHandler) 创建Car的一个实例,所有的方法都要通过InvocationHandler

Dubbo则是分成了两个步骤:

  • 首先生成了Car的实现类proxy0。这是一个代理类,所有的方法都要通过InvocationHandler
  • 然后生成了Proxy的子类Proxy0,并创建了实例。通过newInstance可以创建代理类proxy0的实例。
  • 优点:Proxy类就像一个工厂类,可以创建N个接口的不同实例

谢谢阅读!

你可能感兴趣的:(Dubbo)