根据业务需求,我需要自定义负载均衡规则,获取相应的服务,然后返回;
代码如下:
继承LoadBalancerClientFilter 重写choose方法;
protected ServiceInstance choose(ServerWebExchange exchange) {
//获取用户票据信息
String ticket= AuthTicketUtils.getBxAuthTicket(exchange.getRequest());
logger.info("=====获取用户登录票据信息:{}============",ticket);
if(!StringUtils.isEmpty(ticket)){
//根据ticket获取用户信息
if (this.loadBalancer instanceof RibbonLoadBalancerClient) {
RibbonLoadBalancerClient client = (RibbonLoadBalancerClient) this.loadBalancer;
String userId=getUserNo(ticket);
logger.info("========当前操作用户信息为:{}=============",userId);
if(null == userId){
return super.choose(exchange);
}
//获取服务名称
String serviceId = ((URI) exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR)).getHost();
logger.info("===========当前选择服务名称为:{}===========",serviceId);
//这里使用服务ID 和 用户ID 做为选择服务实例的key
GrayscaleProperties grayscaleProperties=new GrayscaleProperties();
grayscaleProperties.setServerId(serviceId);
grayscaleProperties.setUserId(userId);
return client.choose(serviceId,grayscaleProperties);
}
}
return super.choose(exchange);
}
当调用client.choose(String name,Object hint)方法时,将会调用我的规则,我的自定义规则代码如下:首先继承了AbstractLoadBalancerRule类,重写choose方法
public Server choose(Object o) {
logger.info("=======服务选择:{}=======",o);
//获取当前请求的所有服务
logger.info("============LoadBalancer:{}==============",this.getLoadBalancer().toString());
List<Server> servers = this.getLoadBalancer().getReachableServers();
//TODO 逻辑代码省略
return null;
}
当调用this.getLoadBalancer()时,返回的总是上一次正确的服务,举例来说我有A、B、C三个服务,当第一次请求A服务的时候正常返回,第二次B服务请求的时候,this.getLoadBalancer()返回的对象信息是A服务的信息;
附图:
不要用
this.getLoadBalancer().getReachableServers()
这个,
你可以注入一个
DiscoveryClient discoveryClient;
List<String> ids=discoveryClient.getServices()
获得到所有的 service ID
List<ServiceInstance> service = discoveryClient.getInstances(String serviceId)
就是获取到里面的服务,同名服务可能有多个。
我就是这样来做服务选择的
我的灰度服务代码
package com.learn.gateway.client;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.gateway.config.LoadBalancerProperties;
import org.springframework.cloud.gateway.filter.LoadBalancerClientFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import java.net.URI;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author zhang
* @projectName spring-cloud-parent
* @title GrayLoadBalancerClientFilter
* @package com.learn.gateway.client
* @description 灰度负载均衡
* @date 2019/11/18 7:08 下午
*/
@Slf4j
@Component
public class GrayLoadBalancerClientFilter extends LoadBalancerClientFilter {
private final DiscoveryClient discoveryClient;
private static final String VERSION_KEY = "myself.eureka.version";
public GrayLoadBalancerClientFilter(LoadBalancerClient loadBalancer, LoadBalancerProperties properties, DiscoveryClient discoveryClient) {
super(loadBalancer, properties);
this.discoveryClient = discoveryClient;
}
/**
* 选择服务
* @param exchange
* @return
*/
@Override
protected ServiceInstance choose(ServerWebExchange exchange) {
log.info("服务分发操作");
// 获得请求头
HttpHeaders headers = exchange.getRequest().getHeaders();
// 获取请求同的token
String token = headers.getFirst("token");
// 获取用户携带的版本号
String version = headers.getFirst("version");
if (StringUtils.isEmpty(version)) {
return super.choose(exchange);
}
log.debug("从请求头中获取token:{},\t从请求头中获取到的 version 版本号:{}",token,version);
// 获取用户获取的项目serviceId
URI uri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
assert uri != null;
String instancesId = uri.getHost();
log.debug("请求的服务的instancesId:{}",instancesId);
// 获取服务的信息
List<ServiceInstance> instances = discoveryClient.getInstances(instancesId);
log.debug("从eureka中获取的服务instances为:{}", instances);
// 通过version查找到服务
if (instances != null && instances.size()>0) {
List<ServiceInstance> versionSer = instances.stream().filter(i -> {
assert version != null;
return version.equals(i.getMetadata().get(VERSION_KEY));
}).collect(Collectors.toList());
// 没有找到对应版本号的服务,
if (versionSer.size() == 0) {
return super.choose(exchange);
}
// 找到对应版本好的服务。。。
return versionSer.get(0);
}
return super.choose(exchange);
}
}
这个问题我也遇到过,不知道你后面是怎么解决的,我当时是继承例RandomRule,重写例choose,之后,我将这个类设置成例多例的(即多加一个@Scope(value="prototype")),就正常了。你可以试试
qq107581 11月前
这个问题我也遇到过,不知道你后面是怎么解决的,我当时是继承例RandomRule,重写例choose,之后,我将这个类设置成例多例的(即多加一个@Scope(value="prototype")),就正常了。你可以试试
这个回答就很棒!!!
https://blog.csdn.net/cyxinda/article/details/98884205
楼主,我也遇到了同样问题,折腾了好几天了,你最后是以什么方式解决的?