Dubbo服务暴露流程

在这里插入图片描述

官网描述

在有注册中心,需要注册提供者地址的情况下,ServiceConfig 解析出的 URL 的格式为: registry://registry-host/org.apache.dubbo.registry.RegistryService?export=URL.encode(“dubbo://service-host/com.foo.FooService?version=1.0.0”),

基于扩展点自适应机制,通过 URL 的 registry:// 协议头识别,就会调用 RegistryProtocol 的 export() 方法,将 export 参数中的提供者 URL,先注册到注册中心。

再重新传给 Protocol 扩展点进行暴露: dubbo://service-host/com.foo.FooService?version=1.0.0,然后基于扩展点自适应机制,通过提供者 URL 的 dubbo:// 协议头识别,就会调用 DubboProtocol 的 export() 方法,打开服务端口。

源码分析

Dubbo入口DubboNamespaceHandler.init() 解析xml配置

this.registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));

ServiceBean
实现接口ApplicationContextAware,在setApplicationContext方法中通过反射将当前类addApplicationListener添加到Spring监听器中

public void setApplicationContext(ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
    ...
    Method method = applicationContext.getClass().getMethod("addApplicationListener", ApplicationListener.class);
    method.invoke(applicationContext, this); // 将自己添加到spring监听器中
    this.supportedApplicationListener = true;
    ...
}

ServiceBean实现了ApplicationListener接口,通过onApplicationEvent暴露服务

public void onApplicationEvent(ContextRefreshedEvent event) {
    if (this.isDelay() && !this.isExported() && !this.isUnexported()) {
        if (logger.isInfoEnabled()) {
            logger.info("The service ready on spring started. service: " + this.getInterface());
        }
        this.export();
    }
}

export方法调用ServiceConfig的doExport方法,然后调用doExportUrls方法

