微服务实现全链路灰度发布

一、实现步骤

  1. 再请求 Header 中打上标签,例如再 Header 中添加 "gray-tag: true" ,其表示要进行灰度测试(访问灰度服务),而其他则访问正式服务。
  2. 在负载均衡器 Spring Cloud LoadBalancer 中,拿到 Header 仲的 "gray-tag" 进行判断,如果此标签不为空,并等于"true" 的话,表示要访问灰度发布的服务,否则只访问正式的服务。
  3. 在网关 Spring Cloud Gateway 中,将 Header 标签 "gray-tag:true" 继续往下一个调用服务中传递。
  4. 在后续的调用服务中,需要实现两个关键功能:
    ● 在负载均衡器 Spring Cloud LoadBalancer 中,判断回复发布标签,将请求分发到对应服务
    ● 将灰度发布标签继续传递给下一个调用的服务.如此反复传递

二、服务模块

2.1 注册为灰色服务实例

spring:application:name: user-servicecloud:nacos:discovery:server-addr: localhost:8848username: nacospassword: nacosmetadata:{"gray-tag":true} #标识当前为灰度节点
server:port: 0

2.2 设置负载均衡器

在服务启动类设置父子均衡器和Openfeign 服务 

@SpringBootApplication
@LoadBalancerClients(defaultConfiguration = GllobalLoadbanlancerConfig.class)
@EnableFeignClients
public class UserServiceGrayApplication {public static void main(String[] args) {SpringApplication.run(UserServiceGrayApplication.class, args);}}

2.3 传递灰度标签

package com.example.userservicegray.config;import com.example.globalconfigdemo.global.GlobalVarible;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;import java.util.Enumeration;@Component
public class FeignRequestInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate requestTemplate) {// 从 RequestContextHolder 中获取 HttpServletRequestServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();if(request.getHeader(GlobalVarible.GRAY_TAG)!=null&&request.getHeader(GlobalVarible.GRAY_TAG)=="true"){requestTemplate.header(GlobalVarible.GRAY_TAG,"true");}}
//    @Override
//    public void apply(RequestTemplate requestTemplate) {
//        // 从 RequestContextHolder 中获取 HttpServletRequest
//        ServletRequestAttributes attributes = (ServletRequestAttributes)
//                RequestContextHolder.getRequestAttributes();
//        HttpServletRequest request = attributes.getRequest();
//        Enumeration<String> headerNames = request.getHeaderNames();
//        while (headerNames.hasMoreElements()){
//            String key = headerNames.nextElement();
//            String value = request.getHeader(key);
//            requestTemplate.header(key,value);
//        }
//    }
}

三、网关模块

网关传递灰度发布标识

package com.example.gatewayservice;import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
public class GatewayFilter implements GlobalFilter, Ordered {@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request= exchange.getRequest();ServerHttpResponse response= exchange.getResponse();if(request.getQueryParams().getFirst(GlobalVarible.GRAY_TAG)!=null&&request.getQueryParams().getFirst(GlobalVarible.GRAY_TAG)=="true") {response.getHeaders().set(GlobalVarible.GRAY_TAG, "true");}System.out.println("======================================");return chain.filter(exchange);}@Overridepublic int getOrder() {return 0;}
}

四、自定义负载均衡模块

4.1 自定义负载均衡器

