Spring Cloud 微服务优雅下线 + 灰度发布的正确姿势,写得太好了!

前言

在生产环境中,如何保证在服务升级的时候,不影响用户的体验,这个是一个非常重要的问题。如果在我们升级服务的时候,会造成一段时间内的服务不可用,这就是不够优雅的。那什么是优雅的呢?主要就是指在服务升级的时候,不中断整个服务,让用户无感知,进而不会影响用户的体验,这就是优雅的。

实际上,优雅下线是目标,而不是手段,它是一个相对的概念,例如 kill PIDkill -9 PID都是暴力杀死服务,相对于 kill -9 PID来说, kill PID就是优雅的。但如果单独拿 kill PID出来说,我们能说它是优雅的下线策略吗?肯定不是啊,就是这个道理。

因此,本文讲述的优雅下线仅能称之为”相对的优雅下线”,但相对于暴力的杀死服务,已经足够优雅了。常见的优雅解决方案,主要包括优雅下线和灰度发布。而实际上,灰度发布的范围就已经包含优雅下线了。

最后,在本文中,我们主要讲述基于 Spring Cloud 和 Euraka 的优雅下线以及灰度发布。

优雅下线

常见的下线方式

方式一:kill PID

使用方式:kill java进程ID

该方式借助的是 Spring Boot 应用的 Shutdown hook,应用本身的下线也是优雅的,但如果你的服务发现组件使用的是 Eureka,那么默认最长会有 90 秒的延迟,其他应用才会感知到该服务下线,这意味着:该实例下线后的 90 秒内,其他服务仍然可能调用到这个已下线的实例。因此,该方式是不够优雅的 。

方式二:/shutdown端点

Spring Boot 提供了 /shutdown端点,可以借助它实现优雅停机。

使用方式:在想下线应用的application.yml中添加如下配置,从而启用并暴露 /shutdown端点:

management:
  endpoint:
    shutdown:
      enabled: true
  endpoints:
    web:
      exposure:
        include: shutdown

发送 POST 请求到/shutdown端点

curl -X http://你想停止的服务地址/actuator/shutdown

该方式本质和方式一是一样的,也是借助 Spring Boot 应用的 Shutdown hook 去实现的。

Spring Boot 基础就不介绍了,推荐看这个免费教程:

https://github.com/javastacks/spring-boot-best-practice

方式三:/pause端点

Spring Boot 应用提供了 /pause端点,利用该端点可实现优雅下线。

使用方式:在想下线应用的application.yml中添加配置,从而启用并暴露 /pause端点:

management:
  endpoint:
    # 启用pause端点
    pause:
      enabled: true
    # 启用restart端点,之所以要启用restart端点,是因为pause端点的启用依赖restart端点的启用
    restart:
      enabled: true
  endpoints:
    web:
      exposure:
        include: pause,restart

发送 POST 请求到 /actuator/pause端点:

curl -X POST http://你想停止的服务实例地址/actuator/pause

执行后的效果类似下图:

Spring Cloud 微服务优雅下线 + 灰度发布的正确姿势,写得太好了!

如图所示,该应用在 Eureka Server 上的状已被标记为DOWN,但是应用本身其实依然是可以正常对外服务的。在 Spring Cloud 中,Ribbon 做负载均衡时,只会负载到标记为UP的实例上。

利用这两点,你可以:先用 /pause端点,将要下线的应用标记为DOWN,但不去真正停止应用;然后过一定的时间(例如 90 秒,或者自己做个监控,看当前实例的流量变成 0 后)再去停止应用,例如 kill应用。

缺点 & 局限

Spring Cloud 微服务优雅下线 + 灰度发布的正确姿势,写得太好了!
方式四:/service-registry端点

使用方式:在想下线应用的application.yml中添加配置,从而暴露 /service-registry端点:

management:
  endpoints:
    web:
      exposure:
        include: service-registry

发送 POST 请求到 /actuator/service-registry端点:

curl -X "POST" "http://localhost:8000/actuator/service-registry?status=DOWN" \
   -H "Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8"

实行后的效果类似如下图:

Spring Cloud 微服务优雅下线 + 灰度发布的正确姿势,写得太好了!

优雅的下线方式

在上文中,我们讲述了四种常见的下线方式,对比来看,方式四 是一种比较优雅的下线方式。

在实际项目中,我们可以先使用 /service-registry端点,将服务标记为DOWN,然后监控服务的流量,当流量为 0 时,即可升级该服务。当然,这里假设我们部署了多个服务实例,当一个服务实例DOWN掉之后,其他服务实例仍然是可以提供服务的,如果就部署一台服务的话,那么讨论优不优雅就没那么重要了。

除了上述的下线方式之外,还有一种利用 EurekaAutoServiceRegistration对象达到优雅下线的目标。

  • 执行 eurekaAutoServiceRegistration.start()方法时,当前服务向 Eureka 注册中心注册服务;
  • 执行 eurekaAutoServiceRegistration.stop()方法时,当前服务会向 Eureka 注册中心进行反注册,注册中心收到请求后,会将此服务从注册列表中删除。

