一文聊透 Dubbo 优雅上线

前言

在此文之前,我写过一篇 《一文聊透 Dubbo 优雅停机》,这篇文章算是一个续集,优雅停机和优雅上线两者都是微服务生命周期中,开发者必须关心的环节。

优雅上线还有很多称呼:「无损上线」,「延迟发布」,「延迟暴露」。它们的对立面自然是:「有损上线」,「直接发布」。

我最近写的「一文聊透 Dubbo xx」系列文章,都有一个特点,即当你不注重文章中实践,你的 Dubbo 应用依旧可以正常运行,但总归在某些场景 case 下,你的系统会出现问题。做不到优雅上线,你的系统将会出现:在应用刚启动时,就有流量进入,而此时应用尚未初始化完毕,导致调用失败,在集群规模较大时,影响会变得很明显。

方案一:延迟发布

以 SpingBoot 下使用 Dubbo 为例,被 Dubbo 的 @Service 注解修饰的服务,会按照 Spring 中初始化 Bean 的顺序,串行执行发布逻辑。Dubbo 框架会完成一系列的操作:

  • 创建远程调用的 Proxy
  • 把代理对象注册到 ProviderConsumerRegTable,方便远程调用到来时寻找到对应的服务
  • 向注册中心注册

一旦服务信息注册到注册中心,在消费者看来该服务就是可以被调用的。然而,此时可能出现一些数据库、缓存资源尚未加载完毕的场景,这取决于你的系统有没有对应的组件,它们何时加载完毕,也完全取决于你的业务。如果你担心你的系统存在这种隐患,可以尝试多次重启集群中的任意一台机器,查看调用方是否存在报错,如果有报错,一种可能性是没有实现优雅停机,一种可能性是没有实现优雅上线。

Dubbo 服务暴露的起点一般是以 Spring 容器启动完毕后发出的 ContextRefreshedEvent 事件为准,Dubbo 的 ServiceBean 实现了 ApplicationListener<E extends ApplicationEvent> 接口,用以接收这一容器刷新事件。

public void onApplicationEvent(ContextRefreshedEvent event) {
    // 是否有延迟导出 && 是否已导出 && 是不是已被取消导出
    if (isDelay() && !isExported() && !isUnexported()) {
        // 导出服务
        export();
    }
}

Dubbo 为服务提供了 delay 配置:

<dubbo:service delay="5000" />

如上配置后,Dubbo 服务将会在 Spring 容器启动后 5s,再执行暴露逻辑。这里 delay 的时长,取决于你系统资源初始化的耗时,没有一个经验值。如果不配置改值,Dubbo 将会在收到 ContextRefreshedEvent 事件后,立即执行发布逻辑。

Dubbo 2.6.5 版本对服务延迟发布逻辑进行了细微的调整,将需要延迟暴露(delay > 0)服务的倒计时动作推迟到了 Spring 初始化完成后进行。在此之前的版本的逻辑不太合理,如果想要让 2.6.5 之前的版本延迟到 Spring 初始化完成后,再暴露服务,可以这样配置: 本节参考 Dubbo 官方文档 延迟暴露:http://dubbo.apache.org/zh-cn/docs/user/demos/delay-publish.html

方案二:QOS 命令上线

Dubbo 还为服务提供了另一个配置项:

<dubbo:service register="false" />

该配置项配置后,服务将不会发布到注册中心,可能很多 Dubbo 用户不会注意到这个配置,它的作用恰恰是 QOS 指令使用的。

Dubbo 2.5.8 及以上的版本,还提供了一些在线运维命令。为了演示该命令,我们准备一个 GreetingService 的 demo:

public class DubboProvider {

    public static void main(String[] args) throws Exception {
        ServiceConfig<GreetingsService> service = new ServiceConfig<>();
        service.setApplication(new ApplicationConfig("dubbo-provider"));
        service.setRegistry(new RegistryConfig("nacos://127.0.0.1:8848"));
        service.setInterface(GreetingsService.class);
        service.setRef(new GreetingsServiceImpl());
        service.setRegister(false);
        service.export();

        System.out.println("dubbo service started");
        new CountDownLatch(1).await();
    }
}

注意我们配置了不发布:service.setRegister(false),由于 QOS 配置是默认打开的,在本地的 22222 端口,可以进入 QOS 控制台。

 krito git:(master) ✗ telnet localhost 22222
Trying ::1...
Connected to localhost.
Escape character is '^]'.
   ___   __  __ ___   ___   ____     
  / _ \ / / / // _ ) / _ ) / __ \
 / // // /_/ // _  |/ _  |/ /_/ /
