微服务分布式架构中,如何实现优雅发版?

本文主要讲解了spring cloud微服务架构中如何做到服务版本升级部署过程中API不报错。

背景:

1.发版导致当前服务不可用,返回熔断信息,影响用户体验,如“系统繁忙,请稍后重试”

2.请求链路跑到一半中断,1次请求前半部分的数据正确,后半部分的数据不正确,导致数据错乱

3.情况2导致的问题,研发同事需要花大量时间定位问题并修复数据

实现思路:

1.将发版服务从eureka注册中心移除,待服务没有流量后,对服务进行发版

2.触发其他服务刷新服务列表,停止对发版服务请求,让发版服务没有流量

3.发版服务启动后,触发其他服务刷新服务列表,对发版服务请求,让发版服务分担流量

以下图简单demo举例,我们要对B1进行发版

一、eureka的服务发现机制

先来了解一下eureka的服务发现机制

微服务内最终发起请求的组件是Ribbon(有自己维护的服务列表),如果是按照eureka的默认服务发现机制来发版服务,会有0~270秒的时间,A1或A2还会将请求发送到B1,B1在停机到重启完毕这段时间,是无法处理请求的,所以会触发A1或A2的内部熔断机制(如“系统繁忙,请稍后重试”)

对于这种由于发版导致的熔断,其实我们是可以避免的,来看看我是怎么做的

二、优雅发版

1.将B1从eureka移除,待服务没有流量后,对B1进行发版

这里的B1虽然已从eureka移除,但B1还是正常运行的,如果使用ip+端口方式直接请求B1的API,是可以正常处理的

因为B1、B2是B服务集群的节点,所以等待B1启动后会自动注册到eureka,并且分配流量,再对B2做同样的发版过程即可(服务A发版也是一样的)

整个过程我们都是在对没有流量的服务进行重启,对于整个微服务请求链路来说,是完全没有影响的

2.服务怎么下线

1)禁用readOnlyCacheMap

eureka.server.use-read-only-response-cache: false

如果不禁用,正常主动下线会直接同步到readWriteCacheMap,但不会同步到readOnlyCacheMap,可能会有30秒的差异,eureka客户端拉取的服务列表里面还是有B1,无法做到即时把B1移除

2)执行DiscoveryClient.shutdown()

定义一个API,来调用DiscoveryClient.shutdown()来主动从eureka下线

并且利用MQ(我这里用的是rabbitmq)发送一个广播消息给其他所有服务,告诉其他服务本服务已经下线了,快点刷新Ribbon的服务列表,不要再将请求发到本服务啦

这里为什么要刷新eureka客户端的服务列表?因为Ribbon的服务列表数据是来自eureka客户端的服务列表(看前面的图)

3)其他下线(目前只遇到MQ消费者需要处理,如果你的项目中有别的要处理也可以参考)

虽然B1已经不处理API请求了,但B1的MQ消费者还在消费数据啊,如果消费者在处理数据的过程中B1停了,也会出问题啊,这怎么办啊?

下线B1的所有MQ消费者

4)服务启动后通知其他服务刷新服务列表,快速获取流量

这步操作不是必须的,等待服务自主同步服务列表也行,这样的话可能就会有一定的延迟,需要等一等才能部署下一个节点

三、应用

主要是调用上面的/offline接口(尽量保留同步阻塞,如果改为异步非阻塞可能会出现服务下线还没完成就杀进程了,导致异常)

用法1:直接请求http://localhost:8881/offline,响应成功后可以杀进程重启

用法2:结合jenkins部署,将http://localhost:8881/offline添加到shell脚本当中,在jenkins构建过程中执行shell脚本,构建完成后杀进程重启

怎么样?如果你觉得有用的话,还不快快搞起!!!

附:涉及的代码目录

github:https://github.com/897665787/springcloud-template

gitee:https://gitee.com/jq_di/springcloud-template

springcloud-template
└── template-eureka
     eureka.server.use-read-only-response-cache: false -- 禁用readOnlyCacheMap
└── template-framework
     └── deploy
          └── DeployController -- 部署相关接口(用于优雅发版)
          └── MQAutoRefresh -- MQ自动刷新注册列表(如果使用了MQ,可以利用MQ广播消息自动刷新)
          └── RefreshHandler -- 刷新处理器
          └── StartupApplicationRunner -- 服务启动成功执行

猜你喜欢

转载自blog.csdn.net/w13528476101/article/details/126678975