示例代码如下:

@RestController
@RequestMapping(value = "/graceful/registry-service")
public class GracefulOffline {

    @Autowired
    private EurekaAutoServiceRegistration eurekaAutoServiceRegistration;

    @RequestMapping("/online")
    public String online() {
        this.eurekaAutoServiceRegistration.start();
        return "execute online method, online success.";
    }

    @RequestMapping("/offline")
    public String offline() {
        this.eurekaAutoServiceRegistration.stop();
        return "execute offline method, offline success.";
    }
}

到这里,我们已经介绍了两种相对优雅的下线方式了。具体如何操作,我们可以根据实际上情况进行包装,或者利用自动化的脚本来实现更加优雅的下线方式。

灰度发布

蓝绿部署

蓝绿部署,英文名为 Blue Green Deployment, 是一种可以保证系统在不间断提供服务的情况下上线的部署方式。

如何保证系统不间断提供服务呢?那就是同时部署两个集群,但仅对外提供一个集群的服务,当需要升级时,切换集群进行升级。蓝绿部署无需停机,并且风险较小。其大致步骤为:

  • 部署集群 1 的应用(初始状态),将所有外部请求的流量都打到这个集群上
  • 部署集群 2 的应用,集群 2 的代码与集群 1 不同,如新功能或者 Bug 修复等
  • 将流量从集群 1 切换到集群 2
  • 如集群 2 测试正常,就删除集群 1 正在使用的资源(例如实例),使用集群 2 对外提供服务

因为在使用蓝绿部署的方式时,我们需要控制流量,所以我们需要借助路由服务,如 Nginx 等。

滚动部署

滚动部署,英文名为 Rolling Update,同样是一种可以保证系统在不间断提供服务的情况下上线的部署方式。和蓝绿部署不同的是,滚动部署对外提供服务的版本并不是非此即彼,而是在更细的粒度下平滑完成版本的升级。

如何做到细粒度平滑升级版本呢?滚动部署只需要一个集群,集群下的不同节点可以独立进行版本升级。比如在一个 12 节点的集群中,我们每次升级 4 个节点,并将升级后的节点重新投入使用,周而复始,直到集群中所有的节点都更新为新版本。

这种部署方式相对于蓝绿部署,更加节约资源,因为它不需要运行两个集群。但这种方式也有很多缺点,例如:

  • 没有一个确定 OK 的环境。使用蓝绿部署,我们能够清晰地知道老版本是 OK 的,而使用滚动发布,我们无法确定。
  • 修改了现有的环境。
  • 如果需要回滚,很困难。举个例子,在某一次发布中,我们需要更新 100 个实例,每次更新 10 个实例,每次部署需要 5 分钟。当滚动发布到第 80 个实例时,发现了问题,需要回滚。这时,我们估计就要疯了。
  • 有的时候,我们还可能对系统进行动态伸缩,如果部署期间, 系统自动扩容/缩容了,我们还需判断到底哪个节点使用的是哪个代码。尽管有一些自动化的运维工具,但是依然令人心惊胆战。

并不是说滚动发布不好,滚动发布也有它非常合适的场景。

金丝雀部署

金丝雀部署又称灰度部署(或者,灰度发布),英文名为 Canary Deployment, 是指在黑与白之间,能够平滑过渡的一种发布方式

金丝雀的名称来源于「矿井中的金丝雀」,早在 17 世纪,英国矿井工人发现,金丝雀对瓦斯这种气体十分敏感,空气中哪怕有极其微量的瓦斯,金丝雀也会停止歌唱;而当瓦斯含量超过一定限度时,虽然鲁钝的人类毫无察觉,金丝雀却早已毒发身亡。当时在采矿设备相对简陋的条件下,工人们每次下井都会带上一只金丝雀作为”瓦斯检测指标”,以便在危险状况下紧急撤离。

我们来看一下金丝雀部署的步骤:

  • 准备好部署各个阶段的工件,包括: 构建工件,测试脚本,配置文件和部署清单文件
  • 从负载均衡列表中移除掉”金丝雀”服务器
  • 升级”金丝雀”应用(切断原有流量并进行部署)
  • 对应用进行自动化测试
  • 将”金丝雀”服务器重新添加到负载均衡列表中(连通性和健康检查)
  • 如果”金丝雀”在线使用测试成功,升级剩余的其他服务器(否则就回滚)

在金丝雀部署中,常常按照用户量设置路由权重,例如 90% 的用户维持使用老版本,10% 的用户尝鲜新版本。不同版本应用共存,经常与 A/B 测试一起使用,用于测试选择多种方案。

金丝雀部署比较典型的例子,就是我们在使用某个应用的时候,该应用邀请我们进行”内测”或者”新版本体验”,如果我们同意了,那么我们就成了金丝雀。

版权声明:本文为CSDN博主「CG国斌」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/qq_35246620/article/details/109166722

近期热文推荐:

  1. 1,000+ 道 Java面试题及答案整理(2022最新版)

  2. 劲爆!Java 协程要来了。。。

  3. Spring Boot 2.x 教程,太全了!

  4. 别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!

  5. 《Java开发手册(嵩山版)》最新发布,速速下载!

