文章目录

  • ​​服务export入口​​
  • ​​ServiceBean 完成自身暴露​​
  • ​​doExportUrls多个注册中心URL暴露服务​​
  • ​​单协议向多注册中心暴露​​
  • ​​总结​​
  • ​​扩展​​

服务export入口

  • ApplicationListener触发服务暴露
public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener
implements Ordered {
private final DubboBootstrap dubboBootstrap;
public DubboBootstrapApplicationListener() {
this.dubboBootstrap = DubboBootstrap.getInstance();
}
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
dubboBootstrap.start();
}
}

public class DubboBootstrap extends GenericEventListener {
public DubboBootstrap start() {
...... 删除其他代码
服务暴露
exportServices();
服务引用
referServices();
return this;
}
}

暴露所有服务

private void exportServices() {
...... 删除其他代码
configManager是在 spring加载bd 到new 的时候将sc 加入进来
configManager.getServices().forEach(sc -> {
sc就是dubbo: service对应的bean 类型未ServiceBean
sc.export();
exportedServices.add(sc);
});
}

ServiceBean 完成自身暴露

  • ServiceBean继承ServiceConfig
  • 通过doexport完成暴露
public class ServiceConfig<T> extends ServiceConfigBase<T> {
通过doExport完成暴露
public synchronized void export() {
...... 删除其他代码
doExport();
}
}
protected synchronized void doExport() {
if (exported) {
return;
}
exported = true;

if (StringUtils.isEmpty(path)) {
path = interfaceName;
}
完成服务暴露
doExportUrls();
}

doExportUrls多个注册中心URL暴露服务

  • 获取应用中配置的所有注册中心集合[一般一个zk集群]
  • 获取应用中配置的协议集合[一般一个dubbo协议]
  • 遍历所有的协议,处理每一个协议向所有的注册中心的注册
  • ConfigValidationUtils.loadRegistries(this, true);涉及到只注册不订阅,只订阅不注册等相关特性
private void doExportUrls() {
registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService
加载注册中心 URL 数组 用于远程暴露使用【registryURLs有多少 取决于配置多少<dubbo:registry />
远程暴露根据该url进行服务注册
List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
// 循环 如dubbo,grpc 等`protocols` ,向注册中心分组暴露服务。
for (ProtocolConfig protocolConfig : protocols) {
* 包含了本地和远程两种暴露方式。
* 在下文中,我们会看到,本地暴露不会向注册中心注册服务
* 因为仅仅用于 JVM 内部本地调用,内存中已经有相关信息
暴露协议到远程注册中心
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}

单协议向多注册中心暴露

  • 构建url参数map
  • 根据Service配置完成直连暴露,本地暴露与远程暴露
  • 构建Invoker
  • 通过协议保留Invoker
/**
* 组装url 暴露本地和远程 注册注册中心
* 基于单个协议,暴露服务
* 删除大量代码 有兴趣可以参阅https://github.com/renxinlin/dubboStudy
* @param protocolConfig
* @param registryURLs
*/
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {

我们知道url 一般是协议://ip:port/uri?param=val & param1=val1
step-1: map构建[这里的map就是param=val 集合]
Map<String, String> map = new HashMap<String, String>();
ServiceConfig.appendRuntimeParameters(map);
...... 删除大量代码map构建代码

step-2: URL构建 url是dubbo协议的灵魂,其强大的自适应扩展机制就是根据url的key,val实现
String host = findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = findConfigedPorts(protocolConfig, name, map);
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);


step-3:判断下本地暴露还是远程暴露 一般默认不配置就是同时暴露本地和远程
String scope = url.getParameter(SCOPE_KEY);
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {

if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
step-3.1 本地暴露
exportLocal(url);
}
// 远程暴露
// export to remote if the config is not local (export to local only when config is local)
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
registryURLs 为null 表示直连服务消费者 在load注册中心url的时候 如果配置成<dubbo:registry address="N/A"/> 表示服务消费者直连服务提供者
if (CollectionUtils.isNotEmpty(registryURLs)) {
step3-2: 向所有的注册中心进行暴露
for (URL registryURL : registryURLs) {
step4: 使用 ProxyFactory 创建 Invoker 对象
创建 Invoker 对象。该 Invoker 对象,执行 #invoke(invocation) 方法时,内部会调用 Service 对象( ref )对应的调用方法。
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));

创建 DelegateProviderMetaDataInvoker 对象
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

step5: 使用 Protocol 暴露 Invoker 对象
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
该exporters容器用于exporter生命周期管理 并不参与服务调用
exporters.add(exporter);
}
} else {
用于被服务消费者直连服务提供者,主要用于开发测试环境使用。
/**
* 在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,
* 点对点直联方式,将以服务接口为单位,忽略注册中心的提供者列表,A 接口配置点对点,不影响 B 接口从注册中心获取列表。
*/
此时url没有注册中心信息,类似于dubbo:// 192.168.102:57:18000 只暴露本地 消费者可以配置url = dubbo:// 192.168.102:57:18000 进行服务直连
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

Exporter<?> exporter = Protocol.export(wrapperInvoker);
exporters.add(exporter);
}
...... 删除其他代码
}
}
this.urls.add(url);
}

总结

  • ConfigValidationUtils.loadRegistries涉及了只订阅不注册等特性
  • 暴露时根据有无注册中心判断是直连暴露还是本地暴露与远程暴露
  • PROTOCOL协议是一个自适应扩展类,涉及dubbo sp机制

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

扩展

<dubbo:registry address=“zookeeper://127.0.0.1:2181” subscribe=“true” register=“true”/>

注册订阅特性配置

<dubbo:reference id=“demoService” interface=“com.renxl.demo.DemoService” url=“dubbo://127.0.0.1:8888”/>

直连提供者