讲服务暴露之前,我们先思考下几个问题:
- 服务暴露都做了哪些事
- 本地暴露和远程暴露的区别
- 为什么需要本地暴露
先说一个整体流程,文档是最好的说明:dubbo文档
服务提供者暴露一个服务的详细过程
文档说我们从ServiceBean开始,OK,那我们就从ServiceBean的继承开始,发现它是实现ApplicationListener,这是spring的事件机制,监听spring容器初始化完成,从onApplicationEvent开始
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (isDelay() && !isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
debug,继续走export(),发现走到ServiceConfig类中
public synchronized void export() {
//这块,就是看是否配置了延迟暴露
if (delay != null && delay > 0) {
delayExportExecutor.schedule(new Runnable() {
@Override
public void run() {
doExport();
}
}, delay, TimeUnit.MILLISECONDS);
} else {
doExport();
}
}
然后看doExport()方法:
protected synchronized void doExport() {
....
//上面就是检查interface的合法性,ProviderConfig、ApplicationConfig 等核心配置类对象是否为空
//检测本地存根配置等等,这些大家可以自行分析,我们主要说这个doExportUrls()
doExportUrls();
...
}
进入这个方法:
private void doExportUrls() {
//加载注册中心,可以有多个注册中心
List<URL> registryURLs = loadRegistries(true);
for (ProtocolConfig protocolConfig : protocols) {
//遍历不同的协议,并在每个协议下导出服务
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
我们的注册中心地址,是一个List(因为dubbo是多注册中心)
我们的配置协议(当然也是多协议,缺省是dubbo协议)
接下来我们开始分析doExportUrlsFor1Protocol(protocolConfig, registryURLs):
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
...
//这个是把要暴露的接口方法给组成数组,然后在下面以,分割,放如map中
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
if (methods.length == 0) {
logger.warn("NO method found in service interface " + interfaceClass.getName());
map.put(Constants.METHODS_KEY, Constants.ANY_VALUE);
} else {
map.put(Constants.METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
}
}
//版本、时间戳、方法名以及各种配置对象的字段信息放入到 map 中,map 中的内容将作为 URL 的查询字符串。构建好 map 后,紧接着是获取上下文路径、主机名以及端口号等信息。最后将 map 和主机名等数据传给 URL 构造方法创建 URL 对象
URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
...
if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
//本地暴露
exportLocal(url);
}
//远程暴露
if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
...
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
}
}
}
this.urls.add(url);
}
我们分别看下URL和map参数:
总结:
这个方法onApplicationEvent做的事情有哪些:
- 加载注册中心,根据不同协议进行不同的服务导出(dubbo是多协议,多注册中心)
- 在服务导出的时候,我们进行各种的检查配置,然后为map赋值和组装URL
- map和URL组装好之后,根据scope进行本地暴露或远程暴露或都暴露等
目前为止,我们只看到了ServiceConfig的身影,和ref服务类,别着急,接下来我们开始分析重要的本地暴露和远程暴露