觉得不错,别忘了随手点赞+转发哦!

Original: https://www.cnblogs.com/javastack/p/16721762.html
Author: Java技术栈
Title: Spring Cloud 微服务优雅下线 + 灰度发布的正确姿势,写得太好了!

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/616392/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 搭建分布式事务组件 seata 的Server 端和Client 端详解(小白都能看懂)

    一,server 端的存储模式为:Server 端 存 储 模 式 (store-mode) 支 持 三 种 :file: ( 默 认 ) 单 机 模 式 , 全 局 事 务 会 …

    Java 2023年6月14日
    087
  • ENDIAN的由来及BIG-EDIAN 和LITTLE-ENDIAN(转)

    在各种计算机体系结构中,对于字节、字等的存储机制有所不同,因而引发了计算机通信领域中一个很重要的问题,即通信双方交流的信息单元(比特、字节、字、双字等等)应该以什么样的顺序进行传送…

    Java 2023年6月6日
    087
  • Spring boot中Yml文件的坑

    1、YML配置文件中的值如果是纯数字的话,中间不能使用 “_” 比如配置下面的值: 在项目中注入后的值会变为:2020081801 会默认去掉中间的&#82…

    Java 2023年5月30日
    080
  • 可落地的DDD(7)-战术设计上的一些误区

    背景 几年前我总结过DDD战术设计的一些落地经验可落地的DDD(5)-战术设计,和一次关于聚合根的激烈讨论最近两年有些新的落地体验,回过头来发现,当初对这些概念的理解还是没有深入,…

    Java 2023年6月8日
    082
  • 【Spring Boot】 Spring Boot之Hystrix使用介绍

    { "annotations": { "list": [ { "builtIn": 1, "datasourc…

    Java 2023年5月29日
    066
  • JVM学习笔记之类加载机制【八】

    一、类加载时机 1.1 触发类初始化的六个场景: 加载? 1、遇到new、getstatic、putstatic或invokestatic这四条字节码指令时 如果类型没有进行过初始…

    Java 2023年6月5日
    074
  • pyton对Quoted-printable进行编码和解码

    前言 利用的是python的 quopri模块进行操作 一.加密 import quopri str = "你好世界" a = quopri.encodestr…

    Java 2023年6月13日
    094
  • 保存登录信息的Cookie加密技术

    所有需要账户登录的website 基本都会想到这样一个问题, 如何保持用户在一定时间内登录有效。 最近本人就在项目中遇到这样的需求,某些页面只能Admin账户登录后访问, 当登录A…

    Java 2023年6月5日
    085
  • 14、线程同步机制

    线程同步机制 1、同一个进程的多个线程共享同一块存储空间,带来了方便,也有冲突,为了保证数据在方法中被访问的正确性,在访问时加锁机制,synchronized,当一个线程获得对象的…

    Java 2023年6月8日
    065
  • vnpy源码阅读学习(4):自己写一个类似vnpy的UI框架

    自己写一个类似vnpy的界面框架 概述 通过之前3次对vnpy的界面代码的研究,我们去模仿做一个vn.py的大框架。巩固一下PyQt5的学习。 这部分的代码相对来说没有难度和深度,…

    Java 2023年6月7日
    092
  • JAVA中让Swagger产出更加符合我们诉求的描述文档,按需决定显示或者隐藏指定内容

    大家好,又见面啦。 在前一篇文档《JAVA中自定义扩展Swagger的能力,自动生成参数取值含义说明,提升开发效率》中,我们探讨了如何通过自定义注解的方式扩展swagger的能力让…

    Java 2023年6月7日
    089
  • CSharp: Abstract Factory in donet core 3

    csharp;gutter:true; /// /// Abstract Factory抽像工厂 /// geovindu,Geovin Du eidt /// public in…

    Java 2023年6月16日
    069
  • Java正则表达式

    不包含thumb.jpg,但包含_HH_,并以.jpg结尾 不包含_Check.xml,但以.xml结尾 Original: https://www.cnblogs.com/gis…

    Java 2023年5月29日
    066
  • 浅谈Java面向对象之抽象类(abstract)

    java语言,声明类时格式为: abstract class Db{} 说明Db类为抽象类。抽象方法是说没有方法的实现(方法体)此方法为抽象方法,只有抽象类和接口中才可以有抽象方法…

    Java 2023年6月7日
    071
  • mysql

    基础篇 通用语法及分类 DDL: 数据定义语言,用来定义数据库对象(数据库、表、字段) DML: 数据操作语言,用来对数据库表中的数据进行增删改 DQL: 数据查询语言,用来查询数…

    Java 2023年6月16日
    073
  • 关键字,接口,代码块,枚举

    今日内容 1 关键字 2 代码块 3 接口 4 枚举 1 Java中的关键字 static关键字 : 静态的意思 , 可以修饰变量 , 也可以修饰方法 , 被static修饰的成员…

    Java 2023年6月13日
    071
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球