Dubbo架构设计与源码解析(二) 服务注册
创始人
2024-04-27 23:07:55
0

一、Dubbo简介

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

二、SPI机制

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



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 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为例)







三、服务注册

1、服务注册流程





2、服务注册类图详解





3、服务注册步骤

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

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

private static void startWithExport() throws InterruptedException {//初始化配置ServiceConfig 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=1663049763199List registryURLs = ConfigValidationUtils.loadRegistries(this, true);int protocolConfigNum = protocols.size();for (ProtocolConfig protocolConfig : protocols) {//组装pathKey : org.apache.dubbo.demo.DemoServiceString 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 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=1663051456562URL 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()));//wrapperDelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);//服务注册(此时URL头部协议变成了register,实际会调用RegistryProtocol)Exporter exporter = PROTOCOL.export(wrapperInvoker);exporters.add(exporter);
}# PROXY_FACTORY
public  Invoker getInvoker(T proxy, Class type, URL url) {// 动态代理类生成,反射生成实例final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);return new AbstractProxyInvoker(proxy, type, url) {@Overrideprotected 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  Exporter export(final Invoker 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=1663052353098providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);// export invokerfinal ExporterChangeableWrapper exporter = doLocalExport(originInvoker, providerUrl);// 此时Registry实例默认是ZookeeperRegistryfinal Registry registry = getRegistry(originInvoker);final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);// decide if we need to delay publishboolean register = providerUrl.getParameter(REGISTER_KEY, true);if (register) {//底层调用ZK,创建node节点registry.register(registeredProviderUrl);}....
}# RegistryProtocol : doLocalExport
private  ExporterChangeableWrapper doLocalExport(final Invoker originInvoker, URL providerUrl) {String key = getCacheKey(originInvoker);return (ExporterChangeableWrapper) bounds.computeIfAbsent(key, s -> {Invoker invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);//此时会调用DubboProtocol进行exporter封装return new ExporterChangeableWrapper<>((Exporter) protocol.export(invokerDelegate), originInvoker);});
}
# DubboProtocol : export
public  Exporter export(Invoker invoker) throws RpcException { ......// export service.String key = serviceKey(url);//exporter封装DubboExporter exporter = new DubboExporter(invoker, key, exporterMap);exporterMap.put(key, exporter);......//开启服务监听openServer(url);optimizeSerialization(url);return exporter;
}

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

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

# RegistryProtocol : export
public  Exporter export(final Invoker 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=1663052353098providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);// export invokerfinal ExporterChangeableWrapper exporter = doLocalExport(originInvoker, providerUrl);// 此时Registry实例默认是ZookeeperRegistryfinal Registry registry = getRegistry(originInvoker);final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);// decide if we need to delay publishboolean register = providerUrl.getParameter(REGISTER_KEY, true);if (register) {//底层调用ZK,创建node节点registry.register(registeredProviderUrl);}....
}

四、总结

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

相关内容

热门资讯

原创 存... “钱存银行,50万以内绝对安全”。 这句话你一定听过,但很多人只知其一,不知其二。 2015年《存款...
美银CEO判断:特朗普关税政策... 智通财经获悉,美国银行首席执行官Brian Moynihan表示,尽管2025年的关税措施曾冲击美国...
牛市早报|财政部:2026年继... 【市场数据】 12月26日收盘,上证综指涨0.1%,报3963.68点;科创50指数跌0.24%,报...
原创 其... 大家都知道,对于新能源汽车来说,动力电基本上可以说是最基础的东西了,如果动力电池不行,那么这辆车在设...
茅台称尽最大努力防止价格炒作 ... 来源:第一财经 备受关注的茅台2026年全国经销商联谊会于12月28日下午举行。2025年在经历了...
从数据合规到生态构建,大模型企... 界面新闻记者 | 伍洋宇 界面新闻编辑 | 崔宇 随着全球范围内人工智能基础模型的竞争加速,中国...
特斯拉推自动驾驶出租车清洁费制... 2025-12-28 09:40:16 作者:狼叫兽 12月28日,特斯拉在其自动驾驶出租车服务...
俄罗斯或持续放宽货币政策 俄罗斯中央银行在12月19日举行的2025年最后一次董事会上决定继续降息,同时向金融市场发出维持较紧...
西安市长安区司法局引镇司法所开... 阳光讯(记者 侯菲)近日,西安市长安区司法局引镇司法所联合派出所、村委会等单位,开展了社区矫正和安置...
货币政策工具加力支持 □ 本报记者 詹 超 【场景】 昆山开发区,苏州清陶动力科技有限公司车间的固态电池生产线嗡嗡作响,金...