dubbo服务的发布的入口也在ServiceBean中,实现ApplicationListener接口,在spring容器初始代启动完成之后,会回调接口的onApplicationEvent方法。
步骤
1. ServiceBean.onApplicationEvent()方法调用ServiceConfig.export()->doExport()
2. doExport方法中,会调用 checkApplication(), checkRegistry(), checkProtocol()等方法,
这些方法会检测 dubbo的配置是否在spring配置文件通过dubbo标签的方式生成,如果没有则会读取 dubbo.properties文件中的配置,并初始代。
3. 接下来调用ServiceConfig.doExportUrls()方法,首先会调用loadRegistries()方法加载注册中心,这里又调用了checkRegistry方法初始代,然后对RegistryConfig进行了读取和处理,封装成url列表返回。(对于多注册中心的配置,官方文档上说是根据逗号进行分隔,但读取代码发现需要以|进行分隔), 注意对于在标签中指定了registry属性的ServiceBean,会加载BeanDefinition时就加载了注册中心。
4. 接下来doExportUrls方法会遍历前面返回的url,并把服务发布到不同的注册中心中。
5. doExportUrlsFor1Protocol方法,首先会获取本机的ip发部服务的端口号,在dubbo.properties配置文件中指定了dubbo.protocol.host属性的,直接读取,否则能过InetAddress.getLocalHost().getHostAddress()获取。
6. 把dubbo的配置信息封装到map中,并生成对应的url.如下
http://10.0.5.71:8080/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&loadbalance=roundrobin&methods=sayHello&organization=dubbo&owner=william&pid=12108&side=provider×tamp=1484906734130
7. 根据服务具体实现,实现接口以及regitryUrl从代理工厂ProxyFactory获取代理Invoker(继承于AbstractProxyInvoker),它是对具体实现的一种代理。
8. Protocol.export(invoker) 暴露服务invoker, 调用Protocol$Adpative(生成适配类)的export方法
com.alibaba.dubbo.common.URL url = invoker.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.geExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class)
.getExtension(extName);
return extension.export(arg0)
9. Invoker包含上一步传入的RegistryUrl, registryUrl的protocol值为registry,接下来会调用RegistryProtocol去暴露服务。
Protocol有两个ProtocolListenerWrapper和ProtocolFilterWrapper对于协议为REGISTRY_PROTOCOL直接跳过,最终由RegistryProtocol处理export的过程
RegistryProtocol暴露服务过程
1. 执行 doLocalExport(originInvoker), 实际是调用DubboProtocol.export()暴露服务,启动提供者。
2. getRegistedProviderUrl(originInvoker)拿到提供者的地址,注册到注册中心。
3. 提供者订阅,即对这个服务提供者的地址进行监听,registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener),当节点发生发生变代时,注册中心会通知各节点。实际调用OverrideListener.notify方法,重新暴露服务。
4. 包装第一步中返回的exporter返回,并重写unexport方法,到服务反暴露时,
取消订阅。
下面详细看一看doLocalExport方法
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker){
//1. 从invoker中获取 提供者的url dubbo://172.30.18.26:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&loadbalance=roundrobin&methods=sayHello&organization=dubbo&pid=7376&side=provider×tamp=1486718882603
// 作为bounds中的key
String key = getCacheKey(originInvoker);
// 2.从bounds中获取exporter, 单例实现
ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
if (exporter == null) {
synchronized (bounds) {
exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
if (exporter == null) {
//3.根据invoker也提供者的地址实例代出Invoker的实现
final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
//4. 调用protocol.export暴露服务,返回 exporter,并存到bounds中,根据提供者的协议,调用具体的Protocol实现的export方法
exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);
bounds.put(key, exporter);
}
}
}
return (ExporterChangeableWrapper<T>) exporter;
}
DubboProtocol暴露服务过程
1. 根据url构建key,缓存exporter到exporterMap中
2. 调用openService->createServer打开服务,传入requestHandler
/默认使用netty
server = Exchangers.bind(url, requestHandler);
3. 返回exporter对象
4. ExchangeHandler.reply方法处理消费者的消息 ,调用invoker.invoke执行具体的方法