概述
文章内容参考infoq以及其他博文综合整理而来,具体参考的文章如下:
- https://www.infoq.cn/article/IwZCAp3jo_H5fJFbWOZu
- https://segmentfault.com/a/1190000021238007
- https://segmentfault.com/a/1190000016741532
架构概览图
-
Provider导出一个服务,这个服务就是可被调用的;
-
第二步,往注册中心注册这个服务;
-
Consumer 这端会来订阅相关的服务,如果注册中心里面,Provider 列表有变化的话,它也会得到通知;
-
Consumer 会根据一定的路由规则从注册中心拿到 Provider 列表,再根据一定的负载均衡策略,精确地调用到某台 Provider 上去。
问题或改进点
案例
在杭州有一家中等规模的电商公司,公司内部有 4000+ 个服务,以 Zookeeper 作为注册中心,Zookeeper 有 100w 个节点,在发布日的时候,公司内部网络的网卡被打爆了,进而导致服务变更的推送失败,新的服务注册也失败。整个集群基本上处于不可用状态。同样的也收到了一些中小公司的反馈,每次在发布的时候,网络也会有个抖动。
分析一下为什么会出现这种情形。
Zookeeper 的 100 万节点中,大约有 10 万个 Provider 节点和 50 万个 Consumer 节点。按照前面的算法,在所有 Provider 同时发布的极端情况下,有 2×10 万×50 万次推送,也就是说会产生 1000 亿条的数据推送。针对每次推送的数据进行了一个统计,每条 URL 大小大概有 1KB,那么计算出来的极端的推送数据量是 1KB 再乘以 1000 亿,已经是 100TB 的级别了。
主要原因:
- zk上的URL内的内容非常多,URL内包含,机器信息,应用信息,接口信息以及方法等。在极端情况下,应用上下线都要通知provider,会造成网络堵塞,导致服务不可用。
解决方案
-
注册中心只保存ip和端口。
-
url等信息存储到一个持久化存储,拆分出元数据中心。
讨论理想的注册中心?
-
Eureka,AP应用,性能zk的60%,是应用注册维度,而非目前服务注册维度。
-
Etcd,CP系统,要求数据强一致性,牺牲了部分性能。
-
zookeeper, 中心化,leader单点,CP系统,选举中有最多30秒不可用。
-
Nacos,去中心化,设计上满足AP和最终一致性,性能与ZK接近。(比较理想的方案,原理: https://www.infoq.cn/article/B*6vyMIKao9vAKIsJYpE)
Nacos应该是比较理想的一个服务注册中心,github地址: https://github.com/alibaba/nacos
Nacos一致性协议:
目前的一致性协议实现,一个是基于简化的 Raft 的 CP 一致性,一个是基于自研协议 Distro 的 AP 一致性。Raft 协议不必多言,基于 Leader 进行写入,其 CP 也并不是严格的,只是能保证一半所见一致,以及数据的丢失概率较小。Distro 协议则是参考了内部 ConfigServer 和开源 Eureka,在不借助第三方存储的情况下,实现基本大同小异。Distro 重点是做了一些逻辑的优化和性能的调优。
三个中心
期望的使用方式:Provider 先去配置中心里获取注册中心的地址和元数据中心地址,再根据拿到的注册中心地址去对应的注册中心注册服务,根据拿到的元数据中心地址写入元数据信息到对应的元数据中心 Server。Consumer 和 OPS 也是类似的。
设计原则
拓展站点设计原则:http://dubbo.apache.org/zh-cn/docs/dev/principals/extension.html
设计基本常识:http://dubbo.apache.org/zh-cn/docs/dev/principals/general-knowledge.html
扩展:http://dubbo.apache.org/zh-cn/docs/dev/principals/expansibility.html
源码导读入口:http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html
整体架构设计
图例说明:
- 图中左边淡蓝背景的为服务消费方使用的接口,右边淡绿色背景的为服务提供方使用的接口,位于中轴线上的为双方都用到的接口。
- 图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service 和 Config 层为 API,其它各层均为 SPI。
- 图中绿色小块的为扩展接口,蓝色小块为实现类,图中只显示用于关联各层的实现类。
- 图中蓝色虚线为初始化过程,即启动时组装链,红色实线为方法调用过程,即运行时调时链,紫色三角箭头为继承,可以把子类看作父类的同一个节点,线上的文字为调用的方法。
各层说明
- service层:消费者使用的接口,在代码里,我们直接使用这个
- config 配置层:对外配置接口,以
ServiceConfig
,ReferenceConfig
为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类 - proxy 服务代理层:服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以
ServiceProxy
为中心,扩展接口为ProxyFactory
- registry 注册中心层:封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为
RegistryFactory
,Registry
,RegistryService
- cluster 路由层:封装多个提供者的路由及负载均衡,并桥接注册中心,以
Invoker
为中心,扩展接口为Cluster
,Directory
,Router
,LoadBalance
- monitor 监控层:RPC 调用次数和调用时间监控,以
Statistics
为中心,扩展接口为MonitorFactory
,Monitor
,MonitorService
- protocol 远程调用层:封装 RPC 调用,以
Invocation
,Result
为中心,扩展接口为Protocol
,Invoker
,Exporter
- exchange 信息交换层:封装请求响应模式,同步转异步,以
Request
,Response
为中心,扩展接口为Exchanger
,ExchangeChannel
,ExchangeClient
,ExchangeServer
- transport 网络传输层:抽象 mina 和 netty 为统一接口,以
Message
为中心,扩展接口为Channel
,Transporter
,Client
,Server
,Codec
- serialize 数据序列化层:可复用的一些工具,扩展接口为
Serialization
,ObjectInput
,ObjectOutput
,ThreadPool
领域模型
在 Dubbo 的核心领域模型中:
- Protocol 是服务域,它是 Invoker 暴露和引用的主功能入口,它负责 Invoker 的生命周期管理。
- Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它,它代表一个可执行体,可向它发起 invoke 调用,它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。
- Invocation 是会话域,它持有调用过程中的变量,比如方法名,参数等。
基本设计原则
- 采用 Microkernel + Plugin 模式,Microkernel 只负责组装 Plugin,Dubbo 自身的功能也是通过扩展点实现的,也就是 Dubbo 的所有功能点都可被用户自定义扩展所替换。
- 采用 URL 作为配置信息的统一格式,所有扩展点都通过传递 URL 携带配置信息。