6 changed files with 159 additions and 20 deletions
@ -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; |
|||
|
|||
/** |
|||
* 优先级负载均衡器 - 首选优先策略 |
|||
* <p> |
|||
* 根据配置文件中实例的声明顺序确定优先级, |
|||
* 始终选择优先级最高的健康实例。 |
|||
* <p> |
|||
* 健康检查并发执行后返回结果顺序不确定, |
|||
* 因此通过 preferredOrder 显式匹配配置中的优先级顺序。 |
|||
* |
|||
* @author zdxt |
|||
*/ |
|||
@Slf4j |
|||
public class PreferredFirstLoadBalancer implements ReactorServiceInstanceLoadBalancer { |
|||
|
|||
private final String serviceId; |
|||
private final ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider; |
|||
/** 配置中声明的实例顺序(host:port),索引越小优先级越高 */ |
|||
private final List<String> preferredOrder; |
|||
|
|||
public PreferredFirstLoadBalancer( |
|||
ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, |
|||
String serviceId, |
|||
List<String> preferredOrder) { |
|||
this.serviceId = serviceId; |
|||
this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider; |
|||
this.preferredOrder = preferredOrder; |
|||
log.info("[{}] 优先级顺序: {}", serviceId, preferredOrder); |
|||
} |
|||
|
|||
@Override |
|||
public Mono<Response<ServiceInstance>> choose(Request request) { |
|||
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider |
|||
.getIfAvailable(NoopServiceInstanceListSupplier::new); |
|||
return supplier.get(request) |
|||
.next() |
|||
.map(this::selectInstance); |
|||
} |
|||
|
|||
private Response<ServiceInstance> selectInstance(List<ServiceInstance> 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); |
|||
} |
|||
} |
|||
Loading…
Reference in new issue