前面博客已经说了dubbo整合spring,不过还没有涉及到dubbo的核心服务导出,服务引入,服务调用的客户端和服务端的通信流程,本篇博客我们先来说dubbo的服务导出源码
入口如下
private static void startWithExport() throws InterruptedException {
ServiceConfig<DemoServiceImpl> service = new ServiceConfig<>();
service.setInterface(DemoService.class);
service.setRef(new DemoServiceImpl());
service.setApplication(new ApplicationConfig("dubbo-demo-api-provider"));
service.setRegistry(new RegistryConfig("zookeeper://127.0.0.1:2181"));
service.export();//可以直接从这里看
System.out.println("dubbo service started");
new CountDownLatch(1).await();
}
如果你要从整合spring的入口看服务导出的逻辑,那么在DubboBootstrapApplicationListener 的dubboBootstrap.start方法,不过最终都会调用到ServiceConfig#export
ServiceConfig#export
dubbo的配置可以用java-D, 控制台全局配置,控制台应用配置,当前应用全局配置,局部配置,@Service,@Reference都可以写配置,所以服务导出大量代码都在处理这些配置的优先级,这些我们都不看,直接看最核心的代码即可
public synchronized void export() {
if (bootstrap == null) {
bootstrap = DubboBootstrap.getInstance();
bootstrap.initialize();
}
checkAndUpdateSubConfigs();
serviceMetadata.setVersion(getVersion());
serviceMetadata.setGroup(getGroup());
serviceMetadata.setDefaultGroup(getGroup());
serviceMetadata.setServiceType(getInterfaceClass());
serviceMetadata.setServiceInterfaceName(getInterface());
serviceMetadata.setTarget(getRef());
if (!shouldExport()) {
return;
}
//延迟导出
if (shouldDelay()) {
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
} else {
//核心导出代码
doExport();
}
//发布ServiceConfigExportedEvent事件
exported();
}
doExport------>doExportUrls------->doExportUrlsFor1Protocol
doExportUrlsFor1Protocol这个方法内部实在太长了,我们只看下面的核心代码
//ServiceConfig有个api setScope(scope)
String scope = url.getParameter(SCOPE_KEY);
//我们一般不会配置scope,所以scope是null
//如果我们配置了none,不导出服务
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
//如果scope不是remote, 就本地导出一下
if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
exportLocal(url);
}
//如果scope不是local,就导出到注册中心,注册url到注册中心
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
//如果配置了注册中心地址
if (CollectionUtils.isNotEmpty(registryURLs)) {
for (URL registryURL : registryURLs) {
//如果协议是injvm,不要导出
if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
continue;
}
//生成代理对象
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
//包装一层
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
//真正的服务导出代码,远程导出
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
exporters.add(exporter);
}
}
private void exportLocal(URL url) {
//进来的url开头是dubbo://
//生成一个本地url
URL local = URLBuilder.from(url)
.setProtocol(LOCAL_PROTOCOL)
.setHost(LOCALHOST_VALUE)
.setPort(0)
.build();
//local url开头是injvm://
//本地导出
Exporter<?> exporter = PROTOCOL.export(
PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local));
exporters.add(exporter);
}
PROTOCOL.export
注意此时协议是injvm
PROTOCOL是一个Protocol$Adaptive类,这个类是dubbo自己用字符串拼接出来,再用javassist compiler出来的类,它可以根据你调用的方法的参数URL,url放入不同的参数调用不同的实现类,比如url的协议如果是injvm,你调用export方法就会调用到InjvmProtocol#export,如果协议是dubbo,就会调用DubboProtocol#export,这是dubbo的spi机制,在笔者dubbo的第一篇源码非常详细的介绍了,笔者也自己手写了dubbo的spi源码,一并在那篇博客。
当前协议是injvm,dubbo的spi的aop机制会在InjvmProtocol前面包装几个类,当你调用PROTOCOL.export时,实际调用逻辑如下:
QosProtocolWrapper#export----->ProtocolFilterWrapper#export------>ProtocolListenerWrapper#export------>InjvmProtocol#export
QosProtocolWrapper#export
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//如果协议是registry或者service-discover-registry,就开启服务质量监控
if (UrlUtils.isRegistry(invoker.getUrl())) {
startQosServer(invoker.getUrl());
return protocol.export(invoker);
}
//injvm直接交给下一个,即ProtocolFilterWrapper
return protocol.export(invoker);
}
ProtocolFilterWrapper#export
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//如果协议是registry或者service-discover-registry,直接交给下一个
if (UrlUtils.isRegistry(invoker.getUrl())) {
return protocol.export(invoker);
}
//injvm需要构造执行链条,这个涉及到dubbo的spi的@Activate注解
//拿provider组的执行链,例如回声测试,泛化调用,不是主线,直接跳过
return protocol.export(buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
}
ProtocolListenerWrapper#expor
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//如果协议是registry或者service-discover-registry,直接交给下一个
if (UrlUtils.isRegistry(invoker.getUrl())) {
return protocol.export(invoker);
}
//拿到监听器的链,回调exported方法,不是主线,直接跳过
return new ListenerExporterWrapper<T>(protocol.export(invoker),
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), EXPORTER_LISTENER_KEY)));
}
InjvmProtocol#export
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//直接return InjvmExporter,并没有注册url到注册中心,也没有开启netty server
return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
}
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
此时协议是registry,执行export执行链路如下:
QosProtocolWrapper#export----->ProtocolFilterWrapper#export------>ProtocolListenerWrapper#export------>RegistryProtocol#export
前面三个看过了,直接看第四个
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
//zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-demo-annotation-provider......
URL registryUrl = getRegistryUrl(originInvoker);
//dubbo://192.168.238.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=dubbo-demo-annotation-provider&bind.ip=192.168.238.1&bind.port=20880
URL providerUrl = getProviderUrl(originInvoker);
//开启nettyServer
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
// url to registry
final Registry registry = getRegistry(originInvoker);
final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
//真正注册url到注册中心
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
if (register) {
register(registryUrl, registeredProviderUrl);
}
return new DestroyableExporter<>(exporter);
}
doLocalExport(originInvoker, providerUrl);
providerUrl此时协议是dubbo,执行export执行链路如下:
QosProtocolWrapper#export----->ProtocolFilterWrapper#export------>ProtocolListenerWrapper#export------>DubboProtocol#export
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//dubbo://....
URL url = invoker.getUrl();
//org.apache.dubbo.demo.DemoService:20880,导出服务的唯一标志
String key = serviceKey(url);
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
//存到map
exporterMap.put(key, exporter);
//本地存根stub,省略
//开启nettyServer
openServer(url);
optimizeSerialization(url);
return exporter;
}