/____/ \____//____//____/ \____/
dubbo>ls
As Provider side:
+-------------------------------------+---+
|        Provider Service Name        |PUB|
+-------------------------------------+---+
|com.alibaba.edas.api.GreetingsService| N |
+-------------------------------------+---+
As Consumer side:
+---------------------+---+
|Consumer Service Name|NUM|
+---------------------+---+

dubbo>online
OK
dubbo>ls
As Provider side:
+-------------------------------------+---+
|        Provider Service Name        |PUB|
+-------------------------------------+---+
|com.alibaba.edas.api.GreetingsService| Y |
+-------------------------------------+---+
As Consumer side:
+---------------------+---+
|Consumer Service Name|NUM|
+---------------------+---+

dubbo>

如上图所示,我们首先查看到 com.alibaba.edas.api.GreetingsService 服务是未发布的,通过 online 命令手动将服务发布,再使用 ls 查看服务列表时,已经显示服务处于发布状态了。

除了使用 telnet,还可以通过 HTTP 访问:

curl localhost:22222/online

小 tips:在使用 SpringBoot 注解式声明一个 Service 时,register 属性会失效,在 xml 或者 API 方式下声明则运行正常,怀疑是 dubbo-spring-boot-starter 的一个 bug。

大家在 SpringBoot 下使用 Dubbo 需要留意类似的问题,之前有过一些属性在 SpringBoot 注解中未解析或为提供注解配置的案例,在使用时需要注意。

本节参考 Dubbo 官方文档 服务配置说明:http://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-service.html QOS:http://dubbo.apache.org/zh-cn/docs/user/references/qos.html

最佳实践

本文介绍了两种 Dubbo 的机制:

  • 方案一:延迟发布(delay=5000)
  • 方案二:不发布 + QOS 指令发布(register=false)

想要实现优雅上线,可以采取适合你系统的方式。方案一延迟发布的优势在于实现简单,但具体 delay 多少秒,比较依赖系统维护者的经验。方案二使用 QOS 指令,一般依靠于发布系统,当发布系统检测到固定的资源加载完毕这样一个信号时,自动触发上线命令,更加灵活。

本文分享自微信公众号 - Kirito的技术分享(cnkirito)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-11-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏AI算法与图像处理

全世界都在努力做科研,只有我们在玩命的朝SCI灌水

2016年世界最大学术出版商Elsevier旗下Scopus 数据库收录中国论文数首次超越美国,国际上第一次正式承认中国成为世界上最大的科研论文产出国。

6010
来自专栏DBA随笔

Mysql中的Redo Log解析(二)

昨天介绍了redo log的概念,关于redo log的写入方法没有很详细的讲解,今天接着说一下。

10220
来自专栏算法与编程之美

计算机技术|卡片视图CardView

在使用MUI开发APP的过程中,功能的融合和美观的布局,往往能使得项目得以顺利进展。不错的设计肯定离不开MUI的各个控件。对于每个控件的熟悉,并且能个灵活的运用...

4720
来自专栏码力全开

浅析 JS 事件循环之 Microtask 和 Macrotask

我们在上一篇 《浅析 JS 中的EventLoop 事件循环》 中提到一个 Event Queue,其实在事件循环中 queue 一共有两种,还有一种叫 Job...

5320
来自专栏志学Python

带你认识 flask 的模板

在终端会话中设置环境变量FLASK_APP=microblog.py,然后执行flask run命令来运行应用。包含这个应用的Web服务启动之后,你可以通过在W...

8710
来自专栏FreeBuf

OpenRASP梳理总结

RASP英文为 Runtime application self-protection,即运行时应用程序自我保护。“运行时应用程序自我保护”的概念由Gartne...

13820
来自专栏Vue源码 & 前端进阶体系

【JS应用】Iframe 解决跨域

跨域的东西, 简直不要接触太多,网上相关内容一抓一大把,但是突然学习到一个关于前端解决跨域的方式

15210
来自专栏Spark学习技巧

小米OLAP服务架构演进

如果你是一名数据分析师,或者是一位经常和 SQL 打交道的研发工程师,那么 OLAP这个词对你一定不陌生。你或许听说过 OLAP、OLTP 技术,但是今天文章的...

9220
来自专栏机器学习算法与Python学习

26 个鲜为人知的 Python 技巧,成为真正的Pyer!

人们还经常把 Python 笑称为「可执行伪码(executable pseudocode)」。但是,当你可以编写这样的代码时,很难去反驳这种言论:

7720
来自专栏前端劝退师

一文搞懂 Webpack 多入口配置

再一次,在网上找的不少文章都不合我的需求,很多文章都是只简单介绍了生产环境下配置,没有介绍开发环境下的配置,有的也没有将多入口结合 vue-router、 vu...

9020

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励