【Dubbo】 服务暴露

问题

  1. dubbo的服务提供者是怎么将服务暴露给注册中心(这里是说zk)
  2. 消费者是怎么从zk获取服务的地址,进行调用的

时序图

未命名文件.png
image.png

服务发布种类

本地发布

定义:本地发布是部署在jvm中, 在同一个服务,自己调用自己的接口没必要使用网络来通信

远程发布

暴露服务

(1) 只暴露服务端口:

  • 在没有注册中心,直接暴露提供者的情况下,即:
    • <dubbo:service regisrty="N/A" /> or <dubbo:registry address="N/A" />
  • ServiceConfig解析出的URL的格式为:
  • 基于扩展点的Adaptive机制,通过URL的"dubbo://"协议头识别,直接调用DubboProtocol的export()方法,打开服务端口。

(2) 向注册中心暴露服务:

暴露服务源码流程

2、ServiceBean实现了下面的接口
InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener, BeanNameAware

在spring容器启动的时候执行
实现了InitializingBean接口的afterPropertiesSet(),该接口为spring留给开发者的一个hook,系统启动只执行一次

afterPropertiesSet()里面主要干了什么事情呢
构建ProviderConfig 信息(如果集成了spring,这个是在DubboNamespaceHandler初始化的)
构建ApplicationConfig
构建ModuleConfig
构建RegistryConfig
构建MonitorConfig
构建ProtocolConfig

执行ServiceConfig的export()

public synchronized void export() {
        if (provider != null) {
            if (export == null) {
                export = provider.getExport();
            }
            if (delay == null) {
                delay = provider.getDelay();
            }
        }
        // 如果不需要暴露该服务,则就此结束
        if (export != null && ! export.booleanValue()) {
            return;
        }
        // 如果明确指定了想要延迟的时间差,则依赖线程休眠来完成延迟暴露,delay的值只有为-1或null才依赖spring的事件机制完成延迟暴露
        if (delay != null && delay > 0) {
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(delay);
                    } catch (Throwable e) {
                    }
                    doExport();
                }
            });
            thread.setDaemon(true);
            thread.setName("DelayExportServiceThread");
            thread.start();
        } else {
        // 重点在这里
            doExport();
        }
    }

private transient volatile boolean exported;

protected synchronized void doExport() {
        if (unexported) {
            throw new IllegalStateException("Already unexported!");
        }
        //如果发布了就不执行
        if (exported) {
            return;
        }
        // 设置暴露状态
        exported = true;
        if (interfaceName == null || interfaceName.length() == 0) {
            throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
        }
        checkDefault();
        if (provider != null) {
            if (application == null) {
                application = provider.getApplication();
            }
            if (module == null) {
                module = provider.getModule();
            }
            if (registries == null) {
                registries = provider.getRegistries();
            }
            if (monitor == null) {
                monitor = provider.getMonitor();
            }
            if (protocols == null) {
                protocols = provider.getProtocols();
            }
        }
        if (module != null) {
            if (registries == null) {
                registries = module.getRegistries();
            }
            if (monitor == null) {
                monitor = module.getMonitor();
            }
        }
        if (application != null) {
            if (registries == null) {
                registries = application.getRegistries();
            }
            if (monitor == null) {
                monitor = application.getMonitor();
            }
        }
        if (ref instanceof GenericService) {
            interfaceClass = GenericService.class;
            generic = true;
        } else {
            try {
                interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                        .getContextClassLoader());
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            checkInterfaceAndMethods(interfaceClass, methods);
            checkRef();
            generic = false;
        }
        if(local !=null){
            if(local=="true"){
                local=interfaceName+"Local";
            }
            Class<?> localClass;
            try {
                localClass = ClassHelper.forNameWithThreadContextClassLoader(local);
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            if(!interfaceClass.isAssignableFrom(localClass)){
                throw new IllegalStateException("The local implemention class " + localClass.getName() + " not implement interface " + interfaceName);
            }
        }
        if(stub !=null){
            if(stub=="true"){
                stub=interfaceName+"Stub";
            }
            Class<?> stubClass;
            try {
                stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub);
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            if(!interfaceClass.isAssignableFrom(stubClass)){
                throw new IllegalStateException("The stub implemention class " + stubClass.getName() + " not implement interface " + interfaceName);
            }
        }
        checkApplication();
        checkRegistry();
        checkProtocol();
        this.parameters = fixApplicationParams(this.parameters);
        appendProperties(this);
        checkStubAndMock(interfaceClass);
        if (path == null || path.length() == 0) {
            path = interfaceName;
        }
        doExportUrls();
    }
private void doExportUrls() {
  // 解析所有的注册中心地址
    List<URL> registryURLs = loadRegistries(true);  
    for (ProtocolConfig protocolConfig : protocols) {
        doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}

调用者如何调用服务

引用服务

(1) 直连引用服务:

  • 在没有注册中心,直连提供者的情况下,即:
    • <dubbo:reference url="dubbo://service-host/com.foo.FooService?version=1.0.0" />
  • ReferenceConfig解析出的URL的格式为:
  • 基于扩展点的Adaptive机制,通过URL的"dubbo://"协议头识别,直接调用DubboProtocol的refer()方法,返回提供者引用。

(2) 从注册中心发现引用服务:

拦截服务

  • 基于扩展点的Wrapper机制,所有的Protocol扩展点都会自动套上Wrapper类。
  • 基于ProtocolFilterWrapper类,将所有Filter组装成链,在链的最后一节调用真实的引用。
  • 基于ProtocolListenerWrapper类,将所有InvokerListener和ExporterListener组装集合,在暴露和引用前后,进行回调。
  • 包括监控在内,所有附加功能,全部通过Filter拦截实现。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,156评论 19 139
  • dubbo暴露服务有两种情况,一种是设置了延迟暴露(比如delay="5000"),另外一种是没有设置延迟暴露或者...
    加大装益达阅读 21,348评论 5 36
  • 人与人交往本质,是对等的相互交换 权利,地位,金钱,能力 每天都在上演,傲慢与偏见
    清水柠檬蜂蜜茶阅读 131评论 0 0
  • 给花瓶添了只耳朵,好像更具立体感和质感。 中午回家对画进行调整,最后的定稿画。
    ry影阅读 264评论 2 3
  • 电视荧屏都被《欢乐颂》霸屏了。生活在欢乐颂22楼性格境遇各异的5位姑娘,淋漓尽致的向我们展现了都市生活的全貌,各个...
    顾一宸阅读 2,590评论 22 66