Dubbo
服务发现与注册
- 服务提供者
Provider在启动时,向注册中心注册自己提供的服务 - 服务消费者
Consumer在启动时,向注册中心订阅自己所需的服务 - 注册中心
Registry返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者 - 服务消费者
Consumer从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用
注册中心
Zookeeper(推荐)- 分布式协调服务,是一个树型的目录服务,支持变更推送,适合作为
Dubbo服务的注册中心,可用于生产环境,并推荐使用
- 分布式协调服务,是一个树型的目录服务,支持变更推送,适合作为
Nacos- 阿里推出的动态服务发现、服务配置、服务元数据及流量管理、多租户的开源项目,同时支持注册中心、配置中心分离和合并部署
Multicast- 不需要启动任何中心节点,只要广播地址就可以互相发现,组播受网络结构限制,只适合小规模应用或开发阶段使用
Redis- 使用
Redis的Key/Map结构存储数据结构,使用Redis的Publish/Subscribe事件通知数据变更
- 使用
Simple- 本身就是一个普通的
Dubbo服务,可以减少第三方依赖,使整体通讯方式一致
- 本身就是一个普通的
负载均衡策略
RandomLoadBalance- 加权随机,默认算法,默认权重相同- 按权重设置随机概率
- 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重
- 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上
RoundRobinLoadBalance- 加权轮询,借鉴于 Nginx 的平滑加权轮询算法,默认权重相同- 按公约后的权重设置轮询比率,循环调用节点
- 同样存在慢的提供者累积请求的问题
LeastActiveLoadBalance- 最少活跃优先 + 加权随机,背后是能者多劳的思想- 活跃数越低,越优先调用,相同活跃数的进行加权随机(活跃数指调用前后计数差(针对特定提供者:请求发送数 - 响应返回数),表示特定提供者的任务堆积量,活跃数越低,代表该提供者处理能力越强)
- 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大,相对的,处理能力越强的节点,处理更多的请求。
ShortestResponseLoadBalance- 最短响应优先 + 加权随机,关注响应速度- 在最近一个滑动窗口中,响应时间越短,越优先调用,相同响应时间的进行加权随机
- 使得响应时间越快的提供者,处理更多的请求
- 可能会造成流量过于集中于高性能节点的问题
ConsistentHashLoadBalance- 一致性Hash,确定的入参,确定的提供者,适用于有状态请求- 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动
- 缺省只对第一个参数
Hash - 缺省用
160份虚拟节点
集群容错方案
Failover Cluster- 失败自动切换,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟(缺省)Failfast Cluster- 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作,比如新增记录Failsafe Cluster- 失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作Failback Cluster- 失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作Forking Cluster- 并行调用多个服务器,只要一个成功即返回,通常用于实时性要求较高的读操作,但需要浪费更多服务资源Broadcast Cluster- 广播调用所有提供者,逐个调用,任意一台报错则报错,通常用于通知所有提供者更新缓存或日志等本地资源信息Available Cluster- 调用目前可用的实例(只调用一个),如果当前没有可用的实例,则抛出异常,通常用于不需要负载均衡的场景Mergeable Cluster- 将集群中的调用结果聚合起来返回结果,通常和group一起配合使用ZoneAware Cluster- 多注册中心订阅的场景,注册中心集群间的负载均衡- 指定优先级
- 同中心优先
- 权重轮询
- 缺省值:选择一个可用的注册中心
超时设置
- 服务提供者端设置超时时间
- 服务消费者端设置超时时间
- 消费者端设置了超时时间,以消费者端为主
- 调用服务不成功时,默认是会重试两次
协议
dubbo- 单一长连接,适合大并发小数据量的服务调用,以及消费者远大于提供者。
TCP协议,异步,Hessian序列化。
- 单一长连接,适合大并发小数据量的服务调用,以及消费者远大于提供者。
rest- 基于标准的
Java REST API - JAX-RS 2.0(Java API for RESTful Web Services的简写)实现的REST调用支持。多个短连接,HTTP协议,同步,json/xml序列化。
- 基于标准的
HTTP- 基于
HTTP表单的远程调用协议,采用Spring HttpInvoker实现。多个短连接,HTTP协议,同步,JSON序列化,传入参数大小混合,提供者个数多于消费者,可给应用程序和浏览器JS调用。
- 基于
Hessian- 集成
Hessian服务,采用Servlet暴露服务,内嵌Jetty作为服务器默认实现,提供与Hession服务互操作。多个短连接,HTTP协议,同步,Hessian序列化,传入参数较大,提供者大于消费者,提供者压力较大,可传文件。
- 集成
gRPC- 使用
HTTP/2通信,带来Stream、反压、Reactive编程等能力。
- 使用
RMI- 采用
JDK标准的RMI协议实现,使用阻塞式短连接,传输数据包大小混合,消费者和提供者个数差不多。多个短连接,TCP协议,同步,JDK标准序列化。
- 采用
WebService- 基于
WebService的远程调用协议,集成CXF实现,提供和原生WebService的互操作。多个短连接,HTTP协议,同步,SOAP文本序列化,适用系统集成和跨语言调用。
- 基于
Thrift- 原生协议的基础上添加了一些额外的头信息,比如
service name,magic number等。使用Thrift协议同样需要使用Thrift的IDL编译生成相应的Java代码。
- 原生协议的基础上添加了一些额外的头信息,比如
Memcache- 基于
Memcache实现的RPC协议。
- 基于
Redis- 基于
Redis实现的RPC协议。
- 基于
序列化
avrofastjsonFST(推荐)gsonhessian2(默认)jdkkryo(推荐)protobufprotostuff
服务容器
Spring ContainerJetty ContainerLog4j Container
线程模型
- 如果事件处理的逻辑能迅速完成,并且不会发起新的
IO请求,比如只是在内存中记个标识,则直接在IO线程上处理更快,因为减少了线程池调度。 - 但如果事件处理逻辑较慢,或者需要发起新的
IO请求,比如需要查询数据库,则必须派发到线程池,否则IO线程阻塞,将导致不能接收其它请求。 - 如果用
IO线程处理事件,又在事件处理过程中发起新的IO请求,比如在连接事件中发起登录请求,会报可能引发死锁异常,但 不会真死锁。
<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />
Dispatcher
all- 所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等direct- 所有消息都不派发到线程池,全部在IO线程上直接执行message- 只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在IO线程上执行execution- 只有请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在IO线程上执行connection- 在IO线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池
ThreadPool
fixed- 固定大小线程池,启动时建立线程,不关闭,一直持有(缺省)cached- 缓存线程池,空闲一分钟自动删除,需要时重建limited- 可伸缩线程池,但池中的线程数只会增长不会收缩,只增长不收缩的目的是为了避免收缩时突然来了大流量引起的性能问题eager- 优先创建Worker线程池,在任务数量大于corePoolSize但是小于maximumPoolSize时,优先创建Worker来处理任务;当任务数量大于maximumPoolSize时,将任务放入阻塞队列中,阻塞队列充满时抛出RejectedExecutionException(相比于cached:cached在任务数量超过maximumPoolSize时直接抛出异常而不是将任务放入阻塞队列)
SPI
dubbo spi实现原理和java spi相似,只不过增强了一些功能和优化。java spi的是把所有的spi都加载到内存,但对于dubbo来说可能只需要加载用户指定的实现方式,而不需要全部加载进来,全部加载也会有性能问题,所以dubbo实现的是在有用到的时候去加载 这些扩展组件
重要的注解
@SPI- 被此注解标记的接口,就表示是一个可扩展的接口,并标注默认值
@Adaptive- 有两种使用方式,一种是注解在类上,一种是注解在方法上
@Activate- 需要标注在类上或者方法上,并注明被激活的条件,以及所有的被激活实现类中的排序信息
SPI 声明加载目录
META-INF/services/- 用来兼容Java SPIMETA-INF/dubbo/- 存放用户自定义的SPI配置META-INF/dubbo/internal/- 存放Dubbo内部使用的SPI配置
加载流程
- 通过
SPI接口类型查找/创建ExtensionLoader(从extension loader cache中找,没有则创建) - 通过
SPI名称用ExtensionLoader查找/创建SPI实现实例(从instance cache中找,没有则创建)