SpringCloud全链路灰度发布

日升时奋斗,日落时自省

目录

1、实现框架

2、负载均衡模块

3、封装负载均衡器

4、网关模块

5、服务模块

5.1、注册为灰度服务实例

5.2、设置负载均衡器

5.3、传递灰度标签


1、实现框架

Spring Cloud全链路灰色发布实现构架:

灰度发布的具体实现 :

前端程序在灰度测试用户Header头中打上标签,例如Header中添加一个参数"gray-tay:true",其表示要进行会灰度测试的(访问灰度服务),而其他则为正式服务

在负载均衡器Spring Cloud LoadBalancer中,拿到Header中的"gray-tag"进行判断,如果此标签不为空,并等于"true"的话,表示要访问灰度发布的服务,否则只访问正式的服务

在网关Spring Cloud Gateway中,Header标签"gray-tag:true"继续往下一服务中传传递(因为通过网关的时候,是从新发送请求,Header中是没有灰度标签的)

在后续的调用服务中,需要实现以下两个关键功能:

  • 在负载均衡器Spring Cloud LoadBalancer中,判断灰度发布标签,将请求分发到对应服务
  • 将灰度发布标签(如果存在),继续传递给下一个调用的服务

经过第四步的反复传递之后,整个Spring Cloud全链路的灰度发布就完成了

2、负载均衡模块

实现ReactorServiceInstanceLoadBalancer接口

java 复制代码
public class GrayLoadBalancer implements ReactorServiceInstanceLoadBalancer{
    private static final Log log = LogFactory.getLog(GrayLoadBalancer.class);
    private final String serviceId;
    private AtomicInteger position; // 位置,下标
    private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;

    public GrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        this.position = new AtomicInteger(new Random().nextInt(1000));  //随机进行设置一个值
    }

    public Mono<Response<ServiceInstance>> choose(Request request) {
        // 提供备选的服务实例列表
        ServiceInstanceListSupplier supplier = (ServiceInstanceListSupplier) this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);
        // 选择服务实例
        return supplier.get(request).next().map((serviceInstances) -> {
            return this.processInstanceResponse(supplier, serviceInstances, request);
        });
    }

    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier,
                                                              List<ServiceInstance> serviceInstances,
                                                              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 { // 服务不为空
            // 灰度节点的业务实现
            // 0.得到 Request 对象[通过方法参数的传递得到此对象]
            // 1.从 Request 对象的 Header 中得到灰度标签
            RequestDataContext requestContext = (RequestDataContext) request.getContext();
            HttpHeaders headers = requestContext.getClientRequest().getHeaders();
            List<String> headersList = headers.get(GlobalVariable.GRAY_TAGE);
            if (headersList != null && headersList.size() > 0 &&
                    headersList.get(0).equals("true")) { // 灰度请求
                // 灰度列表  这里采用lambda的方法进行过滤
                List<ServiceInstance> grayList = instances.stream().
                        filter(i -> i.getMetadata().get(GlobalVariable.GRAY_TAGE) != null &&
                                i.getMetadata().get(GlobalVariable.GRAY_TAGE).equals("true")).
                        toList();
                if (grayList.size() > 0) { // 存在灰度服务节点
                    instances = grayList;
                }
            } else { // 正式节点
                // 2.将实例进行分组【正式服务列表|灰度服务列表】
                instances = instances.stream().
                        filter(i -> i.getMetadata().get(GlobalVariable.GRAY_TAGE) == null ||
                                !i.getMetadata().get(GlobalVariable.GRAY_TAGE).equals("true")).
                        toList();
            }
            // 3.使用负载均衡算法选择上一步列表中的某一个节点
            int pos = this.position.incrementAndGet() & Integer.MAX_VALUE;
            ServiceInstance instance = (ServiceInstance)instances.get(pos % instances.size());
            return new DefaultResponse(instance);
        }
    }
}

3、封装负载均衡器

负载均衡器修改之后,我们需要使用修改后的负载均衡器

java 复制代码
public class GrayLoadBalancerConfig {
    @Bean
    public ReactorLoadBalancer<ServiceInstance> GrayLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new GrayLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}

4、网关模块

