DUBBO服务启动过程

Dubbo的启动主要是发布服务的过程,起到核心作用的就是ServiceConfig(ServiceConfig就是我们在Dubbo的配置文件中配置的dubbo:service这些配置项对应的实体类)。服务的启动初始位置也基本是在这里,下面我们来看看具体的实现内容。

讲基本内容前首先理清楚几个名词概念:

Invoker:Invoker的概念我们在动态代理的时候就接触过,中文的意思大概是执行者,这里其实可以理解为具体方法的执行者。其核心内容大致如下:

Class getInterface(); Result invoke(Invocation invocation) throws RpcException; URL getUrl();

通过以上的三个方法们就可以执行到具体的方法并且获得方法的执行结果。通过getUrl获得需要执行的方法具体实现细节,主要是获得具体的ref;其次就是组装方法的参数信息等等,这些信息在invocation里面都有封装;最后通过执行invoke方法触发具体的方法并返回结果。从这里可以看出Invoker是具体方法执行的最后一个守关者,获得了Invoker,就获得了具体接口的代码,然后执行代理就可以。

Invoker仅仅是作为一个代理的门面,其不仅可以代表本地执行Duubo调用的代理,还可以充当RPC时候的代理,更是可以将自己包装成一个多个Invoker聚合而成的代理(主要是处理集群的一些策略,包括负载均衡和路由等)。

Exporter:服务暴露的过程中会将Invoker转换成Exporter(暴露者),及Exporter其实包含了Invoker,主要是用于不同层面的服务发布。 其实Dubbo 还有一些比较重要的对象,像Protocol,Exchanger等等。我认为在这里直接说明不太合适,所以等到我们用到之后再开始说明。

  1. 核心的属性信息

一些基本的属性:group,version,interfaceName,interfaceClass,timeout等等。我们凡是可以在dubbo:service上配置的属性都在ServiceConfig中可以找得到对应的属性;

//dubbo对应的服务发布协议,这里可以清楚地看到Dubbo在这里使用的自己的spi机制,来保证灵活性。(至于SPI机制的具体实现,之后有机会的话会讲到,简单理解就是通过getExtensionLoader获得对应类的扩展类实现类)

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

private final List urls = new ArrayList(); private final List<Exporter> exporters = new ArrayList>();

2.服务暴露过程