package com.example.globalconfigdemo.global;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.core.*;
import org.springframework.http.HttpHeaders;
import reactor.core.publisher.Mono;import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;public class GlobalLoadbancer implements ReactorServiceInstanceLoadBalancer {private static final Log log = LogFactory.getLog(RoundRobinLoadBalancer.class);private AtomicInteger position;private String serviceId;ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;public GlobalLoadbancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {this(serviceInstanceListSupplierProvider, serviceId, (new Random()).nextInt(1000));}public GlobalLoadbancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId, int seedPosition) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;this.position = new AtomicInteger(seedPosition);}public Mono<Response<ServiceInstance>> choose(Request request) {ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier)this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get(request).next().map((serviceInstances) -> {//此时调用时,将request作为参数传给调用方法,在得到服务实例时通过判断请求头中的标识来返回实例return this.processInstanceResponse(supplier, serviceInstances,request);});}private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances,Request request) {//将request 传给调用方法Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances,request);if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());}return serviceInstanceResponse;}private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances,Request request) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " + this.serviceId);}return new EmptyResponse();} else if (instances.size() == 1) {return new DefaultResponse((ServiceInstance)instances.get(0));} else {//得到 Request 对象,[通过方法传递参数得到此对象]//从 Request 对象的 Header 中得到灰度标签RequestDataContext requestDataContext= (RequestDataContext) request.getContext();HttpHeaders headers=requestDataContext.getClientRequest().getHeaders();if(headers.get(GlobalVarible.GRAY_TAG)!=null&&headers.get(GlobalVarible.GRAY_TAG).get(0).equals("true")){List<ServiceInstance> grayInstance=instances.stream().filter(s->s.getMetadata().get(GlobalVarible.GRAY_TAG)!=null&&s.getMetadata().get(GlobalVarible.GRAY_TAG).equals("true")).toList();//判断灰度列表不为空if(grayInstance.size()>0){instances=grayInstance;}}else {instances=instances.stream().filter(s->s.getMetadata().get(GlobalVarible.GRAY_TAG)==null || !s.getMetadata().get(GlobalVarible.GRAY_TAG).equals("true")).toList();}int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;ServiceInstance instance = instances.get(pos % instances.size());return new DefaultResponse(instance);}}
}

4.2 封装自定义负载均衡器

package com.example.globalconfigdemo.global.config;import com.example.globalconfigdemo.global.GlobalLoadbancer;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;public class GllobalLoadbanlancerConfig {@Beanpublic ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty("loadbalancer.client.name");return new GlobalLoadbancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/874023.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Deepin系统,中盛科技温湿度模块读温度纯c程序(备份)

#include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <termios.h>int main() {int fd;struct termios options;// 打开串口设备fd open("/dev/ttyMP0", O_RDWR | O_NOCTTY|O_NDELAY); //O_NDELAY:打开设备不阻塞//O_NOCTT…

Qt Creator配置以及使用Valgrind - 检测内存泄露

Qt Creator配置以及使用Valgrind - 检测内存泄露 引言一、下载安装1.1 下载源码1.2 安装 二、配置使用2.1 Qt Creator配置2.2 使用2.3 更多详细信息可参考官方文档&#xff1a; 三、参考链接 引言 Valgrind是一个在Linux平台下广泛使用的开源动态分析工具&#xff0c;它提供了一…

任务2:python+InternStudio 关卡

任务地址 https://github.com/InternLM/Tutorial/blob/camp3/docs/L0/Python/task.md 文档 https://github.com/InternLM/Tutorial/tree/camp3/docs/L0/Python 任务 Python实现wordcount import re import collectionstext """ Got this panda plush to…

【机器学习】智能驱动未来:机器学习在能源效率提升与环境管理中的创新应用

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀目录 &#x1f50d;1. 引言&#x1f4d2;2. 机器学习能源环境领域的应用潜力&#x1f304;能源效率提升&#x1f3de;️环境管理⛰️具体案例…

Linux之旅:常用的指令,热键和权限管理

目录 前言 1. Linux指令 &#xff08;1&#xff09; ls &#xff08;2&#xff09; pwd 和 cd &#xff08;3&#xff09;touch 和 mkdir &#xff08;4&#xff09; rmdir 和 rm &#xff08;5&#xff09;cp &#xff08;6&#xff09;mv &#xff08;7&#xff09;…

开发工具推荐:await-to-js

目录 前言&#xff1a; 1. .then().catch() 2. async await 3. await-to-js 前言&#xff1a; 今天给大家推荐一块我觉得用着还不错的工具&#xff0c;await-to-js&#xff1b; await-to-js - npm GitHub - scopsy/await-to-js: Async await wrapper for easy error ha…

HTML+CSS+JS精美气泡提示框

源代码在效果图后面 点赞❤️关注&#x1f49b;收藏⭐️ 主要实现&#xff1a;提示框出现和消失两种丝滑动画 弹出气泡提示框后延迟2秒自动消失 效果图 错误框 正确 警告 提示 源代码 <!DOCTYPE html> <html lang"en"> <head><meta cha…

PHP场地预约共享茶室棋牌室小程序系统源码

&#x1f375;&#x1f3b2;【聚会新宠】场地预约神器&#xff0c;共享茶室棋牌室小程序大揭秘&#xff01;&#x1f389; &#x1f3e1;【开篇&#xff1a;告别繁琐&#xff0c;聚会新选择】&#x1f3e1; 还在为找不到合适的聚会场地而烦恼吗&#xff1f;想要一个既私密又舒…

JS 原型与原型链图解:彻底搞懂的终极指南

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热爱技术和分享&#xff0c;欢迎大家交流&#xff0c;一起学习进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 在JavaScript中&#xff0c;原型和原型链是非常重要的知识点&#xff0c;只有理解了…

2.2.填充和步幅

我们已经知道&#xff0c;卷积的输出形式取决于输入形式和卷积核的形式。 ​ 此外还有其他因素会影响输出的大小。假设以下情景&#xff1a; 有时&#xff0c;在应用了连续的卷积之后&#xff0c;我们最终得到的输出远小于输入大小。这是由于卷积核的宽度和高度通常大于1所导致…

驱动框架——CMSIS第一部分 RTE驱动框架介绍

一、介绍CMISIS 什么是CMSIS&#xff08;cortex microcontrol software interface standard一种软件标准接口&#xff09;&#xff0c;官网地址&#xff1a;https://arm-software.github.io/CMSIS_6/latest/General/index.html 包含的core、driver、RTOS、dsp、nn等部分&…

Linux系统编程:自定义协议(序列化和反序列化)

1. 协议 在之前我们谈到&#xff0c;协议就是一种"约定"&#xff0c;socket api接口&#xff0c;在读写数据时&#xff0c;都是按照"字符串"的方式来发送接收的&#xff0c;那么我们要传输一些"结构化"数据时怎么办呢&#xff1f;,比如说一个结构…

前端-04-VScode敲击键盘有键入音效,怎么关闭

目录 问题解决办法 问题 今天正在VScode敲项目&#xff0c;不知道是按了什么快捷键还是什么的&#xff0c;敲击键盘有声音&#xff0c;超级烦人啊&#xff01;&#xff01;于是我上网查了一下&#xff0c;应该是开启了VScode的键入音效&#xff0c;下面是关闭键入音效的办法。…

分布式服务框架zookeeper+消息队列kafka

一、zookeeper概述 zookeeper是一个分布式服务框架&#xff0c;它主要是用来解决分布式应用中经常遇到的一些数据管理问题&#xff0c;如&#xff1a;命名服务&#xff0c;状态同步&#xff0c;配置中心&#xff0c;集群管理等。 在分布式环境下&#xff0c;经常需要对应用/服…

云计算数据中心(三)

目录 四、自动化管理&#xff08;一&#xff09;自动化管理的特征&#xff08;二&#xff09;自动化管理实现阶段&#xff08;三&#xff09;Facebook自动化管理 五、容灾备份&#xff08;一&#xff09;容灾系统的等级标准&#xff08;二&#xff09;容灾备份的关键技术&#…

Flink CDC 同步表至Paimon 写数据流程,write算子和commit算子。

Flink CDC 同步表至Paimon 写数据流程,write算子和commit算子。(未吃透版) 流程图 一般基本flink cdc 任务同步数据至paimon表时包含3个算子,source、write、global commit。 source端一般是flink connector实现的连接源端进行获取数据的过程,本文探究的是 source算子获…

Haproxy服务

目录 一.haproxy介绍 1.主要特点和功能 2.haproxy 调度算法 3.haproxy 与nginx 和lvs的区别 二.安装 haproxy 服务 1. yum安装 2.第三方rpm 安装 3.编译安装haproxy 三.配置文件详解 1.官方地址配置文件官方帮助文档 2.HAProxy 的配置文件haproxy.cfg由两大部分组成&…

Synchronized升级到重量级锁会发生什么?

我们从网上看到很多&#xff0c;升级到重量锁的时候不会降级&#xff0c;再来线程都是重量级锁 今天我们来实验一把真的是这样的吗 1.首选导入Java对象内存布局的工具库&#xff1a; <dependency><groupId>org.openjdk.jol</groupId><artifactId>jol-…

什么是内网穿透?

前言 我们常常会听到“内网穿透”这个术语&#xff0c;但对于很多人来说&#xff0c;它可能还比较陌生。作为一个在网络世界中摸索了一段时间的使用者&#xff0c;我来和大家分享一下我对内网穿透的理解。 目录 一、内网穿透介绍 二、发现 三、特点 四、优势 简单来说&am…

初识godot游戏引擎并安装

简介 Godot是一款自由开源、由社区驱动的2D和3D游戏引擎。游戏开发虽复杂&#xff0c;却蕴含一定的通用规律&#xff0c;正是为了简化这些通用化的工作&#xff0c;游戏引擎应运而生。Godot引擎作为一款功能丰富的跨平台游戏引擎&#xff0c;通过统一的界面支持创建2D和3D游戏。…