网关是能收到灰度发布标识,但是调用的服务是不能获得Header中的灰度标签,需要在网关这里传递给调用的服务

java 复制代码
@Component // 实现全局过滤器 进行过滤设置
public class LoadBalancerFilter implements GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 事件处理 exchange 获取请求头
        ServerHttpRequest request=exchange.getRequest();
        // 事件处理 exchange 获取响应头
        ServerHttpResponse response = exchange.getResponse();
        // 请求头参数 获取的参数 获取 key 对应的 value值
        if(request.getQueryParams().getFirst(GlobalVariable.GRAY_TAGE)!=null){
            //获取响应请求头 设置灰度标签的参数 为 true
            response.getHeaders().set(GlobalVariable.GRAY_TAGE,"true");
        }
        // 返回 一个调用链 过滤条件的
        return chain.filter(exchange);
    }
}

5、服务模块

5.1、注册为灰度服务实例

在负载均衡器上获得了metaData数据,这个数据就是从灰色服务实例中获取,数据存储在nacos中

java 复制代码
spring:
  application:
    name: user-service-gray
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        username: nacos
        password: nacos
        metadata: {"gray-tag":"true"}  #metadata可以自定义一些元数据
server:
  port: 0

5.2、设置负载均衡器

在服务启动类设置负载均衡器和开启OpenFeign服务:

defaultConfiguration = GrayLoadBalancerConfig.class全局负载均衡器的配置

java 复制代码
@SpringBootApplication
@EnableFeignClients  //启用Fegin客户端使用
//这里需要调用我们写的负载均衡器,我们自定义的负载均衡器主要就是为了检测gray参数
@LoadBalancerClients(defaultConfiguration = GrayLoadBalancerConfig.class)  
public class NewUserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(NewUserServiceApplication.class, args);
    }

}

5.3、传递灰度标签

针对其他服务实例需要处理请求头信息

RequestInterceptor是一个Spring Cloud Feign中的接口,它用于在发起Feign请求前和请求后执行一些自定义的操作,例如添加请求头信息、记录请求日志、实现认证授权等

RequestInterceptor接口中包含一个方法:

apply方法:在发送请求之前执行的操作,比如添加请求头、修改请求路径或请求参数等

java 复制代码
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
    //相当于是一个前置操作 处理请求头信息,记录请求日志、实现认证授权
    @Override
    public void apply(RequestTemplate requestTemplate) {
        //获取请求参数
        ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        //拿到请求  
        HttpServletRequest request = attributes.getRequest();
        //获取请求头参数  当然调用上面这条代码的 request是一样的
        Enumeration<String> headerNames = attributes.getRequest().getHeaderNames();
        //将获取的请求中的所有参数
        while(headerNames.hasMoreElements()){
            //将key先获取
            String key=headerNames.nextElement();
            //通过key 获得 value
            String value=request.getHeader(key);
            //将获取的参数 放到header头中采用键值 key:value 放到请求模板中
            requestTemplate.header(key,value);
        }
    }
}
相关推荐
customer0813 小时前
【开源免费】基于SpringBoot+Vue.JS贸易行业crm系统(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·开源
等一场春雨1 天前
Alibaba Spring Cloud 十三 Nacos,Gateway,Nginx 部署架构与负载均衡方案
nginx·spring cloud·gateway
小白的一叶扁舟2 天前
Nacos 2.5.0 全文详解及配置(并解决WebServerException: Unable to start mbedded Tomcat)
java·spring cloud·nacos
天使day2 天前
SpringCloud两种注册中心
java·spring·spring cloud
Mr.Demo.2 天前
[Spring] Gateway详解
java·spring·spring cloud·gateway
Cent'Anni3 天前
【Nacos】负载均衡
java·分布式·spring cloud·负载均衡
跟我很快乐3 天前
Docker搭建minio对象存储
spring cloud·docker·容器
ChinaRainbowSea3 天前
10. SpringCloud Alibaba Sentinel 规则持久化部署详细剖析
java·spring·spring cloud·sentinel·负载均衡
等一场春雨3 天前
Alibaba Spring Cloud 十七 Sentinel熔断降级
spring·spring cloud·sentinel
红衣女妖仙3 天前
分布式微服务系统简述
分布式·spring cloud·微服务·架构