dubbo架构设计与源码解析服务注册

author author     2022-12-21     311

关键词:

一、Dubbo简介

Dubbo是一款典型的高扩展、高性能、高可用的RPC微服务框架,用于解决微服务架构下的服务治理与通信问题。其核心模块包含【RPC通信】【服务治理】,其中服务治理又分为服务注册与发现、服务容错、负载均衡、流量调度等。今天将重点介绍Dubbo的服务注册与发现

二、SPI机制

在介绍服务注册发现之前,先简单介绍一下贯穿整个Dubbo源码,也是Dubbo实现自适应扩展的核心--SPI机制,下图为Dubbo SPI实现的简单类图。



Dubbo架构设计与源码解析(二)



1、Dubbo SPI原理:通过读取相应的配置文件找到具体实现类,然后通过以下两种方式实例化对象:(1)通过自适应动态字节码编译技术,生成相应的动态代理类,(2)利用反射机制实现实例化。相较于Java SPI,Dubbo SPI实现了内部的IoC和Aop

2、Dubbo SPI 优点:(1)高扩展:用户可以根据实际业务需求扩展相应的实现模块,包含字节码编译技术、rpc协议、通信方式、注册方式等,(2)解耦:通过封装SPI调用机制,架构上实现了上层应用与底层逻辑之间的解耦,为高扩展提供了支撑条件

3、Dubbo SPI 常用样例(以getExtension和getAdaptiveExtension为例)

配置文件内容
test1=com.dubbo.demo.service.TestServiceimpl
test2=com.dubbo.demo.service.TestServiceImpl2

一、通过getExtension方法生成实例
ExtensionLoader<TestService> extensionLoader = ExtensionLoader.getExtensionLoader(TestService.class);
TestService t1 = extensionLoader.getExtension("test1");
TestService t2 = extensionLoader.getExtension("test2");

二、通过getAdaptiveExtension生成实例(方法中需要@Adaptive注解,参数会对URL校验)
TestService testService = ExtensionLoader.getExtensionLoader(TestService.class).getAdaptiveExtension();
URL url = new URL("test", "localhost", 8080, new String[]"test.service", "test1");
testService.sayHello("bbb", url);

调用getAdaptiveExtension方法最终会生成相应的代理类,最终生成的代理类会根据URL参数里面的protocol决定(以内部Protocol为例



Dubbo架构设计与源码解析(二)





三、服务注册

1、服务注册流程



Dubbo架构设计与源码解析(二)



2、服务注册类图详解



Dubbo架构设计与源码解析(二)



3、服务注册步骤

(1)步骤一:初始化配置(类图:抽象Config与初始化配置)

首先需要实例化ServiceConfig实例,声明“注册接口、接口实例、注册中心配置”,其中“ServiceBean”是实现Spring与Dubbo整合的桥梁。然后会由DubboBootstrap调用initialize方法实现configManager和Environment的初始化,其中就包括将ServiceConfig中的配置转换成内部封装的协议(ApplicationModel、ProviderModel等

private static void startWithExport() throws InterruptedException 
//初始化配置
ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>();
service.setInterface(DemoService.class);
service.setRef(new DemoServiceImpl());
service.setApplication(new ApplicationConfig("dubbo-demo-api-provider"));
service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
//服务注册入口
service.export();
public synchronized void export() 
if (bootstrap == null)
bootstrap = DubboBootstrap.getInstance();
// compatible with api call.
if (null != this.getRegistry())
bootstrap.registries(this.getRegistries());

//初始化配置()
bootstrap.initialize();

......
if (shouldDelay())
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
else
//服务注册
doExport();


exported();

(2)步骤二:组装URL

根据初始化配置组转注册接口服务的URL。其中URL也是Dubbo内部通过@Adaptive注解实现SPI的核心,通过修改URL的头部协议(如:register、dubbo、injvm等),在调用

private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
PROTOCOL.export(wrapperInvoker)

该方法的时候,会根据不同的协议切换不通的实现类,实现了Dubbo技术架构与业务逻辑的解耦。

private void doExportUrls() 
//组装后的URL格式样例
//registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-api-provider&dubbo=2.0.2&pid=26212®istry=zookeeper×tamp=1663049763199
List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);

int protocolConfigNum = protocols.size();
for (ProtocolConfig protocolConfig : protocols)
//组装pathKey : org.apache.dubbo.demo.DemoService
String pathKey = URL.buildKey(getContextPath(protocolConfig)
.map(p -> p + "/" + path)
.orElse(path), group, version);
//保存接口服务
repository.registerService(pathKey, interfaceClass);
//服务注册
doExportUrlsFor1Protocol(protocolConfig, registryURLs, protocolConfigNum);

(3)步骤三:Invoker封装(类图:Ref -> Invoker)

通过内置的动态字节码编译(默认javassist)生成Invoker代理类,然后通过反射机制生成Wrapper实例。其中Invoker是Dubbo的核心模型,Invoker是Dubbo中的实体域,也就是真实存在的。其他模型都向它靠拢或转换成它

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs, int protocolConfigNum) 
......
//组装新的URL
//dubbo://2.0.0.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-api-provider&bind.ip=2.0.0.1&bind.port=20880&default=true&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHelloAsync&pid=46528&release=&service.name=ServiceBean:/org.apache.dubbo.demo.DemoService&side=provider×tamp=1663051456562
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
......
//Invoker封装
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass,
registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
//wrapper
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

