🍖 Dubbo

吞佛童子2022年10月10日
  • 框架
  • Dubbo
大约 6 分钟

🍖 Dubbo

1. 总体架构

1) 结构图

img.png

2) 节点说明

节点描述
Provider暴露服务的 服务提供者
Consumer远程调用服务的 服务消费者
Registry服务注册 & 发现的 注册中心
Monitor统计 服务调用次数 & 调用时间 的 监控中心
Container服务运行容器

3) 流程说明

  1. Container 负责启动,加载,运行 Provider
  2. Provider 在启动时,向 Registry 注册自己提供的服务
  3. Consumer 在启动时,向 Registry 订阅自己所需的服务
  4. Registry 返回 Provider 地址列表Consumer
    • 如果有变更,Registry 将基于 长连接 推送变更数据给 Consumer
  5. ConsumerProvider 地址列表 中,基于 软负载均衡 算法,选一台 Provider 进行调用
    • 如果调用失败,再选另一台调用
  6. Provider & Consumer 在内存中累计 调用次数调用时间定时 每分钟发送一次统计数据到 Monitor

2. 分层

  • Business
    • service 业务逻辑层
      • 提供 接口 & 实现
  • RPC
    • config 配置层
      • 初始化 配置信息,注意围绕 ServiceConfig & ReferenceConfig
    • proxy 代理层
      • Provider & Consumer 都会生成代理类,使得 服务接口透明化
      • 提供远程调用 & 返回结果
    • registry 注册中心层
      • 提供 服务地址的注册与发现
    • cluster 路由层
      • 提供 负载均衡 & 远程调用失败的容错措施
    • monitor 监控层
      • 记录 RPC 调用次数 & 调用时间
    • protocol 远程调用层
      • 封装 RPC 调用,主要负载管理 Invoker
    • exchange 信息交换层
      • 封装请求响应模式,同步转异步
  • Remoting
    • transport 网络传输层
      • 抽象网络传输的 统一接口
      • 提供 Netty & Mina 两种传输模式
    • serialize 数据序列化层
      • 对 需要在网络传输的数据 进行序列化 & 反序列化

微内核 & SPI 机制

SPI

  • JDK 内置的一个 服务发现机制
  • 程序运行过程中会读取 配置文件,通过 反射 加载实现类,当修改配置文件时,就会 动态替换接口的实现类
  • 使得 接口 & 具体实现 完全解耦,只需要声明 接口,具体实现可以在 配置 中选择
    • META-INF/dubbo/ 目录下放置一个与 接口 同名的 文本文件,文本的内容为 接口的实现类的全限定名,多个实现类用 换行符 分隔
    • 例:
      • 实现了 Dubbo 的负载均衡类,具体实现类为 MyLoadBalance implements LoadBalance
      • META-INF/dubbo/ 目录下添加 文件 org.apache.dubbo.rpc.cluster.LoadBalance
      • 文本内容为 xxx=com.example.MyLoadBalance

Dubbo 自己实现了 SPI 机制

  • Java 的 SPI 在查找扩展实现类时,会遍历 SPI 的配置文件,并将 实现类全部实例化
    • 若某个 实现类 初始化过程十分耗时 & 消耗资源,而实际又暂时用不到,则会产生浪费
  • Dubbo 的 SPI 机制只有需要用到的时候,才会去配置文件中查找该 具体实现类,然后实例化,按需加载

3. 服务暴露过程

  1. Spring IoC 容器 refresh 完毕后,Listener 收到该广播,进行 服务的暴露
  2. 通过 ServiceConfig 组装 Dubbo 的 URL 标签
  3. 通过 ProxyFactory.getInvoker() 获取 Invoker
    • Invoker 通过 javassist | JDK 动态代理 生成
  4. 通过 DubboProtocol 将 Invoker 转换成 Exporter
  5. 启动服务器 Server,监听端口
  6. RegistryProtocol 保存 URL 和 Exporter 的映射关系
  7. 向 注册中心 注册 服务提供者 的信息

4. 服务引用流程

  • 分为 饿汉式 & 懒汉式
    • default = 懒汉式
  1. 服务消费者 根据 配置文件信息注册中心 订阅服务
  2. 通过 DubboProtocol 根据 订阅得到的 服务提供者地址 & 接口信息 创建 Invoker
  3. 通过 Invoker 为 服务接口生成 代理对象,通过该 代理对象 进行远程调用 服务提供者

5. 服务调用流程

  1. 服务消费者 通过 Invoker 生成代理类,记录 该请求 & 请求 ID 等待 服务提供者的响应
  2. 服务提供者 接收请求后,根据 URL 找到对应的 Exporter,调用真正的实现类,进行调用,组装结果后返回给 服务消费者,携带请求ID
  3. 服务消费者 得到该响应后,根据 请求ID 找之前存放的记录,然后将 响应结果 放入对应的 Future 中,唤醒等待的线程,消费者得到响应,流程完毕

6. 负载均衡策略

1) 加权随机

  • 假设我们有一组服务器 servers = [A, B, C],他们对应的权重为 weights = [5, 3, 2],权重总和为10。
  • 现在把这些权重值平铺在一维坐标值上,[0, 5) 区间属于服务器 A,[5, 8) 区间属于服务器 B,[8, 10) 区间属于服务器 C。
  • 通过随机数生成器生成一个范围在 [0, 10) 之间的随机数,
  • 计算这个随机数会落到哪个区间上就可以了

2) 加权轮询

  • 比如服务器 A、B、C 权重比为 5, 3, 2,
  • 那么在 10 次请求中,服务器 A 将收到其中的 5 次请求,服务器 B 会收到其中的 3 次请求,服务器 C 则收到其中的 2 次请求

3) 最小活跃数

  • 每个服务提供者对应一个活跃数 active,初始情况下,所有服务提供者活跃数均为0。
  • 每收到一个请求,活跃数加 1,完成请求后则将活跃数减 1。
  • 在服务运行一段时间后,性能好的服务提供者处理请求的速度更快,因此活跃数下降的也越快,此时这样的服务提供者能够优先获取到新的服务请求。
  • 如果有多个 Invoker 具有相同的最小活跃数,则此时继续走 加权随机 策略

4) 一致性hash

  • 通过 hash 算法,把 provider 的 invoke 和 随机节点生成 hash,并将这个 hash 投射到 [0, 2^32 - 1] 的圆环上,
  • 查询的时候根据 key 进行 md5 然后进行 hash,顺时针得到第一个节点就是所求

7. 集群容错方式

1) Failover Cluster

  • 失败自动切换
  • dubbo 的默认容错方案
  • 当调用失败时自动切换到其他可用的节点,具体的重试次数和间隔时间可用通过引用服务的时候配置,默认重试次数为 1,即只调用一次

2) Failback Cluster

  • 失败自动恢复
  • 在调用失败,记录日志和调用信息,然后返回空结果给consumer,并且通过定时任务每隔5秒对失败的调用进行重试

3) Failfast Cluster

  • 快速失败
  • 只会调用一次,失败后立刻抛出异常

4) Failsafe Cluster

  • 失败安全
  • 调用出现异常,记录日志不抛出,返回空结果

5) Forking Cluster

  • 并行调用多个服务提供者
  • 通过线程池创建多个线程,并发调用多个provider,结果保存到阻塞队列,只要有一个provider成功返回了结果,就会立刻返回结果

6) Broadcast Cluster

  • 广播模式
  • 逐个调用每个provider,如果其中一台报错,在循环调用结束后,抛出异常。
上次编辑于: 2022/10/10 下午8:43:48
贡献者: liuxianzhishou