对于服务暴露来说,在ServiceConfig里面的初始方法就是export()方法了,下面我们从export方法开始来看看: public synchronized void export() { if (provider != null) { //默认取provider的配置 if (export == null) { export = provider.getExport(); } if (delay == null) { delay = provider.getDelay(); } } //如果export设置为false的话就直接返回 if (export != null && ! export.booleanValue()) { return; } //如果设置延迟时间的话就延迟指定时间然后进行暴露 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(); //一切暴露的核心还都是要看doExport方法。 } }

protected synchronized void doExport() {
    // 防止服务多次暴露
     
    // 设置默认的基本属性
     
    // 针对泛化接口做单独处理
    if (ref instanceof GenericService) {
        interfaceClass = GenericService.class;
        if (StringUtils.isEmpty(generic)) {
            generic = Boolean.TRUE.toString();
        }
    } else {
        try {//通过反射初始化接口(interfaceName是实现类的全称)
            interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                    .getContextClassLoader());
        } catch (ClassNotFoundException e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
        //检查定义的方法是否为接口中的方法
        checkInterfaceAndMethods(interfaceClass, methods);
        //检查引用不为空,并且引用必需实现接口
        checkRef();
        //如果到这一步的话说明类实现是自己定义的,所以设置generic为false
        generic = Boolean.FALSE.toString();
    }
     
    // 处理Local和Stub代理处理
     
    // 检查Application,Registry,Protocol的配置情况
     
    //将配置的属性绑定到当前对象
    appendProperties(this);
     
    //针对Local,Stub和Mock进行校验
     
    //上面的操作主要是做一些检验和初始化的操作,没有涉及到具体的暴露服务逻辑
     
    doExportUrls();
}
 
private void doExportUrls() {
    //取到注册中心的URL
    List<URL> registryURLs = loadRegistries(true);
    for (ProtocolConfig protocolConfig : protocols) {
        //根据配置的通信协议将服务暴露到注册中心
        doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }
}
 
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
     
    //  默认采用Dubbo协议
     
    //之后的部分逻辑就是想尽一切办法取到host
     
    //取到端口号(之后的部分关于端口的逻辑就是想尽一切办法取端口号)
     
    // 这个map十分重要,它的意义在于所有存储所有最终使用到的属性,我们知道一个属性例如timeout,可能在Application,provider,service中都有配置,具体以哪个为准,都是这个map处理的事情。
    Map<String, String> map = new HashMap<String, String>();
    if (anyhost) { //如果此时anyhost为true的话
        map.put(Constants.ANYHOST_KEY, "true");
    }
    // 存储简单的服务信息
     
    //将application,module,provider,protocol和service的信息设置到map里面
    //将应用配置的树勇按照层级存入map中。注意这里的层级关系,是一层层覆盖的 即关系为:ServiceConfig->PrtocolConfig->ProviderConfig->ModuleConfig->ApplicaionConfig
     
    //单独处理好method层级的参数关系
     
    //判断有没有配置通配协议
    if (ProtocolUtils.isGeneric(generic)) {
        map.put("generic", generic);
        map.put("methods", Constants.ANY_VALUE);
    } else {
        String revision = Version.getVersion(interfaceClass, version);
        if (revision != null && revision.length() > 0) {
            map.put("revision", revision);
        }
        //通过包装类将interfaceClass进行包装然后取得方法名字,对于wapper包装器就是将不同的类统一化
        //参考http://blog.csdn.net/quhongwei_zhanqiu/article/details/41597261理解Wapper
        String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
        if(methods.length == 0) {
            logger.warn("NO method found in service interface " + interfaceClass.getName());
            map.put("methods", Constants.ANY_VALUE);
        }
        else {
            //将所有的方法拼接成以逗号为分隔符的字符串
            map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
        }
    }
    //处理token属性
     
    //如果配置的injvm的话就代表本地调用(本地调用还用Dubbo的话实在有点蛋疼)
     
    //所有的核心属性最后都成了URL的拼接属性,如果我们还记得map里面拼装了多少属性的话就知道这个URL内容有多丰富
    URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
     
     
    // 下面是核心暴露过程,将不会省略源码
    String scope = url.getParameter(Constants.SCOPE_KEY);
    //配置为none不暴露
    if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {

        //配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
        if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
            exportLocal(url);
        }
        //如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务)
        if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){
            if (logger.isInfoEnabled()) {
                logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
            }
            if (registryURLs != null && registryURLs.size() > 0
                    && url.getParameter("register", true)) {
                for (URL registryURL : registryURLs) {
                    //dynamic表示是否需要人工管理服务的上线下线(动态管理模式)
                    url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
                    URL monitorUrl = loadMonitor(registryURL);
                    if (monitorUrl != null) {
                        url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                    }
                    if (logger.isInfoEnabled()) {
                        logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                    }
                   ====================================================
                   //取到invoker对象(ref为接口实现类的引用)
                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                    //将invoker转化为exporter对象
                    Exporter<?> exporter = protocol.export(invoker);
                    exporters.add(exporter); //将exporter添加到需要暴露的列表中取
                }
                ================================================================
            } else {
                //如果找不到注册中心的话就自己充当自己的注册中心吧
                Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);

                Exporter<?> exporter = protocol.export(invoker);
                exporters.add(exporter);
            }
        }
    }
    //多个协议就有多个url与其对应,所以要一一存储。
    this.urls.add(url);
}
复制代码

通过ServicConfig中的内容分解,我们看出来里面主要做的内容如下: 检验所需参数的合法性 将多层的参数(可能重复配置)最终整理出最终的结果(map),然后根据参数拼接成暴露服务需用到的url。 处理generic,Stub,injvm等其他需要支持的内容,补充dubbo的功能多样性,但是都不涉及核心流程。 根据对应的协议将服务进行暴露(将提供的服务推送到注册中心供服务调用者发现),默认使用Dubbo协议。

转载于:https://juejin.im/post/5b63fa5f5188251a9f24c225

Dubbo 是一种分布式服务框架,它支持高性能的远程服务调用和服务治理。下面是 Dubbo 服务的暴露过程: 1. 配置服务接口:首先,你需要定义一个服务接口,它描述了你希望暴露的服务的方法和参数。 2. 实现服务接口:然后,你需要编写一个类来实现这个服务接口,提供具体的业务逻辑实现。 3. 配置服务实现:在 Dubbo 的配置文件中,你需要配置服务的实现类,指定要暴露的接口和端口等信息。 4. 启动 Dubbo 服务:在应用启动时,Dubbo 框架会根据配置文件初始化相关的组件,并启动服务提供者。 5. 注册服务Dubbo 服务提供者会将自己的地址、接口和方法等信息注册到注册中心,以便消费者能够发现和调用该服务。 6. 监听请求:一旦 Dubbo 服务提供者启动成功并注册到注册中心,它就会开始监听来自消费者的远程调用请求。 7. 处理请求:当有消费者发起远程调用请求时,Dubbo 服务提供者会根据请求的接口和方法等信息,找到对应的实现类并执行相应的业务逻辑。 8. 返回结果:Dubbo 服务提供者执行完业务逻辑后,会将结果返回给消费者,完成一次远程调用。 总结来说,Dubbo 服务的暴露过程包括定义服务接口、实现服务接口、配置服务实现、启动 Dubbo 服务、注册服务、监听请求、处理请求和返回结果等步骤。通过这些步骤,Dubbo 实现了远程服务的调用和治理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值