weixin_41374507
不努力的猿
采纳率50%
2020-01-08 14:31 阅读 2.0k
已采纳

spring cloud gateway 自定义负载均衡getLoadBalancer()获取的服务总是同一个服务

40

根据业务需求,我需要自定义负载均衡规则,获取相应的服务,然后返回;

 代码如下:
     继承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服务的信息;

附图:

图片说明

图片说明

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

5条回答 默认 最新

  • 已采纳
    it_zhangwei 路漫漫兮其修远兮 2020-01-09 09:29

    不要用

    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);
        }
    }
    
    
    点赞 评论 复制链接分享
  • qq107581 qq107581 2020-02-27 17:34

    这个问题我也遇到过,不知道你后面是怎么解决的,我当时是继承例RandomRule,重写例choose,之后,我将这个类设置成例多例的(即多加一个@Scope(value="prototype")),就正常了。你可以试试

    点赞 5 评论 复制链接分享
  • c362734454 c362734454 2021-01-25 17:06

    qq107581 qq107581 11月前

    这个问题我也遇到过,不知道你后面是怎么解决的,我当时是继承例RandomRule,重写例choose,之后,我将这个类设置成例多例的(即多加一个@Scope(value="prototype")),就正常了。你可以试试
    这个回答就很棒!!!

    点赞 1 评论 复制链接分享
  • dabocaiqq dabocaiqq 2020-01-08 14:45
  • jiang_2992 不怕孤单的123 2020-08-20 17:38

    楼主,我也遇到了同样问题,折腾了好几天了,你最后是以什么方式解决的?

    点赞 1 评论 复制链接分享

相关推荐