dubbo源码-服务导出

dubbo源码-服务导出

  • 前言
  • 源码
    • 本地导出:exportLocal(url);
    • 远程导出
  • 总结

前言

前面博客已经说了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);
                    }
                } 
                

本地导出:exportLocal(url);

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;
    }

总结

dubbo源码-服务导出_第1张图片

你可能感兴趣的:(dubbo,dubbo,dubbo源码,服务导出,spi,export)