Dubbo 服务发布源码分析
export() 进行服务发布
- 首先会获得注册中心集合列表以及协议集合列表,遍历协议中心列表进行多协议发布服务
- 服务发布时,会根据配置文件的参数信息封装成map对象,最后将map对象转换成URL地址的参数
- 根据URL地址的协议头:registry://xxx ,会在 protocol.exort(url) 中,进入到RegistryProtocol#export()方法中,在该方法中主要做了一下几步
- 做本地发布:会根据协议将协议头由 registry:// 转换成 dubbo:// 协议,再次调用Protocol#export进入到DubboProtocol#exprot 中,这里是通过Netty 打开了一个监听
- 会根据注册中心的获得一个Registry, 即ZookeeperRegistry 并将服务注册到 zk上。
- 最后是发起一个订阅的功能,主要是能动态的去改变服务提供方提供的服务信息。
服务发布及注册流程
ServiceBean
发布(export)入口
org.springframework.context.support.AbstractApplicationContext#refresh -> finishRefresh() -> publishEvent() -> … -> com.alibaba.dubbo.config.spring.ServiceBean#onApplicationEvent()
public class ServiceBean<T> extends ServiceConfig<T>
implements InitializingBean, DisposableBean,
ApplicationContextAware, ApplicationListener, BeanNameAware {
// InitializingBean
//为接口 bean 提供了初始化方法的方式,凡是实现了该接口的类,在初始化 bean 的时候会执行该方法
@Override
public void afterPropertiesSet() throws Exception {
//发布入口
if (!isDelay()) {
export();
}
}
// DisposableBean ,bean 被销毁的时候,spring 容器会自动执行 destroy 方法,比如释放资源
@Override
public void destroy() throws Exception {}
// ApplicationContextAware
//实现了这个接口的类,当 spring 容器初始化的时候,会自动的将 ApplicationContext 注入进来
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {}
// ApplicationListener ,ApplicationEvent 事件监听,spring 容器启动后会发一个事件通知
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
//发布入口
export();
}
// BeanNameAware 获得自身初始化时,本身 bean 的 id 属性
@Override
public void setBeanName(String s) {}
}
服务发布主要做了什么?
从 com.alibaba.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol
核心入口开始说起
进行服务发布之前,会获得配置的注册中心集合、以及多协议集合,进行多协议的发布
//发布服务
@SuppressWarnings({"unchecked", "rawtypes"})
private void doExportUrls() {
//获得注册中心的url地址
List<URL> registryURLs = loadRegistries(true);
//根据 配置的多个协议,进行依次发布
for (ProtocolConfig protocolConfig : protocols) {
//发布服务根据协议
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
//发布服务的核心入口
private void doExportUrlsFor1Protocol
(ProtocolConfig protocolConfig, List<URL> registryURLs) {
// 执行具体的远程调用
// registryURL = registry://192.168.1.1.1...
// proxyFactory = ProxyFactory$Adpative
// 通过 ProxyFactory 获得 invoker 对象 ,
// invoker = JavassistProxyFactory$1 代理对象 {url=registry://127.0.0.1..}
Invoker<?> invoker = proxyFactory.
getInvoker(ref, (Class) interfaceClass,
registryURL.
addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
// 服务发布
// protocol :Protocol$Adpative
// invoker.url:registry://192.168.1.1.1...
Exporter<?> exporter = protocol.export(invoker);
exporters.add(exporter);
}
Protocol$Adpative
类信息
/**
* @author : guaoran
* @Description : <br/>
* 根据 createAdaptiveExtensionClass() 动态获得的 适配器扩展点
* @date :2019/1/7 13:31
*/
public class Protocol$Adpative implements Protocol {
//....
//服务发布
public Exporter export(Invoker arg0) throws RpcException {
if (arg0 == null) throw new IllegalArgumentException("Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("Invoker argument getUrl() == null");
//当url=registry://192.168.45.123....时,extension = RegistryProtocol
URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException
("Fail to get extensionProtocol) name from url(" +
url.toString() + ") use keys([protocol])");
// 当url=registry://192.168.45.1...时,
// extension = ProtocolListenerWrapper(ProtocolFilterWrapper(RegistryProtocol))
// 当url=dubbo://192.168.45.1...时,
// extension = ProtocolListenerWrapper(ProtocolFilterWrapper(DubboProtocol)) ,
// todo 为什么 extension 不是 [DubboProtocol] ,
// todo 因为 在 Protocol 进行loadFile 的时候,会加载 到 clazz.getConstructor(type);
// 不报错则 cachedWrapperClasses 不为空,
// todo 所以在 getExtension("dubbo") 的时候会首先获得 DubboProtocol,
// 然后会进行封装成 wrapper
Protocol extension = (Protocol)
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
return extension.export(arg0);
}
//客户端引用
public Invoker refer(Class arg0, URL arg1) throws RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
URL url = arg1;
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException
("Fail to get extension(Protocol) name from url(" +
url.toString() + ") use keys([protocol])");
//此时通过url = registry 时
// extension = ProtocolListenerWrapper(ProtocolFilterWrapper(RegistryProtocol))
Protocol extension = (Protocol)
ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
}
继续接着 Exporter<?> exporter = protocol.export(invoker);
走,此时会调用 Protocol$Adpative#export()方法,会通过 getExtensionLoader("registry")
方法获得到 进行Wrapper 包装后的 RegistryProtocol
,进入到该类的 RegistryProtocol#export()
方法中。
RegistryProtocol#export()
- 做本地发布,在
DubboProtocol
打开一个 Netty 服务进行监听 - 获得一个 ZookeeperRegistry 的注册中心的连接,并将服务注册到 zk 上
/dubbo-demo/com.guaoran.source.dubbo.demo.IHelloService/providers/dubbo%3A%2F%2F19..
- 开启一个订阅
在 zk 上注册一个/dubbo-demo/com.guaoran.source.dubbo.demo.IHelloService/configurators
节点
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
//export invoker
// 做本地发布
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
//registry provider
// 获得一个注册中心的连接 ZookeeperRegistry
final Registry registry = getRegistry(originInvoker);
// 获得需要注册到 zk 上的协议地址,即:dubbo://xxx
final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
// 注册到 zk :zkClient.create() , ZookeeperRegistry 的父类
registry.register(registedProviderUrl);
// 订阅override数据
// FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,
// FIXME 因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
final OverrideListener overrideSubscribeListener =
new OverrideListener(overrideSubscribeUrl);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
//保证每次export都返回一个新的exporter实例
return new Exporter<T>() {
}