private void doExportUrls() {
	// 获取所有的注册中心地址
    List<URL> registryURLs = this.loadRegistries(true);
    Iterator i$ = this.protocols.iterator();
	// 逐个暴露
    while(i$.hasNext()) {
        ProtocolConfig protocolConfig = (ProtocolConfig)i$.next();
        this.doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}

方法内会先调用this.exportLocal(var22); 进行本地服务暴露

private void exportLocal(URL url) {
    if(!"injvm".equalsIgnoreCase(url.getProtocol())) {
        URL local = URL.valueOf(url.toFullString()).setProtocol("injvm").setHost("127.0.0.1").setPort(0);
        ServiceClassHolder.getInstance().pushServiceClass(this.getServiceClass(this.ref));
        Exporter exporter = protocol.export(proxyFactory.getInvoker(this.ref, this.interfaceClass, local));
        this.exporters.add(exporter);
        logger.info("Export dubbo service " + this.interfaceClass.getName() + " to local registry");
    }
}

方法内根据配置拼装url
url示例 dubbo://10.9.167.208:10089/com.zgjiuan.api.service.AuditDTOService?accepts=500&anyhost=true&application=dubbo-service&bind.ip=10.9.167.208&bind.port=10089&default.delay=-1&default.retries=0&default.timeout=9000&default.version=test&delay=-1&dubbo=2.0.2&generic=false&interface=com.zgjiuan.api.service.AuditDTOService&methods=add,listIds,audit,get,count,update,list,delete,alter&pid=56452&qos.enable=false&revision=1.0-SNAPSHOT&side=provider&timestamp=1604654507865

// 通过url构建invoker
Invoker<?> invoker = proxyFactory.getInvoker(this.ref, this.interfaceClass, registryURL.addParameterAndEncoded("export", url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
Exporter<?> exporter = protocol.export(wrapperInvoker);

invoker本质就是对url转化成对象存储,可以执行调用的一个对象
断点追踪,来到了RegistryProtocol的export方法

public <T> Exporter<T> export(Invoker<T> originInvoker) throws RpcException {
	// 执行暴露
    RegistryProtocol.ExporterChangeableWrapper<T> exporter = this.doLocalExport(originInvoker);
    URL registryUrl = this.getRegistryUrl(originInvoker);
    Registry registry = this.getRegistry(originInvoker);
    URL registeredProviderUrl = this.getRegisteredProviderUrl(originInvoker);
    boolean register = registeredProviderUrl.getParameter("register", true);
    ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl);
    if (register) {
        this.register(registryUrl, registeredProviderUrl);
        ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
    }

    URL overrideSubscribeUrl = this.getSubscribedOverrideUrl(registeredProviderUrl);
    RegistryProtocol.OverrideListener overrideSubscribeListener = new RegistryProtocol.OverrideListener(overrideSubscribeUrl, originInvoker);
    this.overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
    // 订阅服务变更 overrideSubscribeListener是变化通知类
    registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
    return new RegistryProtocol.DestroyableExporter(exporter, originInvoker, overrideSubscribeUrl, registeredProviderUrl);
}

private <T> RegistryProtocol.ExporterChangeableWrapper<T> doLocalExport(Invoker<T> originInvoker) {
    String key = this.getCacheKey(originInvoker);
    RegistryProtocol.ExporterChangeableWrapper<T> exporter = (RegistryProtocol.ExporterChangeableWrapper)this.bounds.get(key);
    if (exporter == null) {
        synchronized(this.bounds) {
            exporter = (RegistryProtocol.ExporterChangeableWrapper)this.bounds.get(key);
            if (exporter == null) {
                Invoker<?> invokerDelegete = new RegistryProtocol.InvokerDelegete(originInvoker, this.getProviderUrl(originInvoker));
                // 由于使用dubbo协议 所以会执行DubboProtocol.export方法
                exporter = new RegistryProtocol.ExporterChangeableWrapper(this.protocol.export(invokerDelegete), originInvoker);
                this.bounds.put(key, exporter);
            }
        }
    }
	// 返回一个封装的对象
    return exporter;
}

exporter对象封装完成之后,调用 this.register(registryUrl, registeredProviderUrl);
使用zk客户端创建了临时节点
/dubbo/com.zgjiuan.api.service.AuditDTOService/providers/dubbo%3A%2F%2F10.9.167.208%3A10089%2Fcom.zgjiuan.api.service.AuditDTOService%3Faccepts%3D500%26anyhost%3Dtrue%26application%3Ddubbo-service%26default.delay%3D-1%26default.retries%3D0%26default.timeout%3D9000%26default.version%3Dtest%26delay%3D-1%26dubbo%3D2.0.2%26generic%3Dfalse%26interface%3Dcom.zgjiuan.api.service.AuditDTOService%26methods%3Dadd%2ClistIds%2Caudit%2Cget%2Ccount%2Cupdate%2Clist%2Cdelete%2Calter%26pid%3D388%26revision%3D1.0-SNAPSHOT%26side%3Dprovider%26timestamp%3D1604658114263

protected void doRegister(URL url) {
    try {
        this.zkClient.create(this.toUrlPath(url), url.getParameter("dynamic", true));
    } catch (Throwable var3) {
        throw new RpcException("Failed to register " + url + " to zookeeper " + this.getUrl() + ", cause: " + var3.getMessage(), var3);
    }
}

export完成后,开始订阅服务变化 registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
调用zkClient创建永久节点/dubbo/com.zgjiuan.api.service.AuditDTOService/configurators

export方法会根据不同的协议,调用对应的方法 示例dubbo协议,所以进入DubboProtocol.export方法

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    URL url = invoker.getUrl();
    String key = serviceKey(url); // 接口全类名:版本:端口号
    DubboExporter<T> exporter = new DubboExporter(invoker, key, this.exporterMap);
    this.exporterMap.put(key, exporter);
    Boolean isStubSupportEvent = url.getParameter("dubbo.stub.event", false);
    Boolean isCallbackservice = url.getParameter("is_callback_service", false);
    if (isStubSupportEvent && !isCallbackservice) {
        String stubServiceMethods = url.getParameter("dubbo.stub.event.methods");
        if (stubServiceMethods != null && stubServiceMethods.length() != 0) {
            this.stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
        } else if (this.logger.isWarnEnabled()) {
            this.logger.warn(new IllegalStateException("consumer [" + url.getParameter("interface") + "], has set stubproxy support event ,but no stub methods founded."));
        }
    }
	// 开启Netty服务
    this.openServer(url);
    this.optimizeSerialization(url);
    return exporter;
}

// 使用map存储server 如果不存在则创建服务
private void openServer(URL url) {
    String key = url.getAddress();
    boolean isServer = url.getParameter("isserver", true);
    if (isServer) {
        ExchangeServer server = (ExchangeServer)this.serverMap.get(key);
        if (server == null) {
            this.serverMap.put(key, this.createServer(url));
        } else {
            server.reset(url);
        }
    }
}

// 创建netty服务
private ExchangeServer createServer(URL url) {
    url = url.addParameterIfAbsent("channel.readonly.sent", Boolean.TRUE.toString());
    url = url.addParameterIfAbsent("heartbeat", String.valueOf(60000));
    String str = url.getParameter("server", "netty");
    if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
        throw new RpcException("Unsupported server type: " + str + ", url: " + url);
    } else {
        url = url.addParameter("codec", "dubbo");
        ExchangeServer server;
        try {
       		// url对象包含了协议 地址 端口 path等信息
            server = Exchangers.bind(url, this.requestHandler);
        } catch (RemotingException var5) {
            throw new RpcException("Fail to start server(url: " + url + ") " + var5.getMessage(), var5);
        }

        str = url.getParameter("client");
        if (str != null && str.length() > 0) {
            Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
            if (!supportedTypes.contains(str)) {
                throw new RpcException("Unsupported client type: " + str);
            }
        }
        return server;
    }
}

官网时序图

在这里插入图片描述

总结

服务暴露过程,就是先解析配置文件,生成URL,现在本地使用injvm协议暴露,然后生成invoker对象,在转化exporter对象,整个过程涉及了封装url,创建netty服务,通过zkClient在zkServer上创建节点,并订阅节点变化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值