Spring-cloud-Gateway配置根据请求头不同,请求不同服务实例

目前公司有一个服务,有两个开发版本并行开发,需要同时测试联调;由于程序需要经过网关鉴权,不可采用直连形式区分服务;最后改造gateway服务,通过前端请求头添加version=dev调用服务,区分使用哪个实例提供服务;步骤如下:

剔除原有ribbon依赖:

  <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-loadbalancer</artifactId>
        </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-loadbalancer</artifactId>
    </dependency>

增加两个配置类

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.*;
import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.http.HttpHeaders;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * Description:
 * 自定义灰度
 * 通过给请求头添加Version 与 Service Instance 元数据属性进行对比
 */
public class VersionGrayLoadBalancer implements ReactorServiceInstanceLoadBalancer {


    public static final Logger log = LoggerFactory.getLogger(VersionGrayLoadBalancer.class);
    private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;
    private final String serviceId;

    private final AtomicInteger position;

    public VersionGrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {
        this(serviceInstanceListSupplierProvider,serviceId,new Random().nextInt(1000));
    }

    public VersionGrayLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider,
                                   String serviceId, int seedPosition) {
        this.serviceId = serviceId;
        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;
        this.position = new AtomicInteger(seedPosition);
    }

    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);

        return supplier.get(request).next()
                .map(serviceInstances -> processInstanceResponse(serviceInstances,request));

    }


    private Response<ServiceInstance> processInstanceResponse(List<ServiceInstance> instances, Request request) {
        if (instances.isEmpty()) {
            log.warn("No servers available for service: " + this.serviceId);
            return new EmptyResponse();
        } else {
            DefaultRequestContext requestContext = (DefaultRequestContext) request.getContext();
            RequestData clientRequest = (RequestData) requestContext.getClientRequest();
            HttpHeaders headers = clientRequest.getHeaders();
            String reqVersion = headers.getFirst("version");
            log.info("request header version : {}",reqVersion );
            //判断元数据里是否有version
            List<ServiceInstance> serviceInstances = instances.stream()
                    .filter(instance ->  instance.getMetadata().get("version")!=null &&instance.getMetadata().get("version").equals(reqVersion))
                    .collect(Collectors.toList());

            if(serviceInstances.size() > 0){
                return processRibbonInstanceResponse(serviceInstances);
            }else{
                
                List<ServiceInstance> serviceInstances1 = instances.stream()
                        .filter(instance -> instance.getMetadata().get("version")==null)
                        .collect(Collectors.toList());
                return processRibbonInstanceResponse(serviceInstances1);
            }
        }
    }

    /**
     * 负载均衡器
     * 参考 org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer#getInstanceResponse
     * @author javadaily
     */
    private Response<ServiceInstance> processRibbonInstanceResponse(List<ServiceInstance> instances) {
        int pos = Math.abs(this.position.incrementAndGet());
        ServiceInstance instance = instances.get(pos % instances.size());
        return new DefaultResponse(instance);
    }

}

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;

/**
 * Description:
 * 自定义负载均衡器配置实现类
 */
public class VersionLoadBalancerConfiguration {
    @Bean
    ReactorLoadBalancer<ServiceInstance> versionGrayLoadBalancer(Environment environment,
                                                                 LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new VersionGrayLoadBalancer(
                loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }

}

在gateway启动类上添加注解

@LoadBalancerClient(value = "服务名称", configuration = VersionLoadBalancerConfiguration.class)

这样gateway 网关整体改造完成;
现在需要在本地启动服务上添加元数据:spring.cloud. nacos.discovery.metadata.version= dev

这样打开nacos可以看到
在这里插入图片描述
此时使用postman模拟请求添加headers version=dev的,请求到配置元数据的服务上,不添加的请求的没有的服务上;
在这里插入图片描述
最终实现根据不同请求头来访问不同服务实例;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Cloud Gateway中修改请求参数,可以使用Spring Cloud Gateway提供的一些过滤器来实现。具体步骤如下: 1. 创建一个过滤器类,继承AbstractGatewayFilterFactory类,实现filter方法,该方法用于修改请求参数。例如: ``` public class ModifyRequestBodyGatewayFilterFactory extends AbstractGatewayFilterFactory<ModifyRequestBodyGatewayFilterFactory.Config> { public ModifyRequestBodyGatewayFilterFactory() { super(Config.class); } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { // 获取请求参数 ServerHttpRequest request = exchange.getRequest(); Flux<DataBuffer> body = request.getBody(); MediaType contentType = request.getHeaders().getContentType(); Charset charset = contentType.getCharset() != null ? contentType.getCharset() : StandardCharsets.UTF_8; // 修改请求参数 String bodyStr = body.map(dataBuffer -> { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); DataBufferUtils.release(dataBuffer); return new String(bytes, charset); }).reduce((s1, s2) -> s1 + s2).orElse(""); String modifiedBodyStr = modify(bodyStr); // 构造新的请求参数 byte[] newBodyBytes = modifiedBodyStr.getBytes(charset); DataBuffer newBodyBuffer = request.bufferFactory().wrap(newBodyBytes); // 构造新的请求对象 ServerHttpRequest newRequest = request.mutate().body(Mono.just(newBodyBuffer)).build(); // 继续执行后续过滤器链 return chain.filter(exchange.mutate().request(newRequest).build()); }; } private String modify(String body) { // 在此处实现修改请求参数的逻辑 return body; } public static class Config { // 配置参数 } } ``` 2. 在配置文件中配置过滤器,例如: ``` spring: cloud: gateway: routes: - id: modify_request_body_route uri: http://localhost:8080 predicates: - Path=/modifyRequestBody filters: - ModifyRequestBody=modifyRequestBodyFilter default-filters: - ModifyRequestBody=defaultModifyRequestBodyFilter ``` 其中,ModifyRequestBody为过滤器类的名称,modifyRequestBodyFilter和defaultModifyRequestBodyFilter为过滤器实例的名称,可以根据实际需求进行修改。 3. 在控制器中接收修改后的请求参数,例如: ``` @PostMapping("/modifyRequestBody") public String modifyRequestBody(@RequestBody String requestBody) { // 处理请求参数 return "success"; } ``` 在上述代码中,@RequestBody注解用于接收请求参数。如果请求参数已经被修改,那么控制器中接收到的参数就是修改后的参数

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值