//服务注册(此时URL头部协议变成了register,实际会调用RegistryProtocol)
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
exporters.add(exporter);


# PROXY_FACTORY
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url)
// 动态代理类生成,反射生成实例
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf($) < 0 ? proxy.getClass() : type);
return new AbstractProxyInvoker<T>(proxy, type, url)
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);

;

(4)步骤四:Exporter封装(类图:Invoker-> Exporter)

此时会依次调用RegistryProtocol 、DubboProtocol 将Invoker封装成Exporter,并将封装后的Exporter存储到本地map中(类似于spring bean)。然后会调用底层通信服务(默认netty)进行端口监听,此时会通过责任链模式封装Exchanger与Transporter,用于处理网络传输消息的编码/解码。

# RegistryProtocol : export
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException
......
//此时URL头部协议已变成dubbo
//dubbo://2.0.0.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-api-provider&bind.ip=2.0.0.1&bind.port=20880&default=true&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHelloAsync&pid=56036&release=&service.name=ServiceBean:/org.apache.dubbo.demo.DemoService&side=provider×tamp=1663052353098
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
// export invoker
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

// 此时Registry实例默认是ZookeeperRegistry
final Registry registry = getRegistry(originInvoker);

final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);

// decide if we need to delay publish
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
if (register)
//底层调用ZK,创建node节点
registry.register(registeredProviderUrl);

....


# RegistryProtocol : doLocalExport
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl)
String key = getCacheKey(originInvoker);

return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s ->
Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
//此时会调用DubboProtocol进行exporter封装
return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
);
# DubboProtocol : export
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException
......
// export service.
String key = serviceKey(url);
//exporter封装
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
exporterMap.put(key, exporter);
......
//开启服务监听
openServer(url);
optimizeSerialization(url);

return exporter;

(5)步骤五:注册服务节点

封装Exporter并开启服务端口监听后,会调用注册中心(默认Zookeeper)注册服务节点信息

# RegistryProtocol : export
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException
......
//此时URL头部协议已变成dubbo
//dubbo://2.0.0.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-api-provider&bind.ip=2.0.0.1&bind.port=20880&default=true&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHelloAsync&pid=56036&release=&service.name=ServiceBean:/org.apache.dubbo.demo.DemoService&side=provider×tamp=1663052353098
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
// export invoker
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

// 此时Registry实例默认是ZookeeperRegistry
final Registry registry = getRegistry(originInvoker);

final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);

// decide if we need to delay publish
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
if (register)
//底层调用ZK,创建node节点
registry.register(registeredProviderUrl);

....

四、总结

至此,Dubbo服务注册的整体流程已大致结束,文中如有不当或者错误观点,欢迎大家评论区指出。感兴趣的同学,可以关注后续“Dubbo架构设计与源码解析”系列的文章。

dubbo架构设计与源码解析责任链模式

作者:周可强一、责任链模式简介1、责任链模式定义责任链(ChainofResponsibility)模式的定义:为了避免请求发送者与多个请求处理者耦合在一起,于是将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;... 查看详情

dubbo整体架构

...册中心、监控中心二、dubbo详细流程调用图三、dubbo分层架构图Dubbo框架设计一共划分了10个层,而最上面的Service层是留给实际想要使用Dubbo开发分布式服务的开发者实现业务逻辑的接口层。图中左边淡蓝背景的为服务消费方使用... 查看详情

dubbo源码解析-spi

dubbo源码解析-SPI机制架构体系框架介绍概述Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的RPC实现服务的输出和输入功能,可以和Spring框架无缝集成。Dubbo是一款高性能、轻量级的开源JavaRPC框架... 查看详情

dubbo源码解析:服务暴露与发现(代码片段)

dubbo源码解析-服务暴露与发现概述dubbo是一个简单易用的RPC框架,通过简单的提供者,消费者配置就能完成无感的网络调用。那么在dubbo中是如何将提供者的服务暴露出去,消费者又是如何获取到提供者相关信息的呢&#... 查看详情

dubbo源码学习系列动手写dubbo核心原理(代码片段)

