From 5dae7f4ccb3697f52b02c0d507263a84cc8b74fe Mon Sep 17 00:00:00 2001 From: zhangli <123879394@qq.com> Date: Fri, 22 Aug 2025 22:32:29 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B7=AF=E7=94=B1=E6=94=B9=E4=B8=BA=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../component/DynamicRouteScheduler.java | 4 +- .../gateway/config/CustomRouteConfig.java | 19 +-- .../gateway/config/DynamicRouteConfig.java | 122 ++++++++++++++++++ 3 files changed, 126 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/sczx/gateway/config/DynamicRouteConfig.java diff --git a/src/main/java/com/sczx/gateway/component/DynamicRouteScheduler.java b/src/main/java/com/sczx/gateway/component/DynamicRouteScheduler.java index 76a3a21..03f656d 100644 --- a/src/main/java/com/sczx/gateway/component/DynamicRouteScheduler.java +++ b/src/main/java/com/sczx/gateway/component/DynamicRouteScheduler.java @@ -18,8 +18,8 @@ public class DynamicRouteScheduler { @Autowired private DiscoveryClient discoveryClient; - // 每30秒刷新一次路由(可按需调整) - @Scheduled(fixedRate = 30_000) + // 延迟5秒后开始执行,避免与启动时的自动刷新冲突,每30秒刷新一次路由(可按需调整) + @Scheduled(fixedRate = 30_000, initialDelay = 5_000) public void refreshRoutes() { log.info("refreshRoutes"); publisher.publishEvent(new RefreshRoutesEvent(this)); diff --git a/src/main/java/com/sczx/gateway/config/CustomRouteConfig.java b/src/main/java/com/sczx/gateway/config/CustomRouteConfig.java index 950c3bb..bf5f0be 100644 --- a/src/main/java/com/sczx/gateway/config/CustomRouteConfig.java +++ b/src/main/java/com/sczx/gateway/config/CustomRouteConfig.java @@ -14,7 +14,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @Slf4j -@Configuration +//@Configuration public class CustomRouteConfig { @Autowired private DiscoveryClient discoveryClient; @@ -27,21 +27,6 @@ public class CustomRouteConfig { // .route("sczx-user", r -> r.path("/zc/user/**") // .filters(f -> f.rewritePath("/zc/user/(?.*)", "/${segment}")) // .uri(uriWithCustomLoadBalance("sczx-user"))) -// .route("sczx-store", r -> r.path("/zc/store/**") -// .filters(f -> f.rewritePath("/zc/store/(?.*)", "/${segment}")) -// .uri(uriWithCustomLoadBalance("sczx-store"))) -// .route("sczx-car", r -> r.path("/zc/car/**") -// .filters(f -> f.rewritePath("/zc/car/(?.*)", "/${segment}")) -// .uri(uriWithCustomLoadBalance("sczx-car"))) -// .route("sczx-order", r -> r.path("/zc/order/**") -// .filters(f -> f.rewritePath("/zc/order/(?.*)", "/${segment}")) -// .uri(uriWithCustomLoadBalance("sczx-order"))) -// .route("sczx-sync", r -> r.path("/zc/sync/**") -// .filters(f -> f.rewritePath("/zc/sync/(?.*)", "/${segment}")) -// .uri(uriWithCustomLoadBalance("sczx-sync"))) -// .route("sczx-singlepay", r -> r.path("/zc/singlepay/**") -// .filters(f -> f.rewritePath("/zc/singlepay/(?.*)", "/${segment}")) -// .uri(uriWithCustomLoadBalance("sczx-singlepay"))) // .build(); //// .route("sczx_user", r -> r.path("/zc/user/**") //// .uri("lb://sczx_user")) @@ -50,7 +35,7 @@ public class CustomRouteConfig { //// .build(); // } - @Bean +// @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { RouteLocatorBuilder.Builder routesBuilder = builder.routes(); diff --git a/src/main/java/com/sczx/gateway/config/DynamicRouteConfig.java b/src/main/java/com/sczx/gateway/config/DynamicRouteConfig.java new file mode 100644 index 0000000..625189e --- /dev/null +++ b/src/main/java/com/sczx/gateway/config/DynamicRouteConfig.java @@ -0,0 +1,122 @@ +package com.sczx.gateway.config; + + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; +import org.springframework.cloud.gateway.route.RouteDefinition; +import org.springframework.cloud.gateway.route.RouteDefinitionLocator; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.EventListener; +import org.springframework.cloud.gateway.event.RefreshRoutesEvent; +import reactor.core.publisher.Flux; + +import java.net.URI; +import java.util.ArrayList; + +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + + +@Slf4j +@Configuration +public class DynamicRouteConfig implements RouteDefinitionLocator { + + @Autowired + private DiscoveryClient discoveryClient; + + private final AtomicInteger index = new AtomicInteger(0); + + // 记录刷新次数 + private final AtomicLong refreshCount = new AtomicLong(0); + + // 记录上次刷新时间,用于防抖 + private final AtomicReference lastRefreshTime = new AtomicReference<>(0L); + + // 最小刷新间隔(毫秒) + private static final long MIN_REFRESH_INTERVAL = 3000; + + @Override + public Flux getRouteDefinitions() { + long currentTime = System.currentTimeMillis(); + long lastTime = lastRefreshTime.get(); + + // 防抖处理:如果距离上次刷新时间太短,则不处理 + if (currentTime - lastTime < MIN_REFRESH_INTERVAL && refreshCount.get() > 0) { + log.info("刷新过于频繁,跳过本次刷新"); + return Flux.empty(); + } + lastRefreshTime.set(currentTime); + log.info("开始加载路由定义,第{}次调用", refreshCount.incrementAndGet()); + List routeDefinitions = new ArrayList<>(); + + // 定义需要路由的服务列表 + String[] serviceNames = {"sczx-user", "sczx-store", "sczx-car", "sczx-order", "sczx-sync", "sczx-singlepay"}; + + // 为每个服务添加路由规则(如果服务存在) + for (String serviceName : serviceNames) { + if (hasServiceInstances(serviceName)) { + RouteDefinition routeDefinition = new RouteDefinition(); + routeDefinition.setId(serviceName); + + // 设置路径断言 + routeDefinition.setPredicates(Collections.singletonList( + new PredicateDefinition("Path=/zc/" + serviceName.split("-")[1] + "/**") + )); + + // 设置过滤器 + routeDefinition.setFilters(Collections.singletonList( + new org.springframework.cloud.gateway.filter.FilterDefinition( + "RewritePath=/zc/" + serviceName.split("-")[1] + "/(?.*), /$\\{segment}") + )); + + // 设置URI + routeDefinition.setUri(getServiceUri(serviceName)); + + routeDefinitions.add(routeDefinition); + } else { + log.warn("服务 {} 不存在实例,跳过路由配置", serviceName); + } + } + log.info("路由加载完成,共加载 {} 个路由定义", routeDefinitions.size()); + return Flux.fromIterable(routeDefinitions); + } + + /** + * 监听路由刷新事件 + */ + @EventListener + public void onRefreshRoutes(RefreshRoutesEvent event) { + long count = refreshCount.get(); + log.info("接收到路由刷新事件,当前为第{}次刷新", count); + } + + /** + * 检查是否有可用的服务实例 + */ + private boolean hasServiceInstances(String serviceName) { + List instances = discoveryClient.getInstances(serviceName); + return instances != null && !instances.isEmpty(); + } + + /** + * 获取服务URI + */ + private URI getServiceUri(String serviceName) { + log.info("{} ,开始负载均衡路由", serviceName); + List instances = discoveryClient.getInstances(serviceName); + if (instances == null || instances.isEmpty()) { + log.warn("No available instances for service: {}, returning default URI", serviceName); + return URI.create("http://localhost"); + } + + ServiceInstance selected = instances.get(Math.abs(index.getAndIncrement()) % instances.size()); + log.info("Selected instance: {}", selected); + return URI.create("http://" + selected.getHost() + ":" + selected.getPort()); + } +}