服务上线后由于 bug 修复、扩容、或者发现了更好的方法进行了重构等原因,总免不了需要发布新版本,进行系统变更升级。服务变更过程本身也是引起服务不可用的重要原因。为了尽量降低可能出现故障而造成的损失,比较流行的思路是采用灰度发布策略,逐步增加流量导入新版本服务实例上,直至将所有流量切到新版本,下线旧版本。由于,spring cloud gateway 作为整个系统的入口,在 spring cloud gateway 上实施流量管控策略,也是顺利成章。本文就尝试介绍基于 spring cloud gateway 的灰度发布方法。
在大家的一般印象中,服务的部署和发布是一体的,服务部署后就发布上线使用了,服务发布就是通过部署实现的。在考虑服务变更安全的时候,则可以严格将部署和发布分成两个不同的阶段。部署是指将服务程序和配置拷贝到目标机器,并启动程序运行。发布则是服务正式接入线上流量,处理用户请求。
常见的发布方式主要有:
逐个(或小批量)上线服务实例,每上线一个(或小批量)实例,则观察是否正常,正常后再上线下线一个(或小批量)实例,异常则进行回滚。这种方式着眼于逐步增加服务实例,而不是着眼于接入流量。如果系统总共N个实例,已上线M个实例,则新版本接入的流量为 M/N。
灰度发布(Gray Release或Dark Launch),又名金丝雀发布(Canary Deployment)。
灰度发布与滚动发布相比,区别在于它更加考虑的是逐步将流量引入到新版本实例中,而不是发布了多少个新版本实例,下线了多少个旧版本实例。比如先将10%的流量引入新版本,然后增加到 20%,然后 50%, 80%,100% 等等,直到所有流量到引入到新版本中。
蓝绿发布则是同时部署两套系统,当确保新版本系统已正常工作时,则将流量切到新版本系统(听上去有点不太靠谱,实际也有企业应用)。
服务需要进行变更(发布)的几个主要原因:
spring cloud gateway 提供了一个,它支持将流量按照不同比例路由到不同的目的地。这种方式特别适合进行测试。比如同一个服务有 A、B 两个版本,分别注册为服务 app-serer-a,app-server-b,那么可以如下配置 gateway 的 route:
如上所示,A、B 两个版本的流量各占 50%,我们可以通过配置中心(如apollo、spring cloud config等)动态修改生效上述的权重配置,从而调整 A、B 两个版本的流量占比
如果 A、B 两个版本的接口可以通过接口的路径进行区分,则可以如下配置 gateway 的 route:
通过 loadbalancer 的 weightedServiceInstanceListSupplier 实现。
通过对不同的服务实例配置不同的权重值,loadbalancer 在做负载均衡时,按照权重所占比例将流量负载均衡到不同的服务实例。假设版本 A 的所有实例的权重之和为 80,版本 B 的所有实例的权重之和为 20,那么版本 A 承载的流量站 80%,版本 B 承载的流量占 20%。通过动态调整两个版本的权重占比,就能调整两个版本的流量占比了。这种方式比较适合新旧版本的接口和功能都不变的变更场景。
这种方式 gateway 的 route 配置进行普通的配置即可:
每个服务实例配置自己的 weight 值:
然后在 gatway 上配置服务的负载均衡策略为 weightedServiceInstanceListSupplier。
通过 loadbalancer 的 hintBasedServiceInstanceListSupplier 实现。
loadbalancer 的 hintBasedServiceInstanceListSupplier 会检查请求的头,将请求发送给值与请求头值相同的服务实例。这样如果服务实例的配置为实例的版本号,就可以通过在请求的头中设置为相应的版本号,从而控制流量导入不同的版本服务实例。这种方式尤其适合于版本升级了新的功能,提供了新的接口,可以通过在请求头中指定版本号让新版本的请求只由新版本的实例处理。
这种方式 gateway 的 route 配置进行普通的配置即可:
每个服务实例配置自己的版本值:
同时在 gateway 上添加以下配置和代码:
自定义 loadbalancer 的 ServiceInstanceListSupplier,根据服务版本控制流量分配。
上面的几种方法虽然可以实现特定情况下的流量在不同版本的服务实例间分配,但是都不太完美。灰度发布应该将新增接口(功能)调用全部导入新版本的服务实例,而保持不变的接口(功能)流量则在新旧版本实例间按设定的比例分配,然后逐渐调整新旧比例值,直到所有流量都迁移到新版本中。同时服务的名字需要保持不变,用户也不用感知到版本的变化(比如,不需要在请求中添加特殊的头部,用来标识需要调用的服务版本,实际调用哪个版本应该由系统对用户隐藏)。
为实现上述目的,我们可以在 中添加如下 route 配置:
上面的 route 配置目的为:新增接口(假设全部的新增接口的路径前缀统一为 v2)匹配 app-server-v2 路由,匹配后添加请求头,值为,目的是让基于的思路将请求都转发给 v2 版本的服务实例。保持不变的接口的路由配置则也保持不变,自定义的 loadbalancer 策略会根据配置比例将请求分发给不同版本的服务实例。
然后实现自定义的。它实现的功能为:如果请求中存在头,则从服务实例列表中查找所有的值与请求头值相同的实例,然后返回找到的实例列表。如果请求中不存在在头,则根据配置的服务不同版本的流量分配比例,计算当前请求应该分配给哪个版本的服务实例,然后从服务实例列表中查找所有的值等于选中版本号的服务实例,然后返回找到的实例列表。如果上面两种方式找到的服务实例列表均为空,则返回所有的服务实例。代码如下:
然后将自定义注册到 loadbalancer 中。
在 上配置服务不同版本的流量分配比例:
最后,服务实例的配置相应的版本号:
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/9990.html