Dubbo
Last updated
Was this helpful?
Last updated
Was this helpful?
在 Dubbo 的核心领域模型中:
Protocol
是服务域,它是 Invoker 暴露和引用的主功能入口,它负责 Invoker 的生命周期管理。
Invoker
是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。
Invocation
是会话域,它持有调用过程中的变量,比如方法名,参数等。
采用 Microkernel + Plugin
模式,Microkernel 只负责组装 Plugin,Dubbo 自身的功能也是通过扩展点实现的,也就是 Dubbo 的所有功能点都可被用户自定义扩展所替换。
采用 URL 作为配置信息的统一格式,所有扩展点都通过传递 URL 携带配置信息。
第一层:service 层,接口层,给服务提供者和消费者来实现的
第二层:config 层,配置层,主要是对 dubbo 进行各种配置的
第三层:proxy 层,服务代理层,无论是 consumer 还是 provider,dubbo 都会给你生成代理,代理之间进行网络通信
第四层:registry 层,服务注册层,负责服务的注册与发现
第五层:cluster 层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务
第六层:monitor 层,监控层,对 rpc 接口的调用次数和调用时间进行监控
第七层:protocal 层,远程调用层,封装 rpc 调用
第八层:exchange 层,信息交换层,封装请求响应模式,同步转异步
第九层:transport 层,网络传输层,抽象 mina 和 netty 为统一接口
第十层:serialize 层,数据序列化层
第一步:provider 向注册中心去注册
第二步:consumer 从注册中心订阅服务,注册中心会通知 consumer 注册好的服务
第三步:consumer 调用 provider
第四步:consumer 和 provider 都异步通知监控中心
可以,因为刚开始初始化的时候,消费者会将提供者的地址等信息拉取到 本地缓存,所以注册中心挂了可以继续通信。
dubbo 协议:默认就是走 dubbo 协议,单一长连接,进行的是 NIO 异步通信,基于 hessian 作为序列化协议。使用的场景是:传输数据量小(每次请求在 100kb 以内),但是并发量很高。
rmi 协议:走 Java 二进制序列化,多个短连接,适合消费者和提供者数量差不多的情况,适用于文件的传输,一般较少用。
hessian 协议:走 hessian 序列化协议,多个短连接,适用于提供者数量比消费者数量还多的情况,适用于文件的传输,一般较少用。
http 协议:走 json 序列化
webservice:走 SOAP 文本序列化
dubbo
支持 hession
、 Java 二进制序列化、json
、SOAP
文本序列化多种序列化协议。但是 hessian
是其默认的序列化协议。
其实 PB 之所以性能如此好,主要得益于两个:
它使用 proto
编译器,自动进行序列化和反序列化,速度非常快,应该比 XML 和 JSON 快上了 20~100
倍;
它的数据压缩效果好,就是说它序列化后的数据量体积小。因为体积小,传输起来带宽和速度上会有优化。
默认情况下,dubbo 是 random load balance
,即 随机 调用实现负载均衡,可以对 provider
不同实例 设置不同的权重,会按照权重来负载均衡,权重越大分配流量越高,一般就用这个默认的就可以了。
这个的话默认就是均匀地将流量打到各个机器上去,但是如果各个机器的性能不一样,容易导致性能差的机器负载过高。所以此时需要调整权重,让性能差的机器承载权重小一些,流量少一些。
这个就是自动感知一下,如果某个机器性能越差,那么接收的请求越少,越不活跃,此时就会给 不活跃的性能差的机器更少的请求。
一致性 Hash 算法,相同参数的请求一定分发到一个 provider
上去, provider
挂掉的时候,会基于虚拟节点均匀分配剩余的流量,抖动不会太大。如果你需要的不是随机负载均衡,是要一类请求都到一个节点,那就走这个一致性 Hash
策略。
失败自动切换,自动重试其他机器,默认就是这个,常见于读操作。(失败重试其它机器)
一次调用失败就立即失败,常见于写操作。(调用失败就立即失败)
出现异常时忽略掉,常用于不重要的接口调用,比如记录日志。
失败了后台自动记录请求,然后定时重发,比较适合于写消息队列这种。
并行调用 多个 provider
,只要一个成功就立即返回。
逐个调用所有的 provider。
默认使用 javassist
动态字节码生成,创建代理类。但是可以通过 spi 扩展机制配置自己的动态代理策略。
spi
,简单来说,就是 service provider interface
,说白了是什么意思呢,比如你有个接口,现在这个接口有 3 个实现类,那么在系统运行的时候对这个接口到底选择哪个实现类呢?这就需要 spi
了,需要根据指定的配置或者是默认的配置,去找到对应的实现类加载进来,然后用这个实现类的实例对象。
dubbo
也用了 spi
思想,不过没有用 jdk
的 spi
机制,是自己实现的一套 spi
机制。
Protocol
接口,在系统运行的时候, dubbo
会判断一下应该选用这个 Protocol
接口的哪个实现类来实例化对象来使用。
它会去找一个你配置的 Protocol
,将你配置的 Protocol
实现类,加载到 jvm
中来,然后实例化对象,就用你的那个 Protocol
实现类就可以了。
JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
增加了对扩展点 IoC 和 AOP 的支持,一个扩展点可以直接 setter 注入其它扩展点。
如果扩展点加载失败,连扩展点的名称都拿不到了。比如:JDK 标准的 ScriptEngine,通过 getName() 获取脚本类型的名称,但如果 RubyScriptEngine 因为所依赖的 jruby.jar 不存在,导致 RubyScriptEngine 类加载失败,这个失败原因被吃掉了,和 ruby 对应不起来,当用户执行 ruby 脚本时,会报不支持 ruby,而不是真正失败的原因。
一个大型的分布式系统,或者说是用现在流行的微服务架构来说吧,分布式系统由大量的服务组成。那么这些服务之间互相是如何调用的?调用链路是啥?说实话,几乎到后面没人搞的清楚了,因为服务实在太多了,可能几百个甚至几千个服务。
那就需要基于 dubbo 做的分布式系统中,对各个服务之间的调用自动记录下来,然后自动将 各个服务之间的依赖关系和调用链路生成出来,做成一张图,显示出来,大家才可以看到对吧。
需要自动统计 各个接口和服务之间的调用次数以及访问延时,而且要分成两个级别。
一个级别是接口粒度,就是每个服务的每个接口每天被调用多少次,TP50/TP90/TP99
,三个档次的请求延时分别是多少;
第二个级别是从源头入口开始,一个完整的请求链路经过几十个服务之后,完成一次请求,每天全链路走多少次,全链路请求延时的 TP50/TP90/TP99
,分别是多少。
这些东西都搞定了之后,后面才可以来看当前系统的压力主要在哪里,如何来扩容和优化啊。
服务分层(避免循环依赖)
调用链路失败监控和报警
服务鉴权
每个服务的可用性的监控(接口调用成功率?几个 9?99.99%,99.9%,99%)
比如说服务 A 调用服务 B,结果服务 B 挂掉了,服务 A 重试几次调用服务 B,还是不行,那么直接降级,走一个备用的逻辑,给用户返回响应。
举个栗子,我们有接口 HelloService
。HelloServiceImpl
有该接口的具体实现。
我们调用接口失败的时候,可以通过 mock
统一返回 null
。
mock 的值也可以修改为 true,然后再跟接口同一个路径下实现一个 Mock 类,命名规则是 “接口名称+Mock
” 后缀。然后在 Mock 类里实现自己的降级逻辑。
所谓失败重试,就是 consumer
调用 provider
要是失败了,比如抛异常了,此时应该是可以重试的,或者调用超时了也可以重试。配置如下: