Dubbo 延迟服务暴露

前言

你们好,今天开始给你们分享 — Dubbo 专题之 Dubbo 延迟服务暴露。在前一个章节中咱们介绍了 Dubbo StubMock,咱们例举了常见的使用场景而且进行了源码解析来分析其实现原理,同时咱们知道 Dubbo StubMock 都是基于对调用代理对象的包装实现的,这样能够为咱们在调用服务时作一些前置或后置处理工做。有的小伙伴可能会遇到这样的场景:在咱们的应用服务中有不少的本地缓存或者分布式缓存,这些缓存有可能须要加载一段时间那么在这个缓存加载过程当中咱们但愿接口不要对外提供服务,那有没有一种机制让咱们对缓存加载完成后再暴露服务的机制呢?那么在本章节咱们会经过介绍 Dubbo 延迟暴露服务来解决这个问题。那什么是延迟服务暴露?以及延迟服务暴露的实现原理是什么呢?下面就让咱们快速开始吧!java

1. 延迟服务暴露简介

在当前咱们使用的Dubbo2.7.x版本中服务暴露时机是 Spring 容器启动完成并广播出事件 ApplicationContextEvent 时才进行对服务暴露。那这里所谓的延迟暴露也就是在接收到 ApplicationContextEvent 事件后开始一个指定时间的延迟直到延迟时间到才开始对服务进行暴露。延迟服务暴露的核心就是一个延迟调度器,当延迟时间到就开始进行服务暴露。其配置参数为:delay="5000",这里的时间单位为毫秒。git

Tips:其余的 Dubbo 版本的服务暴露可能有一些出入,读者以最新版本为主。

2. 使用方式

延迟服务暴露能够经过 XML 或者注解的方式进行配置且指定的延迟时间单位为毫秒。spring

2.1 XML 配置方式

<!--延迟1秒暴露Dubbo服务-->
    <dubbo:service interface="com.muke.dubbocourse.protocol.api.BookFacade" ref="bookFacade" delay="1000" />

若是配置delay="-1"则表示在 Spring 容器初始化完后再暴露服务。数据库

2.2 注解配置方式

@DubboService(delay = 1000)
public class BookFacadeImpl implements BookFacade {

}

使用@DubboService注解或@Service注解。delay = 1000表示延迟1秒后才进行服务暴露。apache

3. 使用场景

经过上面的延迟服务暴露的简介咱们能够了解到:延迟服务暴露其实就是针对须要暴露的服务配置一个固定的延迟时间,延迟时间一到当即开始服务的暴露。那么咱们基于上面的服务暴露的时效性咱们简单的介绍一些工做中常使用的场景:编程

  1. 缓存预热:当咱们的暴露服务须要依赖一些静态数据,这些静态数据是经过加载数据库或者文件而后缓存到应用内存中。此时咱们能够经过在服务延迟暴露的时间段内进行缓存预加载。
  2. 依赖资源:假设咱们对外提供的服务须要依赖另一个服务,而另一个服务的暴露时间比较缓慢,那么咱们就能够把当前对外暴露的服务进行延迟暴露,这样就能够减小当调用依赖服务时出现超时异常的状况。

4. 示例演示

咱们一样以获取图书列表为例来进行演示。项目结构以下:bootstrap

idea

由于延迟服务暴露是配置服务提供端,全部咱们这里只看服务提供者的 XML 配置dubbo-provider-xml.xmlapi

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <dubbo:application name="demo-provider" metadata-type="remote"/>

   <!--注册中心-->
    <dubbo:registry address="zookeeper://127.0.0.1:2181"/>

    <bean id="bookFacade" class="com.muke.dubbocourse.delayexport.provider.BookFacadeImpl"/>

    <!--延迟1秒暴露Dubbo服务-->
    <dubbo:service interface="com.muke.dubbocourse.common.api.BookFacade" ref="bookFacade" delay="1000"/>

</beans>

上面的 XML 配置中主要配置了delay="1000"表示等 Spring 容器启动完成后服务延迟1秒钟后才开始暴露服务。其注解配置也是相似就再也不演示。缓存

