1. 项目背景与核心价值
钢琴演奏作为一门高度依赖手指灵活性与协调性的艺术形式,其技能天花板往往受限于人类生理结构的天然局限。传统训练方法虽然能通过长期练习提升技巧,但当遇到需要超高速连奏、复杂和弦组合或极端跨度段落时,即便是职业钢琴家也会面临物理极限的挑战。2025年1月Science Robotics期刊封面发表的这项研究,首次将外骨骼机械手技术引入音乐表演领域,通过生物力学增强与智能辅助系统,实现了对钢琴家演奏能力的突破性提升。
这套系统最革命性的创新在于:它不是简单地替代人类手指运动,而是通过实时分析演奏者的肌肉活动、触键力度和音乐意图,在保持艺术家原有演奏风格的前提下,对关键动作进行力学增强和精度补偿。实测数据显示,使用该设备的钢琴家在演奏李斯特《超技练习曲》等超高难度曲目时,八度连续跳音速度提升23%,复杂和弦准确率提高31%,而疲劳度降低40%——这些数据在音乐表演领域堪称颠覆性突破。
2. 系统架构与核心技术解析
2.1 生物信号捕捉子系统
系统在演奏者前臂部署了高密度肌电传感器阵列(128通道,采样率2kHz),配合光学动作捕捉系统(0.1mm精度)实时追踪手指运动轨迹。与传统运动捕捉不同,该方案特别优化了指尖触键瞬间的力反馈检测,采用专利的"压力-加速度复合算法",能在琴键接触前5ms预测触键力度,为后续机械响应赢得宝贵时间。
关键创新:开发了自适应滤波算法,有效消除演奏时手臂自然晃动带来的信号干扰,确保在剧烈运动场景下仍能保持95%以上的肌电信号识别准确率。
2.2 机械增强执行机构
手指外骨骼采用碳纤维-钛合金复合框架,单指驱动模块仅重8g却可输出15N的持续推力。特别设计的"双模传动系统"既支持刚性模式(用于需要绝对精准的快速音阶),也能切换为弹性模式(适合表现柔和的连奏效果)。每个关节配备高精度编码器(0.05°分辨率)和力矩传感器,形成闭环控制。
实际测试中发现,传统外骨骼常见的"机械迟滞"问题在这里被压缩到惊人的3ms以内——这得益于研究人员开发的"预加载缓冲算法",通过分析乐谱提前预判下一个音符所需的手指位置,实现近乎瞬时的状态切换。
2.3 智能辅助决策系统
系统的"AI演奏教练"模块内置了超过2000小时的世界级钢琴家演奏数据,能实时对比当前演奏与理想状态的差异。当检测到技巧瓶颈时(如颤音频率不足或琶音不均匀# 1. 概述
本文分享 Protocol 层。在 《精尽 Dubbo 源码分析 —— 核心流程一览》 一文中,我们了解到 Protocol 是服务域,它是 Invoker 暴露和引用的主功能入口,它负责 Invoker 的生命周期管理。
Dubbo 实现 Protocol 的代码如下:
友情提示:本文会多次提到 Invoker 这个概念,如果不熟悉的胖友,可以看看 《精尽 Dubbo 源码分析 —— 核心流程一览》「4. Invoker」 的简单介绍。
- AbstractProtocol ,实现 Protocol 接口的抽象类,实现了 Protocol 的公用方法,主要是服务暴露和服务引用的公用方法。
- DubboProtocol ,Dubbo 远程调用协议实现类。
- InjvmProtocol ,Dubbo 本地调用协议实现类。
- RegistryProtocol ,基于注册中心的发布服务实现类。
- Filter ,过滤器 Protocol 实现类。
- MockProtocol ,Dubbo 本地伪装协议实现类。
- ThriftProtocol ,Thrift 协议实现类。
2. Protocol
com.alibaba.dubbo.rpc.Protocol ,协议接口。代码如下:
code复制@SPI("dubbo")
public interface Protocol {
/**
* 暴露远程服务:<br>
* 1. 协议在接收请求时,应记录请求来源方地址信息,为RpcContext添加context<br>
* 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。<br>
* 3. export()传入的Invoker由框架实现并传入,协议不需要关心<br>
*
* @param <T> 服务的类型
* @param invoker 服务的执行体
* @return exporter 暴露服务的引用,用于取消暴露
* @throws RpcException 当暴露服务出错时抛出,比如端口已占用
*/
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
/**
* 引用远程服务:<br>
* 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行同URL远端export()传入的Invoker对象的invoke()方法。<br>
* 2. refer()返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请求<br>
* 3. 当url中有设置check=false时,连接失败不能抛出异常,并内部自动恢复。<br>
*
* @param <T> 服务的类型
* @param type 服务的类型
* @param url 远程服务的URL地址
* @return invoker 服务的本地代理
* @throws RpcException 当连接服务提供方失败时抛出
*/
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
/**
* 释放协议:<br>
* 1. 取消该协议所有已经暴露和引用的服务<br>
* 2. 释放协议所占用的所有资源,比如连接和端口<br>
* 3. 协议在释放后,依然能暴露和引用新的服务<br>
*/
void destroy();
}
@SPI("dubbo")注解,Dubbo SPI 拓展点,默认为"dubbo"。@Adaptive注解,基于 Dubbo SPI Adaptive 机制,加载对应的 Protocol 实现,使用URL.protocol属性。#export(invoker)方法,暴露远程服务。- 注意方法上的注释,特别是幂等、来源方地址信息。
#refer(type, url)方法,引用远程服务。- 注意方法上的注释,特别是本地代理、
check=false时连接失败不抛出异常。
- 注意方法上的注释,特别是本地代理、
#destroy()方法,释放协议。
3. AbstractProtocol
com.alibaba.dubbo.rpc.protocol.AbstractProtocol ,实现 Protocol 接口的抽象类,实现了 Protocol 的公用方法,主要是服务暴露和服务引用的公用方法。
3.1 属性
code复制/**
* Exporter 集合
*
* key: 服务键 {@link #serviceKey(URL)} 或 {@link URL#getServiceKey()} 。
* 不同协议会不同
*/
protected final Map<String, Exporter<?>> exporterMap = new ConcurrentHashMap<String, Exporter<?>>();
/**
* Invoker 集合
*/
//TODO SOFEREFENCE
protected final Set<Invoker<?>> invokers = new ConcurrentHashSet<Invoker<?>>();
-
exporterMap属性,Exporter 集合。其中,Key 为服务键,不同协议会不同,例如:- RegistryProtocol 使用
URL#getServiceKey()。 - DubboProtocol 使用
com.alibaba.dubbo.rpc.protocol.AbstractProtocol#serviceKey(URL)。
友情提示:Exporter 是什么?例如在 DubboProtocol 中,是 DubboExporter 。
- RegistryProtocol 使用
-
invokers属性,Invoker 集合。
3.2 服务暴露
code复制 1: @Override
2: public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
3: // 创建 Exporter 对象
4: final Exporter<T> exporter = createExporter(invoker);
5: // 添加到 `exporterMap`
6: addExporter(exporter);
7: return exporter;
8: }
9:
10: protected abstract <T> Exporter<T> createExporter(Invoker<T> invoker) throws RpcException;
11:
12: protected void addExporter(Exporter<?> exporter) {
13: exporterMap.put(serviceKey(exporter.getInvoker().getUrl()), exporter);
14: }
-
#export(invoker)实现方法,暴露服务。-
第 4 行:调用
#createExporter(invoker)抽象方法,创建 Exporter 对象。这是一个抽象方法,子类中实现。代码如下:code复制protected abstract <T> Exporter<T> createExporter(Invoker<T> invoker) throws RpcException; -
第 6 行:调用
#addExporter(exporter)方法,添加到exporterMap中。代码如下:code复制protected void addExporter(Exporter<?> exporter) { exporterMap.put(serviceKey(exporter.getInvoker().getUrl()), exporter); }-
其中,
#serviceKey(url)方法,获得服务键。代码如下:code复制protected static String serviceKey(URL url) { // 获得绑定的端口 int port = url.getParameter(Constants.BIND_PORT_KEY, url.getPort()); return serviceKey(port, url.getPath(), url.getParameter(Constants.VERSION_KEY), url.getParameter(Constants.GROUP_KEY)); } protected static String serviceKey(int port, String serviceName, String serviceVersion, String serviceGroup) { return ProtocolUtils.serviceKey(port, serviceName, serviceVersion, serviceGroup); }- x
-
-
3.3 服务引用
code复制 1: @Override
2: public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
3: // 创建 Invoker 对象
4: final Invoker<T> invoker = createInvoker(type, url);
5: // 添加到 `invokers`
6: addInvoker(invoker);
7: return invoker;
8: }
9:
10: protected abstract <T> Invoker<T> createInvoker(Class<T> type, URL url) throws RpcException;
11:
12: protected void addInvoker(Invoker<?> invoker) {
13: invokers.add(invoker);
14: }
-
#refer(type, url)实现方法,引用服务。-
第 4 行:调用
#createInvoker(type, url)抽象方法,创建 Invoker 对象。这是一个抽象方法,子类中实现。代码如下:code复制protected abstract <T> Invoker<T> createInvoker(Class<T> type, URL url) throws RpcException; -
第 6 行:调用
#addInvoker(invoker)方法,添加到invokers中。代码如下:code复制protected void addInvoker(Invoker<?> invoker) { invokers.add(invoker); }
-
3.4 服务销毁
code复制 1: @Override
2: public void destroy() {
3: // 销毁 `exporterMap`
4: for (Exporter<?> exporter : exporterMap.values()) {
5: exporter.unexport();
6: }
7: exporterMap.clear();
8: // 销毁 `invokers`
9: for (Invoker<?> invoker : invokers) {
10: if (invoker != null) {
11: invokers.remove(invoker);
12: invoker.destroy();
13: }
14: }
15: }
- 第 2 至 7 行:销毁
exporterMap。 - 第 8 至 14 行:销毁
invokers。
4. RegistryProtocol
com.alibaba.dubbo.registry.integration.RegistryProtocol ,实现 Protocol 接口,注册中心协议实现类。
4.1 属性
code复制// ========== 注册中心相关 ==========
/**
* 单例。在 Dubbo SPI 中,被初始化,有且仅有一次。
*/
private static RegistryProtocol INSTANCE;
/**
* 绑定关系集合。
*
* key:服务键,例如:`com.alibaba.dubbo.demo.DemoService:20880`
*/
private final Map<String, ExporterChangeableWrapper<?>> bounds = new ConcurrentHashMap<String, ExporterChangeableWrapper<?>>();
/**
* 注册中心工厂
*/
private RegistryFactory registryFactory;
/**
* 已连接的注册中心
*
* key:{@link URL#toServiceString()}
*/
private final Map<String, Registry> registries = new ConcurrentHashMap<String, Registry>();
/**
* 启动时进行注册
*/
//To solve the problem of RMI repeated exposure port conflicts, the services that have been bound are cached, and the filter is no longer bound.
// 用于解决rmi重复暴露端口冲突的问题,已经暴露过的服务不再重新暴露
// providerurl <--> exporter
private final Map<String, Exporter<?>> overrideListeners = new ConcurrentHashMap<String, Exporter<?>>();
// ========== 协议相关 ==========
/**
* 协议自适应实现类,通过 Dubbo SPI 自动注入。
*/
private Protocol protocol;
/**
* 代理工程
*/
private ProxyFactory proxyFactory;
-
INSTANCE静态属性,单例。通过#getInstance()静态方法,返回单例对象。代码如下:code复制public static RegistryProtocol getInstance() { return INSTANCE; } public void setProtocol(Protocol protocol) { this.protocol = protocol; } public void setProxyFactory(ProxyFactory proxyFactory) { this.proxyFactory = proxyFactory; } public void setRegistryFactory(RegistryFactory registryFactory) { this.registryFactory = registryFactory; }-
在 Dubbo SPI 加载时,创建 RegistryProtocol 对象时,会进行赋值。代码如下:
code复制private RegistryProtocol() { INSTANCE = this; }- 因此,
INSTANCE有且仅有一次初始化。
- 因此,
-
protocol属性,proxyFactory属性,registryFactory属性,通过 Dubbo SPI 进行自动注入。
-
-
bounds属性,绑定关系集合。其中,Key 为服务键,例如:com.alibaba.dubbo.demo.DemoService:20880。 -
registries属性,已连接的注册中心。 -
overrideListeners属性,启动时进行注册。从目前代码上来看,用于处理服务覆盖。
4.2 服务暴露
4.2.1 export
code复制 1: @Override
2: public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
3: // 暴露服务
4: // export invoker
5: final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
6:
7: // 获得注册中心 URL
8: URL registryUrl = getRegistryUrl(originInvoker);
9:
10: // 获得注册中心对象
11: // registry provider
12: final Registry registry = getRegistry(originInvoker);
13:
14: // 获得服务提供者 URL
15: final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
16:
17: //to judge to delay publish whether or not
18: boolean register = registedProviderUrl.getParameter("register", true);
19:
20: // 向注册中心注册服务提供者(自己)
21: ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registedProviderUrl);
22:
23: if (register) {
24: register(registryUrl, registedProviderUrl);
25: }
26:
27: // 使用 OverrideListener 对象,订阅配置规则
28: // Subscribe the override data
29: // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call the same service. Because the subscribed is cached key with the name of the service, it causes the subscription information to cover.
30: final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
31: final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
32: overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
33: registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
34: //Ensure that a new exporter instance is returned every time export
35: return new DestroyableExporter<T>(exporter, originInvoker, overrideSubscribeUrl, registedProviderUrl);
36: }
-
第 5 行:调用
#doLocalExport(originInvoker)方法,暴露服务。详细解析,见 「4.2.2 doLocalExport」 。 -
第 8 行:调用
#getRegistryUrl(originInvoker)方法,获得注册中心 URL 。代码如下:code复制private URL getRegistryUrl(Invoker<?> originInvoker) { URL registryUrl = originInvoker.getUrl(); if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) { // protocol String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY); registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY); } return registryUrl; }- 例如:
registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?protocol=zookeeper。
- 例如:
-
第 12 行:调用
#getRegistry(originInvoker)方法,获得注册中心对象。详细解析,见 「4.2.3 getRegistry」 。 -
第 15 行:调用
#getRegistedProviderUrl(originInvoker)方法,获得服务提供者 URL 。代码如下:code复制private URL getRegistedProviderUrl(final Invoker<?> originInvoker) { URL providerUrl = getProviderUrl(originInvoker); //The address you see at the registry return providerUrl.removeParameters(getFilteredKeys(providerUrl)) // 移除 .hide 为前缀的参数 .removeParameter(Constants.MONITOR_KEY) // monitor .removeParameter(Constants.BIND_IP_KEY) // bind.ip .removeParameter(Constants.BIND_PORT_KEY) // bind.port .removeParameter(QOS_ENABLE) // qos.enable .removeParameter(QOS_PORT) // qos.port .removeParameter(ACCEPT_FOREIGN_IP); // qos.accept.foreign.ip } private URL getProviderUrl(final Invoker<?> origininvoker) { String export = origininvoker.getUrl().getParameterAndDecoded(Constants.EXPORT_KEY); // export if (export == null || export.length() == 0) { throw new IllegalArgumentException("The registry export url is null! registry: " + origininvoker.getUrl()); } return URL.valueOf(export); }- 从
originInvoker.url的export参数,获得服务提供者的 URL 。 - 移除多余的参数。注意,不是
#getFilteredKeys(providerUrl)方法。
- 从
-
第 18 行:配置项
register,服务提供者是否注册到配置中心。 -
第 21 行:调用
ProviderConsumerRegTable#registerProvider(invoker, registryUrl, registedProviderUrl)方法,在ProviderConsumerRegTable中,注册服务提供者(自己)。这样 Dubbo 就能实时管理服务提供者的在线状态。 -
第 23 至 25 行:调用
#register(registryUrl, registedProviderUrl)方法,向注册中心注册服务提供者(自己)。详细解析,见 「4.2.4 register」 。 -
第 30 至 33 行:使用 OverrideListener 对象,向注册中心订阅配置规则。详细解析,见 《精尽 Dubbo 源码解析 —— 集群容错(六)之 Configurator 实现》 。
-
第 35 行:创建 DestroyableExporter 对象。详细解析,见 「4.2.5 DestroyableExporter」 。
4.2.2 doLocalExport
code复制 1: private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker) {
2: // 获得在 `bounds` 中的缓存 Key
3: String key = getCacheKey(originInvoker);
4: // 从 `bounds` 获得,是不是已经暴露过服务
5: ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
6: if (exporter == null) {
7: synchronized (bounds) {
8: exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
9: // 未暴露过,进行暴露服务
10: if (exporter == null) {
11: // 创建 Invoker 为 DelegateProviderMetaDataInvoker 对象
12: final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
13: // 暴露服务,创建 Exporter 对象
14: // 使用 创建的Exporter对象 + originInvoker ,创建 ExporterChangeableWrapper 对象
15: exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);
16: // 添加到 `bounds`
17: bounds.put(key, exporter);
18: }
19: }
20: }
21: return exporter;
22: }
-
第 3 行:调用
#getCacheKey(originInvoker)方法,获得在bounds中的缓存 Key 。代码如下:code复制private String getCacheKey(Invoker<?> originInvoker) { URL providerUrl = getProviderUrl(originInvoker); return providerUrl.removeParameters("dynamic", "enabled").toFullString(); }- 移除
"dynamic""enabled"配置项,用于保证不同参数,但是是相同服务。
- 移除
-
第 5 行:从
bounds获得,是不是已经暴露过服务。若已经暴露,直接返回 ExporterChangeableWrapper 对象。 -
第 12 行:创建 Invoker 为 DelegateProviderMetaDataInvoker 对象。详细解析,见 「4.2.6 InvokerDelegete」 。
-
第 15 行:调用
Protocol#export(invoker)方法,暴露服务,创建 Exporter 对象。此处使用的protocol,是在RegistryProtocol被 Dubbo SPI 加载时,通过#setProtocol(protocol)方法,注入的 DubboProtocol 对象,详细解析,见 「4.1 属性」 。- 也就是说,虽然我们使用的是 RegistryProtocol 的
#export(invoker)方法,实际调用的是 DubboProtocol 的#export(invoker)方法。是不是很神奇~
- 也就是说,虽然我们使用的是 RegistryProtocol 的
-
第 15 行:使用创建的 Exporter 对象 +
originInvoker,创建 ExporterChangeableWrapper 对象。详细解析,见 「4.2.7 ExporterChangeableWrapper」 。 -
第 17 行:添加到
bounds。
4.2.3 getRegistry
code复制protected Registry getRegistry(final Invoker<?> originInvoker) {
// 获得注册中心 URL
URL registryUrl = getRegistryUrl(originInvoker);
// 获得 Registry 对象
return registryFactory.getRegistry(registryUrl);
}
- 调用
RegistryFactory#getRegistry(url)方法,获得 Registry 对象。
4.2.4 register
code复制public void register(URL registryUrl, URL registedProviderUrl) {
// 获得 Registry
Registry registry = registryFactory.getRegistry(registryUrl);
// 注册服务
registry.register(registedProviderUrl);
}
- 调用
Registry#register(url)方法,向注册中心注册服务提供者(自己)。
4.2.5 DestroyableExporter
code复制private class DestroyableExporter<T> implements Exporter<T> {
/**
* 缓存的 Exporter 对象
*/
private Exporter<T> exporter;
/**
* 原始 Invoker 对象
*/
private Invoker<T> originInvoker;
/**
* 订阅 URL 对象
*/
private URL subscribeUrl;
/**
* 服务提供者 URL 对象
*/
private URL registerUrl;
public DestroyableExporter(Exporter<T> exporter, Invoker<T> originInvoker, URL subscribeUrl, URL registerUrl) {
this.exporter = exporter;
this.originInvoker = originInvoker;
this.subscribeUrl = subscribeUrl;
this.registerUrl = registerUrl;
}
@Override
public Invoker<T> getInvoker() {
return exporter.getInvoker();
}
@Override
public void unexport() {
// 取消订阅
try {
Registry registry = registryFactory.getRegistry(subscribeUrl);
registry.unsubscribe(subscribeUrl, new OverrideListener(subscribeUrl, originInvoker));
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
// 取消注册
try {
Registry registry = registryFactory.getRegistry(registerUrl);
registry.unregister(registerUrl);
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
// 取消暴露
try {
exporter.unexport();
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
}
#unexport()方法,取消暴露。- 第 27 至 33 行:取消订阅。
- 第 35 至 41 行:取消注册。
- 第 43 至 47 行:取消暴露。
4.2.6 InvokerDelegete
code复制private static class InvokerDelegete<T> extends InvokerWrapper<T> {
/**
* 服务提供者 URL
*/
private URL providerUrl;
public InvokerDelegete(Invoker<T> invoker, URL url, URL providerUrl) {
super(invoker, url);
this.providerUrl = providerUrl;
}
public URL getProviderUrl() {
return providerUrl;
}
}
4.2.7 ExporterChangeableWrapper
code复制private class ExporterChangeableWrapper<T> implements Exporter<T> {
/**
* 可变的 Exporter 对象
*/
private Exporter<T> exporter;
/**
* 原始 Invoker 对象
*/
private Invoker<T> originInvoker;
private ExporterChangeableWrapper(Exporter<T> exporter, Invoker<T> originInvoker) {
this.exporter = exporter;
this.originInvoker = originInvoker;
}
public Invoker<T> getOriginInvoker() {
return originInvoker;
}
@Override
public Invoker<T> getInvoker() {
return exporter.getInvoker();
}
@Override
public void unexport() {
String key = getCacheKey(this.originInvoker);
// 移除出 `bounds`
bounds.remove(key);
// 取消暴露
exporter.unexport();
}
}
4.3 服务引用
4.3.1 refer
code复制 1: @Override
2: @SuppressWarnings("unchecked")
3: public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
4: // 获得注册中心 URL
5: url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
6: // 获得注册中心对象
7: Registry registry = registryFactory.getRegistry(url);
8: if (RegistryService.class.equals(type)) {
9: return proxyFactory.getInvoker((T) registry, type, url);
10: }
11:
12: // 将 url 的 query 参数,作为 `refer` 参数集合。
13: // group="a,b" or group="*"
14: Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
15: String group = qs.get(Constants.GROUP_KEY);
16: if (group != null && group.length() > 0) {
17: if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
18: // 执行服务引用
19: return doRefer(getMergeableCluster(), registry, type, url);
20: }
21: }
22: // 执行服务引用
23: return doRefer(cluster, registry, type, url);
24: }
-
第 5 行:获得注册中心 URL 。
-
第 7 行:调用
RegistryFactory#getRegistry(url)方法,获得注册中心对象。 -
第 8 至 10 行:当
type = RegistryService.class时,直接创建注册中心的 Invoker 对象。因为RegistryService是注册中心自己本身有的服务。 -
第 14 行:将
url的refer参数,作为qs参数集合。 -
第 15 至 21 行:当
group为多组或"*"时,调用Cluster$Adaptive#join(directory)方法,使用可合并的 Cluster 实现类,合并多个服务组。 -
第 23 行:调用
#doRefer(cluster, registry, type, url)方法,执行服务引用。详细解析,见 「4.3.2 doRefer」 。
4.3.2 doRefer
code复制 1: private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
2: // 创建 RegistryDirectory 对象
3: RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
4: directory.setRegistry(registry);
5: directory.setProtocol(protocol);
6: // 设置服务方法
7: // all attributes of REFER_KEY
8: Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
9: URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
10: if (!Constants.ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(Constants.REGISTER_KEY, true)) {
11: // 注册服务消费者
12: registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
13: Constants.CHECK_KEY, String.valueOf(false)));
14: }
15: // 订阅 providers、configurators、routers 节点
16: directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
17: Constants.PROVIDERS_CATEGORY
18: + "," + Constants.CONFIGURATORS_CATEGORY
19: + "," + Constants.ROUTERS_CATEGORY));
20: // 创建 Invoker 对象
21: Invoker invoker = cluster.join(directory);
22: // 向本地注册表,注册消费者
23: ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
24: return invoker;
25: }
-
第 3 至 5 行:创建 RegistryDirectory 对象。详细解析,见 《精尽 Dubbo 源码解析 —— 集群容错(六)之 Directory 实现》 。
-
第 7 至 9 行:创建
subscribeUrl,用于服务消费者注册和订阅。 -
第 10 至 14 行:调用
Registry#register(url)方法,向注册中心注册服务消费者(自己)。 -
第 15 至 19 行:调用
RegistryDirectory#subscribe(url)方法,向注册中心订阅providersconfiguratorsrouters节点数据。通过这样的方式,服务消费者就可以从注册中心中,获取到服务提供者、配置规则、路由规则等数据。 -
第 21 行:调用
Cluster#join(directory)方法,创建 Invoker 对象。详细解析,见 《精尽 Dubbo 源码解析 —— 集群容错(二)之 Cluster 实现》 。 -
第 23 行:调用
ProviderConsumerRegTable#registerConsumer(invoker, url, subscribeUrl, directory)方法,向本地注册表,注册消费者。这样 Dubbo 就能实时管理服务消费者的在线状态。
4.4 服务销毁
code复制@Override
public void destroy() {
// 销毁所有服务暴露
for (Exporter<?> exporter : bounds.values()) {
exporter.unexport();
}
bounds.clear();
// 销毁所有服务引用
for (Invoker<?> invoker : invokers) {
if (invoker != null) {
invokers.remove(invoker);
try {
if (
