优点:可以保证自己发送的请求(经过网关的请求或者通过fegin调用的请求)都能达到自己机器的服务
缺点:如果服务过多,电脑有可能会承受不住
优点:只需要启动自己开发的服务,方便快捷,节省电脑资源 缺点:由于自己开发的服务不一定只有自己启动了,有可能有别人同时启动了该服务,所以当从前端请求或者通过fegin请求时,可能请求会发送到了别人机器启动的服务上
图1
说明:
图1 通过网关请求后台服务A ,由于两个开发人员都启动了服务A并且注册到了公用注册中心上,使用默认负载均衡策略得话,此时请求到的不一定是小李的还是小张的电脑的服务A
图2
图二 通过fegin请求后台服务A ,由于两个开发人员都启动了服务A并且注册到了公用注册中心上,使用默认负载均衡策略得话,所以此时请求到的不一定是小李的还是小张的电脑的服务A
代码,添加如下这几个类
CustomRibbonRule.java
public class CustomRibbonRule extends RoundRobinRule {
private String version;
public void setVersion(String version) {
this.version = version;
}
@Override
public Server choose(ILoadBalancer lb, Object key) {
List targetList = null;
List upList = lb.getReachableServers();
if (StrUtil.isEmpty(version)) {
version = ReqeustHeaderContextHolder.getVersion();
}
if (StrUtil.isNotEmpty(version)) {
//取指定版本号的实例
targetList = upList.stream().filter(
server -> version.equals(
((NacosServer) server).getMetadata().get("version")
)
).collect(Collectors.toList());
}
if (CollUtil.isEmpty(targetList)) {
//只取无版本号的实例
targetList = upList.stream().filter(
server -> {
String metadataVersion = ((NacosServer) server).getMetadata().get("version");
return StrUtil.isEmpty(metadataVersion);
}
).collect(Collectors.toList());
}
if (CollUtil.isNotEmpty(targetList)) {
return getServer(targetList);
}
return super.choose(lb, key);
}
private Server getServer(List upList) {
int nextInt = RandomUtil.randomInt(upList.size());
return upList.get(nextInt);
}
}
RibbonConfiguration.java
@Configuration
public class RibbonConfiguration {
@Value("${spring.cloud.nacos.discovery.metadata.version:#{null}}")
private String version;
@Bean
public IRule defaultLBStrategy() {
CustomRibbonRule customIsolationRule = new CustomRibbonRule();
customIsolationRule.setVersion(version);
return customIsolationRule;
}
}
ReqeustHeaderContextHolder.java
public class ReqeustHeaderContextHolder {
private static final FastThreadLocal fastThreadLocal = new FastThreadLocal();
public static void putVersion(String version){
fastThreadLocal.set(version);
}
public static String getVersion(){
return Objects.isNull(fastThreadLocal.get())?null:(String) fastThreadLocal.get();
}
public static void clear(){
fastThreadLocal.remove();
}
}
OrderedGatewayFilter.java
public class OrderedGatewayFilter implements GatewayFilter, Ordered {
private final GatewayFilter delegate;
private final int order;
public OrderedGatewayFilter(GatewayFilter delegate, int order) {
this.delegate = delegate;
this.order = order;
}
public GatewayFilter getDelegate() {
return delegate;
}
@Override
public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
try{
if(CollectionUtil.isNotEmpty(exchange.getRequest().getHeaders().get("version"))){
String version = exchange.getRequest().getHeaders().get("version").get(0);
ReqeustHeaderContextHolder.putVersion(version);
}
return this.delegate.filter(exchange, chain);
}finally {
ReqeustHeaderContextHolder.clear();
}
}
@Override
public int getOrder() {
return this.order;
}
@Override
public String toString() {
return new StringBuilder("[").append(delegate).append(", order = ").append(order)
.append("]").toString();
}
}
fegin端重写路由解决方案解决图2问题
服务fegin的负载均衡路由,注册时将自己机器的服务A加上特定key属性注册到注册中心内,获取时优先获取有指定key的实例,没有找到再随机获取一个实例
代码:
各个应用部分可以添加如下类或者抽取出共通类
CustomRibbonRule.java
public class CustomRibbonRule extends RoundRobinRule {
private String version;
public void setVersion(String version) {
this.version = version;
}
@Override
public Server choose(ILoadBalancer lb, Object key) {
List targetList = null;
List upList = lb.getReachableServers();
if (StrUtil.isNotEmpty(version)) {
//取指定版本号的实例
targetList = upList.stream().filter(
server -> version.equals(
((NacosServer) server).getMetadata().get("version")
)
).collect(Collectors.toList());
}
if (CollUtil.isEmpty(targetList)) {
//只取无版本号的实例
targetList = upList.stream().filter(
server -> {
String metadataVersion = ((NacosServer) server).getMetadata().get("version");
return StrUtil.isEmpty(metadataVersion);
}
).collect(Collectors.toList());
}
if (CollUtil.isNotEmpty(targetList)) {
return getServer(targetList);
}
return super.choose(lb, key);
}
private Server getServer(List upList) {
int nextInt = RandomUtil.randomInt(upList.size());
return upList.get(nextInt);
}
}
RibbonConfiguration.java
@Configuration
public class RibbonConfiguration {
@Value("${spring.cloud.nacos.discovery.metadata.version:#{null}}")
private String version;
@Bean
public IRule defaultLBStrategy() {
CustomRibbonRule customIsolationRule = new CustomRibbonRule();
customIsolationRule.setVersion(version);
return customIsolationRule;
}
}
然后对应yml里面添加自己对应的version
首先在需要走自己机器的服务上加上对应version,都启动后如下图
我们通过postman 模拟前端请求网关访问看看是不是走的我们指定的xiaoli 的实例
可以看到我们网关自定义的负载路由去到了version=xiaoli的实例
再来看看fegin调用的时候是不是也是走的我们指定version的实例
可以看order调用商品服务的时候也是找了商品服务version为xiaoli的实例,整明两个问题都解决了