...提升,也会让我去更注重自己的代码质量!一、 Dubbo架构详解    理解Dubbo前,最好先手动画一下dubbo的架构图,画图理解架构是最清晰有效地方式。各模块的职责:注册中心: 提供服务发现与注册功能,如果服务... 查看详情

微服务之架构技术选型与设计

参考技术A本文主要介绍了架构技术选型与设计-微服务选型,Springcloud实现采用的技术,希望对您的学习有所帮助。架构技术选型与设计-DUBBODubbo,是阿里巴巴服务化治理的核心框架,并被广泛应用于阿里巴巴集团的各成员站点(... 查看详情

有赞服务注册与发现架构演进(代码片段)

有赞服务注册与发现架构演进一、概述二、接口级服务注册与发现2.1架构2.2问题三、接口级服务注册与应用级服务发现3.1架构3.2应用级服务发现解析3.3优化3.3.1服务发现延迟聚合推送3.3.2服务发现预加载3.3.3客户端接口与应用映射... 查看详情

dubbo架构设计详解

...源的分布式服务框架,它最大的特点是按照分层的方式来架构,使用这种方式可以使各个层之间解耦合(或者最大限度地松耦合)。从服务模型的角度来看,Dubbo采用的是一种非常简单的模型,要么是提供方提供服务,要么是消... 查看详情

dubbo架构设计及入门案例(代码片段)

...能容错和负载均衡,以及服务自动注册和发现。1.1.2运行架构dubbo运行架构如下图示节点角色说明节点角色说明Provider暴露服 查看详情

说一下dubbo的工作原理?注册中心挂了可以继续通信吗?

...码上说明注册中心挂了还是可以继续通信的Dubbo调用流程架构图流程说明:1.Provider(提供者)绑定指定端口并启动服务2.提供者连接注册中心,并发本机IP、端口、应用信息和提供服务信息发送至注册中心存储3.Consumer(消费者),连... 查看详情

探花交友day00—探花交友前置dubbo(代码片段)

目录1、Dubbo的前世今生2、Dubbo的快速入门2.1、Dubbo的基本架构2.2、Nacos2.3、管理后台2.4、入门案例2.5、代码优化3、Dubbo高级特性3.2、超时与重试3.3、启动检查3.4、多版本3.5、负载均衡4、SpringCloud整合Dubbo4.1、功能概述4.2、入门案例... 查看详情

dubbo--服务注册与发布原理分析(代码片段)

文章目录1、解析配置1.1检查配置1.2多协议多注册中心导出服务1.3组装URL2.导出Dubbo服务2.1Invoker创建过程2.2导出服务到本地2.3导出服务到远程2.3.1服务导出1.doLocalExport2.DubboProtocol的export3.DubboExporter.openServer(url)4.DubboExporter.createServer(U... 查看详情

dubbo源码解析-spi(代码片段)

dubbo源码解析-SPI机制架构体系框架介绍概述Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的RPC实现服务的输出和输入功能,可以和Spring框架无缝集成。Dubbo是一款高性能、轻量级的开源Java... 查看详情

dubbo概念架构(代码片段)

1dubbo简介ApacheDubbo是一款微服务开发框架,它提供了RPC通信与微服务治理两大关键能力。这意味着,使用Dubbo开发的微服务,将具备相互之间的远程发现与通信能力,同时利用Dubbo提供的丰富服务治理能力,可... 查看详情

apachedubbo服务发布源码分析(代码片段)

1、源码分析思考按照对于dubbo的理解,如果要实现服务发布和注册,需要做哪些事情?配置文件解析或者注解解析服务注册启动netty服务实现远程监听2、Dubbo对于Spring的扩展最早我们使用Spring的配置,来实现dubbo服务的... 查看详情

apachedubbo服务发布源码分析(代码片段)

1、源码分析思考按照对于dubbo的理解,如果要实现服务发布和注册,需要做哪些事情?配置文件解析或者注解解析服务注册启动netty服务实现远程监听2、Dubbo对于Spring的扩展最早我们使用Spring的配置,来实现dubbo服务的... 查看详情

dubbo3终极特性「云原生三中心架构」带你探索dubbo3体系下的配置中心和元数据中心注册中心的原理及开发实战(上)

Dubb3的应用级服务发现Dubbo3提供了全新的应用级服务发现模型,该模型在设计与实现上区别于Dubbo2的接口级服务发现模型。概括来说,Dubbo3引入的应用级服务发现主要有以下优势适配云原生微服务变革。云原生时代的基础设施能... 查看详情

dubbo源码解析08_dubbo与spring结合

Dubbo与Spring结合基于dubbo.jar内的META-INF/spring.handlers配置,Spring在遇到dubbo名称空间时,会回调DubboNamespaceHandler。所有dubbo的标签,都统一用DubboBeanDefinitionParser进行解析,基于一对一属性映射,将XML标签解析为Bean对象。在ServiceConfig... 查看详情