5. 实现原理

下面咱们简单的经过服务暴露的源码进行分析。咱们都知道当 Spring 容器启动完成会发出 ApplicationContextEvent 事件,咱们能够看到这个`org.apache.dubbo.config.spring.context
.DubboBootstrapApplicationListener#
onApplicationContextEvent`方法核心代码:微信

public void onApplicationContextEvent(ApplicationContextEvent event) {
        if (event instanceof ContextRefreshedEvent) {
            //Spring 容器启动完成
            onContextRefreshedEvent((ContextRefreshedEvent) event);
        } else if (event instanceof ContextClosedEvent) {
            //Spring 容器关闭
            onContextClosedEvent((ContextClosedEvent) event);
        }
    }

当接收到ContextRefreshedEvent事件后会最终会调用`org.apache.dubbo.config.bootstrap.
DubboBootstrap#start`方法启动服务注册核心代码以下:

public DubboBootstrap start() {
         //判断服务是否启动 防止重复暴露服务 注意:这里是原子操做
        if (started.compareAndSet(false, true)) {
            initialize();
            //..
            // 暴露Dubbo服务
            exportServices();

            //...
        }
        return this;
    }

上面的代码先进行原子操做去设置启动标识防止重复暴露服务,其exportServices代码以下:

/***
     *
     * dubbo服务导出
     *
     * @author liyong
     * @date 16:23 2020-03-01
     * @param
     * @exception
     * @return void
     **/
    private void exportServices() {
        //循环全部须要服务暴露的配置
        configManager.getServices().forEach(sc -> {
            // TODO, compatible with ServiceConfig.export()
            ServiceConfig serviceConfig = (ServiceConfig) sc;
            serviceConfig.setBootstrap(this);

            //是否异步导出
            if (exportAsync) {
                //获取线程池
                ExecutorService executor = executorRepository.getServiceExporterExecutor();
                Future<?> future = executor.submit(() -> {
                    //异步服务暴露
                    sc.export();
                });
                asyncExportingFutures.add(future);
            } else {
                //同步服务暴露导出
                sc.export();
                exportedServices.add(sc);
            }
        });
    }

上面的代码是循环对全部的 Dubbo 服务进行暴露,注意这里有一个exportAsync标识来判断是否异步暴露服务(异步暴露服务是指在另外的线程执行不阻塞当前线程)。下面咱们看到主要的服务暴露代码org.apache.dubbo.config.ServiceConfig#export方法:

/**
     *
     * 服务暴露
     *
     * @author liyong 
     * @date 10:55 PM 2020/11/18 
     * @param  
     * @exception 
     * @return void 
     **/
    public synchronized void export() {

       //...

        //是否延迟暴露判断
        if (shouldDelay()) {
            //延迟暴露
            DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
        } else {
            //服务暴露
            doExport();
        }
    }

上面便是咱们延迟服务暴露的核心代码 DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS)使用延时调度器进行服务暴露。

6. 小结

在本小节中咱们主要学习了 Dubbo 中延迟服务暴露以及使用方式,同时也分析了延迟服务暴露实现的原理,其本质上是经过延迟调度器进行服务暴露,其中延迟调度器是关键所在。

本节课程的重点以下:

  1. 理解 Dubbo 延迟服务暴露
  2. 了解了延迟服务暴露的使用方式
  3. 了解延迟服务暴露使用场景
  4. 了解 延迟服务暴露实现原理

做者

我的从事金融行业,就任过易极付、思建科技、某网约车平台等重庆一流技术团队,目前就任于某银行负责统一支付系统建设。自身对金融行业有强烈的爱好。同时也实践大数据、数据存储、自动化集成和部署、分布式微服务、响应式编程、人工智能等领域。同时也热衷于技术分享创立公众号和博客站点对知识体系进行分享。关注公众号: 青年IT男 获取最新技术文章推送!

博客地址: http://youngitman.tech

微信公众号:

相关文章
相关标签/搜索