diff --git a/zdxt-web-server/zdxt-gateway-web-server/src/main/java/com/zdxt/gateway/webserver/ZdxtGatewayApplication.java b/zdxt-web-server/zdxt-gateway-web-server/src/main/java/com/zdxt/gateway/webserver/ZdxtGatewayApplication.java index 9c7082bb..1496d095 100644 --- a/zdxt-web-server/zdxt-gateway-web-server/src/main/java/com/zdxt/gateway/webserver/ZdxtGatewayApplication.java +++ b/zdxt-web-server/zdxt-gateway-web-server/src/main/java/com/zdxt/gateway/webserver/ZdxtGatewayApplication.java @@ -4,6 +4,7 @@ import com.zdxt.gateway.webserver.config.LoadBalancerConfig; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients; /** * API网关启动类 @@ -11,7 +12,11 @@ import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; * @author zdxt */ @SpringBootApplication -@LoadBalancerClient(name = "backend-service", configuration = LoadBalancerConfig.class) +@LoadBalancerClients({ + @LoadBalancerClient(name = "ent-ep-service", configuration = LoadBalancerConfig.class), + @LoadBalancerClient(name = "enf-ep-service", configuration = LoadBalancerConfig.class), + @LoadBalancerClient(name = "sup-ep-service", configuration = LoadBalancerConfig.class) +}) public class ZdxtGatewayApplication { public static void main(String[] args) { SpringApplication.run(ZdxtGatewayApplication.class, args); diff --git a/zdxt-web-server/zdxt-gateway-web-server/src/main/java/com/zdxt/gateway/webserver/config/LoadBalancerConfig.java b/zdxt-web-server/zdxt-gateway-web-server/src/main/java/com/zdxt/gateway/webserver/config/LoadBalancerConfig.java index 1db755ef..1245116b 100644 --- a/zdxt-web-server/zdxt-gateway-web-server/src/main/java/com/zdxt/gateway/webserver/config/LoadBalancerConfig.java +++ b/zdxt-web-server/zdxt-gateway-web-server/src/main/java/com/zdxt/gateway/webserver/config/LoadBalancerConfig.java @@ -1,7 +1,7 @@ package com.zdxt.gateway.webserver.config; import org.springframework.cloud.client.ServiceInstance; -import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer; +import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties; import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; @@ -9,10 +9,18 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + /** - * 负载均衡配置 - 随机策略 + * 负载均衡配置 - 优先级策略 + *

+ * 始终优先选择配置列表中的第一个健康实例, + * 首选实例不可用时按配置顺序从剩余健康实例中选取。 *

- * 替代默认的 RoundRobin 轮询策略,在存活实例间随机分配请求。 + * 通过 {@link SimpleDiscoveryProperties} 读取原始配置顺序, + * 确保健康检查并发返回结果后仍能正确识别首选实例。 * * @author zdxt */ @@ -20,13 +28,23 @@ import org.springframework.core.env.Environment; public class LoadBalancerConfig { @Bean - public ReactorLoadBalancer randomLoadBalancer( + public ReactorLoadBalancer preferredFirstLoadBalancer( Environment environment, - LoadBalancerClientFactory loadBalancerClientFactory) { + LoadBalancerClientFactory loadBalancerClientFactory, + SimpleDiscoveryProperties discoveryProperties) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); - return new RandomLoadBalancer( + + // 从配置中获取该服务的原始实例顺序(即优先级) + List preferredOrder = discoveryProperties.getInstances() + .getOrDefault(name, Collections.emptyList()) + .stream() + .map(instance -> instance.getHost() + ":" + instance.getPort()) + .collect(Collectors.toList()); + + return new PreferredFirstLoadBalancer( loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), - name + name, + preferredOrder ); } } diff --git a/zdxt-web-server/zdxt-gateway-web-server/src/main/java/com/zdxt/gateway/webserver/config/PreferredFirstLoadBalancer.java b/zdxt-web-server/zdxt-gateway-web-server/src/main/java/com/zdxt/gateway/webserver/config/PreferredFirstLoadBalancer.java new file mode 100644 index 00000000..8d534cc2 --- /dev/null +++ b/zdxt-web-server/zdxt-gateway-web-server/src/main/java/com/zdxt/gateway/webserver/config/PreferredFirstLoadBalancer.java @@ -0,0 +1,79 @@ +package com.zdxt.gateway.webserver.config; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.loadbalancer.DefaultResponse; +import org.springframework.cloud.client.loadbalancer.EmptyResponse; +import org.springframework.cloud.client.loadbalancer.Request; +import org.springframework.cloud.client.loadbalancer.Response; +import org.springframework.cloud.loadbalancer.core.NoopServiceInstanceListSupplier; +import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer; +import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; +import reactor.core.publisher.Mono; + +import java.util.List; + +/** + * 优先级负载均衡器 - 首选优先策略 + *

+ * 根据配置文件中实例的声明顺序确定优先级, + * 始终选择优先级最高的健康实例。 + *

+ * 健康检查并发执行后返回结果顺序不确定, + * 因此通过 preferredOrder 显式匹配配置中的优先级顺序。 + * + * @author zdxt + */ +@Slf4j +public class PreferredFirstLoadBalancer implements ReactorServiceInstanceLoadBalancer { + + private final String serviceId; + private final ObjectProvider serviceInstanceListSupplierProvider; + /** 配置中声明的实例顺序(host:port),索引越小优先级越高 */ + private final List preferredOrder; + + public PreferredFirstLoadBalancer( + ObjectProvider serviceInstanceListSupplierProvider, + String serviceId, + List preferredOrder) { + this.serviceId = serviceId; + this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider; + this.preferredOrder = preferredOrder; + log.info("[{}] 优先级顺序: {}", serviceId, preferredOrder); + } + + @Override + public Mono> choose(Request request) { + ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider + .getIfAvailable(NoopServiceInstanceListSupplier::new); + return supplier.get(request) + .next() + .map(this::selectInstance); + } + + private Response selectInstance(List instances) { + if (instances.isEmpty()) { + log.warn("[{}] 无可用服务实例", serviceId); + return new EmptyResponse(); + } + + // 按配置优先级顺序遍历,找到第一个在健康列表中的实例 + for (String preferred : preferredOrder) { + for (ServiceInstance instance : instances) { + String key = instance.getHost() + ":" + instance.getPort(); + if (preferred.equals(key)) { + log.debug("[{}] 选择优先实例: {}, 健康实例数: {}", + serviceId, key, instances.size()); + return new DefaultResponse(instance); + } + } + } + + // 如果配置顺序中的实例都不在健康列表中(不应发生),取健康列表第一个 + ServiceInstance fallback = instances.get(0); + log.warn("[{}] 未匹配到配置优先级实例,回退使用: {}:{}", + serviceId, fallback.getHost(), fallback.getPort()); + return new DefaultResponse(fallback); + } +} diff --git a/zdxt-web-server/zdxt-gateway-web-server/src/main/resources/application-dev.yml b/zdxt-web-server/zdxt-gateway-web-server/src/main/resources/application-dev.yml index e680baf2..944ff93b 100644 --- a/zdxt-web-server/zdxt-gateway-web-server/src/main/resources/application-dev.yml +++ b/zdxt-web-server/zdxt-gateway-web-server/src/main/resources/application-dev.yml @@ -43,6 +43,13 @@ spring: filters: - StripPrefix=1 - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE + - id: job-service + uri: http://localhost:18003 + predicates: + - Path=/job-ep/** + filters: + - StripPrefix=1 + - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE # Actuator监控端点配置 management: diff --git a/zdxt-web-server/zdxt-gateway-web-server/src/main/resources/application-prod.yml b/zdxt-web-server/zdxt-gateway-web-server/src/main/resources/application-prod.yml index e6cb4d27..79cbbb4d 100644 --- a/zdxt-web-server/zdxt-gateway-web-server/src/main/resources/application-prod.yml +++ b/zdxt-web-server/zdxt-gateway-web-server/src/main/resources/application-prod.yml @@ -5,14 +5,22 @@ spring: application: name: zdxt-gateway cloud: - # 静态服务实例列表(无需注册中心) + # 静态服务实例列表(无需注册中心,配置顺序即优先级) discovery: client: simple: instances: - backend-service: + ent-ep-service: - uri: http://10.76.108.7:18000 + - uri: http://10.76.108.8:18002 + - uri: http://10.76.108.9:18001 + enf-ep-service: + - uri: http://10.76.108.8:18002 + - uri: http://10.76.108.7:18000 + - uri: http://10.76.108.9:18001 + sup-ep-service: - uri: http://10.76.108.9:18001 + - uri: http://10.76.108.7:18000 - uri: http://10.76.108.8:18002 # LoadBalancer 健康检查配置 loadbalancer: @@ -37,29 +45,36 @@ spring: allowedHeaders: "*" allowCredentials: true maxAge: 3600 - # 路由配置 - 使用 lb:// 协议启用负载均衡 + # 路由配置 - 使用 lb:// 协议启用优先级负载均衡 routes: - id: enterprise-service - uri: lb://backend-service + uri: lb://ent-ep-service predicates: - Path=/ent-ep/** filters: - StripPrefix=1 - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE - id: supervision-service - uri: lb://backend-service + uri: lb://sup-ep-service predicates: - Path=/sup-ep/** filters: - StripPrefix=1 - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE - id: enforcement-service - uri: lb://backend-service + uri: lb://enf-ep-service predicates: - Path=/enf-ep/** filters: - StripPrefix=1 - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE + - id: job-service + uri: http://10.76.108.10:18003 + predicates: + - Path=/job-ep/** + filters: + - StripPrefix=1 + - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE # Actuator监控端点配置 management: diff --git a/zdxt-web-server/zdxt-gateway-web-server/src/main/resources/application.yml b/zdxt-web-server/zdxt-gateway-web-server/src/main/resources/application.yml index da7585f7..cf55ca89 100644 --- a/zdxt-web-server/zdxt-gateway-web-server/src/main/resources/application.yml +++ b/zdxt-web-server/zdxt-gateway-web-server/src/main/resources/application.yml @@ -5,14 +5,22 @@ spring: application: name: zdxt-gateway cloud: - # 静态服务实例列表(无需注册中心) + # 静态服务实例列表(无需注册中心,配置顺序即优先级) discovery: client: simple: instances: - backend-service: + ent-ep-service: - uri: http://localhost:18000 + - uri: http://localhost:18002 + - uri: http://localhost:18001 + enf-ep-service: + - uri: http://localhost:18002 + - uri: http://localhost:18000 + - uri: http://localhost:18001 + sup-ep-service: - uri: http://localhost:18001 + - uri: http://localhost:18000 - uri: http://localhost:18002 # LoadBalancer 健康检查配置 loadbalancer: @@ -37,29 +45,36 @@ spring: allowedHeaders: "*" allowCredentials: true maxAge: 3600 - # 路由配置 - 使用 lb:// 协议启用负载均衡 + # 路由配置 - 使用 lb:// 协议启用优先级负载均衡 routes: - id: enterprise-service - uri: lb://backend-service + uri: lb://ent-ep-service predicates: - Path=/ent-ep/** filters: - StripPrefix=1 - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE - id: supervision-service - uri: lb://backend-service + uri: lb://sup-ep-service predicates: - Path=/sup-ep/** filters: - StripPrefix=1 - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE - id: enforcement-service - uri: lb://backend-service + uri: lb://enf-ep-service predicates: - Path=/enf-ep/** filters: - StripPrefix=1 - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE + - id: job-service + uri: http://localhost:18003 + predicates: + - Path=/job-ep/** + filters: + - StripPrefix=1 + - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_UNIQUE # Actuator监控端点配置 management: