Dubbo3学习笔记-(一)服务双方如何向【注册中心】进行注册与订阅

注册中心

一)注册中心

1.1 注册中心的概念

注册中心功能很多,但有主要一点便是用于解决服务发现问题。

服务狭义是指 consumer 查找 provider 地址(IP + port)的过程。

服务发现中的服务含义非常广泛,数据库实例、缓存实例、RPC provider、微服务、网络设备等等,都可被视为服务。

在虚拟化容器广泛使用之前,服务大多部署在物理机器上。IP 相对静态,因此即使没有服务发现,通过 硬编码 服务的地址,也能满足需求。

云计算时代,尤其是 Docker 的快速发展,硬编码服务IP是搞不定的,同时也架不住每次环境变动都去该配置文件之类的操作。

呢么当服务不再部署在物理机上,每次新创建的实例,其 IP 很可能与上次不同,因此需要更加灵活的服务发现机制。

1.2 注册中心的用处
  1. 当服务提供者增加节点、IP变动等,无需做额外操作,解决服务发现的问题,既动态加入

  2. 从消费者角度而言,可以动态感知新配置、路由规则、新的服务提供者,当前服务无需启停,既动态发现

当引入注册中心后,步骤总体围绕下述几个流程:

  1. 服务容器负责启动、加载、运行服务提供者。
  2. 服务提供者在启动时,向注册中心注册自己提供的服务。
  3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
  4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
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 ,元数据内容大致如下:

在这里插入图片描述
主要流程如下:

export() 1.1 registryUrl = getRegistryUrl(originInvoker) 获取注册URL(见附1) 1.2 providerUrl = getProviderUrl(originInvoker) 获取提供者URL(见附2) 1.3 getSubscribedOverrideUrl(providerUrl) 获取提供者URL(见附3) 1.4 final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl) , 进行本地服务暴露。 本地服务暴露 第一:自身作为一个服务进行启动。 第二:告诉注册中心相关地址明细(ip、端口、方法名等),为后续服务消费者进行调用。 1.5.1 final Registry registry = getRegistry(registryUrl), 进行核心注册。见附4. 1.5.2 register(registry, registeredProviderUrl), 进行核心注册。见附4. 进行核心注册。见附4. 1.6 对1.4 获取的exporter 进行赋值并通知notifyExport(exporter) export()

(大体知道类职责即可,后续流程串联知链路)。


附1
service-discovery-registry😕/localhost:2181/org.apache.dubbo.registry.RegistryService?application=hello-world-producer&client=curator&dubbo=2.0.2&pid=13018&registry=zookeeper&release=3.0.1&timestamp=1633366859654


说明

  1. 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&timestamp=1633366859664


说明

  1. dubbo//:此时链接协议为dubbo,仅表示此次链接以dubbo协议构建,还未转换至provider协议。
  2. 192.168.1.238:20880 为服务提供者地址;
  3. 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&timestamp=1633366859664


说明

  1. 根据步骤2dubbo协议体,构建出provider协议。

debug数据摘录:
在这里插入图片描述

附4:
对 三项核心方法进行核心说明

  • doLocalExport
  • Register实例构建
  • register(register,registerProviderUrl)

说明

  1. 通过SPI机制(协议protocol)确定执行类 , 执行类于本次学习中都是ZK;
  2. 都是zk的前提下,对于/dubbo 、/service 两组path下又为不同的实现类;
  3. 对应的实行类写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说明
  1. 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,具体流程是啥。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值