bluejay
2025-04-08 41d85abf23c123e948b1a585c413f5a0a29a504f
gateway 根据matedate转发到本地服务

(cherry picked from commit bee37b65c32864286fc62afdf948ae89bc3e3583)
已修改1个文件
已添加3个文件
180 ■■■■■ 文件已修改
aps-gateway/pom.xml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-gateway/src/main/java/com/aps/gateway/config/loadBalancer/GrayLoadBalancer.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-gateway/src/main/java/com/aps/gateway/config/loadBalancer/GrayReactiveLoadBalancerClientFilter.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-gateway/src/main/java/com/aps/gateway/config/loadBalancer/VersionGrayLoadBalancer.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-gateway/pom.xml
@@ -82,7 +82,16 @@
            <artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
            <version>${springdoc.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.37</version>
        </dependency>
    </dependencies>
    <build>
aps-gateway/src/main/java/com/aps/gateway/config/loadBalancer/GrayLoadBalancer.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,10 @@
package com.aps.gateway.config.loadBalancer;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.http.server.reactive.ServerHttpRequest;
public interface GrayLoadBalancer {
    ServiceInstance choose(String serviceId, ServerHttpRequest request);
}
aps-gateway/src/main/java/com/aps/gateway/config/loadBalancer/GrayReactiveLoadBalancerClientFilter.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,99 @@
package com.aps.gateway.config.loadBalancer;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.util.Asserts;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.LoadBalancerUriTools;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.gateway.config.GatewayLoadBalancerProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter;
import org.springframework.cloud.gateway.support.DelegatingServiceInstance;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.net.URI;
@Slf4j
@Component
public class GrayReactiveLoadBalancerClientFilter extends ReactiveLoadBalancerClientFilter  {
    private final static String SCHEME = "lb";
    private static final int LOAD_BALANCER_CLIENT_FILTER_ORDER = 10150;
    private final GrayLoadBalancer grayLoadBalancer;
    private final GatewayLoadBalancerProperties loadBalancerProperties;
    public GrayReactiveLoadBalancerClientFilter(LoadBalancerClientFactory clientFactory, GatewayLoadBalancerProperties loadBalancerProperties, GrayLoadBalancer grayLoadBalancer) {
        super(clientFactory, loadBalancerProperties);
        this.loadBalancerProperties = loadBalancerProperties;
        this.grayLoadBalancer = grayLoadBalancer;
    }
    @Override
    public int getOrder() {
        return LOAD_BALANCER_CLIENT_FILTER_ORDER;
    }
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        URI url = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        String schemePrefix = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);
        // ç›´æŽ¥æ”¾è¡Œ
        if (url == null || (!SCHEME.equals(url.getScheme()) && !SCHEME.equals(schemePrefix))) {
            return chain.filter(exchange);
        }
        // ä¿ç•™åŽŸå§‹url
        ServerWebExchangeUtils.addOriginalRequestUrl(exchange, url);
        if (log.isTraceEnabled()) {
            log.trace(ReactiveLoadBalancerClientFilter.class.getSimpleName() + " url before: " + url);
        }
        return choose(exchange).doOnNext(response -> {
            if (!response.hasServer()) {
                throw NotFoundException.create(loadBalancerProperties.isUse404(),
                        "Unable to find instance for " + url.getHost());
            }
            URI uri = exchange.getRequest().getURI();
            // if the `lb:<scheme>` mechanism was used, use `<scheme>` as the default,
            // if the loadbalancer doesn't provide one.
            String overrideScheme = null;
            if (schemePrefix != null) {
                overrideScheme = url.getScheme();
            }
            DelegatingServiceInstance serviceInstance = new DelegatingServiceInstance(response.getServer(),
                    overrideScheme);
            URI requestUrl = LoadBalancerUriTools.reconstructURI(serviceInstance, uri);
            if (log.isTraceEnabled()) {
                log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
            }
            exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);
        }).then(chain.filter(exchange));
    }
    /**
     * èŽ·å–å®žä¾‹
     * @param exchange ServerWebExchange
     * @return ServiceInstance
     */
    private Mono<Response<ServiceInstance>> choose(ServerWebExchange exchange) {
        URI uri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        Asserts.notNull(uri, "uri");
        ServiceInstance serviceInstance = grayLoadBalancer.choose(uri.getHost(), exchange.getRequest());
        return Mono.just(new DefaultResponse(serviceInstance));
    }
}
aps-gateway/src/main/java/com/aps/gateway/config/loadBalancer/VersionGrayLoadBalancer.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
package com.aps.gateway.config.loadBalancer;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@RequiredArgsConstructor
@Component
public class VersionGrayLoadBalancer implements GrayLoadBalancer {
    private final DiscoveryClient discoveryClient;
    private final static String VERSION = "version";
    /**
     * æ ¹æ®serviceId ç­›é€‰å¯ç”¨æœåŠ¡
     * @param serviceId æœåŠ¡ID
     * @param request å½“前请求
     * @return ServiceInstance
     */
    @Override
    public ServiceInstance choose(String serviceId, ServerHttpRequest request) {
        List<ServiceInstance> instances = discoveryClient.getInstances(serviceId);
        // æ³¨å†Œä¸­å¿ƒæ— å®žä¾‹ æŠ›å‡ºå¼‚常
        if (instances.isEmpty()) {
            log.warn("No instance available for {}", serviceId);
            throw new NotFoundException("No instance available for " + serviceId);
        }
        // èŽ·å–è¯·æ±‚version,无则随机返回可用实例
        String reqVersion = request.getHeaders().getFirst(VERSION);
        if (Strings.isBlank(reqVersion)) {
            return instances.get(RandomUtil.randomInt(instances.size()));
        }
        // éåŽ†å¯ä»¥å®žä¾‹å…ƒæ•°æ®ï¼Œè‹¥åŒ¹é…åˆ™è¿”å›žæ­¤å®žä¾‹
        List<ServiceInstance> availableList = instances.stream()
                .filter(instance -> reqVersion
                        .equalsIgnoreCase(MapUtil.getStr(instance.getMetadata(), VERSION)))
                .collect(Collectors.toList());
        if (CollUtil.isEmpty(availableList)) {
            return instances.get(RandomUtil.randomInt(instances.size()));
        }
        return availableList.get(RandomUtil.randomInt(availableList.size()));
    }
}