注册中心
一)注册中心
1.1 注册中心的概念
注册中心功能很多,但有主要一点便是用于解决服务发现问题。
服务狭义是指 consumer 查找 provider 地址(IP + port)的过程。
服务发现中的服务含义非常广泛,数据库实例、缓存实例、RPC provider、微服务、网络设备等等,都可被视为服务。
在虚拟化容器广泛使用之前,服务大多部署在物理机器上。IP 相对静态,因此即使没有服务发现,通过 硬编码 服务的地址,也能满足需求。
云计算时代,尤其是 Docker 的快速发展,硬编码服务IP是搞不定的,同时也架不住每次环境变动都去该配置文件之类的操作。
呢么当服务不再部署在物理机上,每次新创建的实例,其 IP 很可能与上次不同,因此需要更加灵活的服务发现机制。
1.2 注册中心的用处
-
当服务提供者增加节点、IP变动等,无需做额外操作,解决服务发现的问题,既
动态加入
; -
从消费者角度而言,可以动态感知新配置、路由规则、新的服务提供者,当前服务无需启停,既
动态发现
;
当引入注册中心后,步骤总体围绕下述几个流程:
- 服务容器负责启动、加载、运行服务提供者。
- 服务提供者在启动时,向注册中心注册自己提供的服务。
- 服务消费者在启动时,向注册中心订阅自己所需的服务。
- 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
- 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
- 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
1.3 注册中心 - zookeeper
3.1 zookeeper 节点数据说明
3.2 zookeeper 特性以及作为分布式中心的优势
Zookeeper注册中心采用 “事件通知” + “客户端拉取” 的实现方式:
- 客户端在第一次连接注册中心的时候,会获取对应目录下的全量数据。
- 客户端会在订阅的节点上注册一个watch,客户端与注册中心之间保持TCP长连接。
- 当节点发生事务操作,节点的版本号发生改变,就会触发watch事件,推送数据给订阅方,订阅方收到通知之后,就会拉取对应目录下的数据。
当然,从目前来看这种CP模型作为注册中心已经广受非议了,大多的服务场景还是力保AP,
Nacos
已经成为了更具说服力的注册中心;
其特点以及与ZK的差异见阿里云关于nacos作为注册中心的推荐理由。
核心论调如下:
3.3 zookeeper的watch机制于dubbo中的使用
于dubbo中的使用而言,主要是实践提供者、消费者信息变动的相关推送,避免被高频轮询;
3.4 dubbo对zookeeper进行数据的写入
基于本次案例,本地自启一台zk作为注册中心。
依次从dubbo-admin、zk节点信息进行观察、dubbo代码作为窥点,填疑相关理解;
结论如下:
/dubbo 路径
路径下是以接口为维度进行的数据组装,每一个服务若存在N要暴露的接口,则会有N条PATH;zk自身的强一致性,对于上述数量级的变化而言是有性能瓶颈隐患的;
/services 路径
后从2.7.5 ~ 3.0.0开始,逐步已 /services 模型进行适配,既服务治理是以服务维度而非接口维度,也是3.0变化中最为重要的变化之一;
本质来看,从/dubbo 逐步切换至 /services+程序解析,相关解析规则、变更同步交由程序来完成,以上也是dubbo较为重要的变化方向之一;
3.4.1 admin观察服务producer侧服务暴露成功:
3.4.2 /dubbo路径下的信息摘要:
得到重点的URL信息,摘录如下:
dubbo%3A%2F%2F192.168.1.238%3A20880%2Fcom.lcy.course.dubbo.HelloServiceAPI%3Faccesslog%3D…%2Fdubbo-log.txt%26anyhost%3Dtrue%26application%3Dhello-world-producer%26deprecated%3Dfalse%26dispatcher%3Dall%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dcom.lcy.course.dubbo.HelloServiceAPI%26metadata-type%3Dremote%26methods%3DsayHello%26pid%3D8484%26queues%3D0%26release%3D3.0.1%26service.filter%3Ddefault%2CmyProviderFilter%26side%3Dprovider%26threadname%3Djiangzh%26threadpool%3Dfixed%26threads%3D200%26timestamp%3D1633357317288
上述URL,会通过org.apache.dubbo.common.URL
类进行封装,其DOC.eg如下:
该类同样也会提供一些getGroup 、addParameters 等基础方法;
URL
对象围绕全文,后续构建成不同的PROTOCOL也是以此为基础做的转换的;
3.4.3 /services路径下的信息摘要:
3.0 dubbo中新增的元数据描述,维度已经是应用维度;
{
"name": "hello-world-producer",
"id": "192.168.1.238:20880",
"address": "192.168.1.238",
"port": 20880,
"sslPort": null,
"payload": {
"@class": "org.apache.dubbo.registry.zookeeper.ZookeeperInstance",
"id": null,
"name": "hello-world-producer",
"metadata": {
"accesslog": "../dubbo-log.txt",
"anyhost": "true",
"application": "hello-world-producer",
"deprecated": "false",
"dispatcher": "all",
"dubbo": "2.0.2",
"dubbo.endpoints": "[{\"port\":20880,\"protocol\":\"dubbo\"}]",
"dubbo.metadata-service.url-params": "{\"version\":\"1.0.0\",\"dubbo\":\"2.0.2\",\"release\":\"3.0.1\",\"port\":\"20880\",\"protocol\":\"dubbo\"}",
"dubbo.metadata.revision": "4e42192c02cbf2a433fb72671cc7ebff",
"dubbo.metadata.storage-type": "local",
"dynamic": "true",
"generic": "false",
"interface": "com.lcy.course.dubbo.HelloServiceAPI",
"metadata-type": "remote",
"methods": "sayHello",
"pid": "8484",
"queues": "0",
"release": "3.0.1",
"service.filter": "default,myProviderFilter",
"side": "provider",
"threadname": "jiangzh",
"threadpool": "fixed",
"threads": "200",
"timestamp": "1633357317288"
}
},
"registrationTimeUTC": 1633357318144,
"serviceType": "DYNAMIC",
"uriSpec": null
}
二)dubbo 与注册中心的交互流程(*)
1) dubbo向注册中心的注册、监听
我们以注册、服务注册、服务监听三个方向进行学习,主要涉及的为dubbo#register
模块。
1.1.1 服务提供者是【如何】注册至注册中心
服务注册的起点代码为:
org.apache.dubbo.registry.integration.RegistryProtoco#export(final Invoker originInvoker)
目标:解析注册协议标签下的信息:
其中,入参参数Inovker传递为其实现类DelegateProviderMetaDataInvoker
,元数据内容大致如下:
主要流程如下:
(大体知道类职责即可,后续流程串联知链路)。
附1:
service-discovery-registry😕/localhost:2181/org.apache.dubbo.registry.RegistryService?application=hello-world-producer&client=curator&dubbo=2.0.2&pid=13018®istry=zookeeper&release=3.0.1×tamp=1633366859654
说明
:
- localhost:2181 为 注册中心的地址;
debug数据摘录:
附2:>dubbo😕/192.168.1.238:20880/com.lcy.course.dubbo.HelloServiceAPI?accesslog=…/dubbo-log.txt&anyhost=true&application=hello-world-producer&bind.ip=192.168.1.238&bind.port=20880&deprecated=false&dispatcher=all&dubbo=2.0.2&dynamic=true&generic=false&interface=com.lcy.course.dubbo.HelloServiceAPI&metadata-type=remote&methods=sayHello&pid=13018&queues=0&release=3.0.1&service.filter=default,myProviderFilter&side=provider&threadname=jiangzh&threadpool=fixed&threads=200×tamp=1633366859664
说明
:
- dubbo//:此时链接协议为dubbo,仅表示此次链接以dubbo协议构建,还未转换至provider协议。
- 192.168.1.238:20880 为服务提供者地址;
- com.lcy.course.dubbo.HelloServiceAPI为服务提供者具体服务;
debug数据摘录:
附3:
provider😕/192.168.1.238:20880/com.lcy.course.dubbo.HelloServiceAPI?accesslog=…/dubbo-log.txt&anyhost=true&application=hello-world-producer&bind.ip=192.168.1.238&bind.port=20880&category=configurators&check=false&deprecated=false&dispatcher=all&dubbo=2.0.2&dynamic=true&generic=false&interface=com.lcy.course.dubbo.HelloServiceAPI&metadata-type=remote&methods=sayHello&pid=13018&queues=0&release=3.0.1&service.filter=default,myProviderFilter&side=provider&threadname=jiangzh&threadpool=fixed&threads=200×tamp=1633366859664
说明
:
- 根据步骤2dubbo协议体,构建出provider协议。
debug数据摘录:
附4:
对 三项核心方法进行核心说明
doLocalExport
Register实例构建
register(register,registerProviderUrl)
说明
:
- 通过SPI机制(协议protocol)确定执行类 , 执行类于本次学习中都是ZK;
- 都是zk的前提下,对于/dubbo 、/service 两组path下又为不同的实现类;
- 对应的实行类写zkNode信息;
我们暂时不去关注listener相关实践,本质还是秉着主线先摸一轮;
1.2.1 服务提供者是【何时】注册至注册中心
通过上述2.1步骤,我们知道了dubbo是如何写zk信息,但是dubbo又是何时向zk写入呢?
不得不说这种SPI模型对于代码阅读是真的累;
其启动类围绕:
org.apache.dubbo.config.spring.schema.DubboNamespaceHandler
@Override
public void init() {
// 基础标签、配置解析
registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class));
registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class));
registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class));
registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class));
registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class));
registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class));
registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class));
registerBeanDefinitionParser("ssl", new DubboBeanDefinitionParser(SslConfig.class));
registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class));
registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class));
registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class));
//业务标签解析受理
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class));
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class));
registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
}
上述初始化主要目的是形成 标签/协议 与对应职责类的相关映射关系,我们重点观察下涉及此阶段的议点项。
随着DubboNamespaceHandler
进行引导register行为时,其中service、reference服务注册、订阅也随之而来。
2)服务提供者 与 服务消费者 的注册
服务提供者注册与服务消费者注册概览如下,下面我们来进行拆解;
2.1 服务提供者-ServiceBean说明
new DubboBeanDefinitionParser(ServiceBean.class))
其ServiceBean的类图如下:
2.1.1 服务提供者-ServiceBean-afterPropertiesSet-引导
其中基于spring扩展接口afterPropertiesSet回调方法
,进行接口的全路径设置并随后注入当前的serviceBean进入容器;
其注入指的是向dubbo#ConfigManager添加serviceBean信息;
简单来说,暂时的初始化完后,等待后续的实例化;
2.1.2 服务提供者-ServiceBean#DubboBootstrapApplicationListener-引导
随后根据spring执行顺序,当容器启动完毕时会调用回调方法onApplicationContextEvent
,dubbo中的实现类为:
org.apache.dubbo.config.spring.context.DubboBootstrapApplicationListener
再此环节,完成与上文提及的zkPath的写入交互。
2.1.3 服务提供者-执行流程
围绕ServiceBean的afterPropertiesSet() 、DubboBoostrapApplicationListener的onContextRefreshEvent() 两组函数进行调度,回顾如下:
2.2 服务消费者
我们主要关注右侧新增
2.2.1服务消费者-订阅流程 (APPLICATION_FIRST)
监听的总体流程较服务注册事项多一些,其主线逻辑大致如下:
其中省略了一些非核心方法,可能存在一些跟丢的地方;
2.2.2 服务消费者-dubboRegister实现
流程见上图,其中需要留意的是无论注册中心是zk还是nacos等,dubbo对于注册中心的信息获取也是存在多级缓存的;
流程图中提到的File文件缓存以及没有提到的内存map缓存等;
3)总结与疑问
1、Provider将自己的元数据信息注册到注册中心上
2、Consumer从注册中心上获取Provider的元数据信息【初始化】
3、Consumer注册Watcher,后续进行监听并更新元数据信息
学习结果 : 大致明白了服务是咋挂上去的、服务是咋监听的 ,为学习服务调用提供基础知识积累;
学习疑问
:
-
dubbo中将协议PROTOCOL 靠拢至的invoker该如何理解。
-
doLocalExport()的具体实现。
-
dubbo cache的PROTOCOL缓存,文件、内存是何时何机遇下才会被加载。
-
多注册中心下 ,dubbo注册与上述流程是否有大区别。
-
添加的监听事件,若产生实际数据变更触发event,具体流程是啥。