因为 Ribbon 使用的是客户端负载均衡,所以下面的操作都是在占用 80 端口的消费者服务工程下。
默认提供的 Rule
通过之前的使用测试会发现 Ribbon 默认的负载均衡策略是依次轮询访问每个微服务,如果我们需要修改它默认的负载均衡策略,则可以使用 IRule 组件。
IRule:实际上是一个接口,它的实现类需要能够根据特定算法从服务列表中选取一个要访问的服务,默认提供的实现有如下:
IRule
    AbstractLoadBalancerRule (com.netflix.loadbalancer)
        ClientConfigEnabledRoundRobinRule (com.netflix.loadbalancer)
            BestAvailableRule (com.netflix.loadbalancer) // 先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
            PredicateBasedRule (com.netflix.loadbalancer)
                ZoneAvoidanceRule (com.netflix.loadbalancer) // 默认规则,综合判断 Server 所在区域的性能和 server 的可用性选择服务器
                AvailabilityFilteringRule (com.netflix.loadbalancer) // 先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的链接数量超过阈值的服务,然后对剩余的服务按照轮询策略进行选择
        RoundRobinRule (com.netflix.loadbalancer) // 轮询
            WeightedResponseTimeRule (com.netflix.loadbalancer) // 根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大,被选择的几率越高。刚启动的时候如果统计信息不足,则使用 RoundRobinRule,等统计信息完善时会自动切换到当前策略
            ResponseTimeWeightedRule (com.netflix.loadbalancer)
        RandomRule (com.netflix.loadbalancer) // 随机
        RetryRule (com.netflix.loadbalancer) // 先按照 RoundRobinRule 策略获取服务,如果获取服务失败则在指定时间内会重试,如果依旧失败则会获取其它可用的服务
如果要修改负载均衡策略,只需要将实现 IRule 接口的 bean 注册到 IoC 容器即可,如:
// zze.springcloud.cfgbeans.ConfigBean
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ConfigBean {
    @Bean
    @LoadBalanced // 客户端负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    @Bean // 负载均衡策略使用随机访问
    public IRule randomRule(){
        return new RandomRule();
    }
}
自定义 Rule
上面说过我们只需要将实现 IRule 接口的 bean 注册到 IoC 容器规则就可以生效,所以我们可以自定义一个 IRule 的实现类,比如要定制一个每个服务依次访问 5 次的规则:
// zze.springcloud.cfgbeans.MyRandomRule
import java.util.List;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
/**
 * 参考 RandomRule 源码自定义 Rule,每个服务访问 5 次
 */
public class MyRandomRule extends AbstractLoadBalancerRule
{
    private int total = 0;             // 总共被调用的次数,目前要求每台被调用5次
    private int currentIndex = 0;    // 当前提供服务的机器号
    public Server choose(ILoadBalancer lb, Object key)
    {
        if (lb == null) {
            return null;
        }
        Server server = null;
        while (server == null) {
            if (Thread.interrupted()) {
                return null;
            }
            List<Server> upList = lb.getReachableServers();
            List<Server> allList = lb.getAllServers();
            int serverCount = allList.size();
            if (serverCount == 0) {
                /*
                 * No servers. End regardless of pass, because subsequent passes only get more
                 * restrictive.
                 */
                return null;
            }
//            int index = rand.nextInt(serverCount);// java.util.Random().nextInt(3);
//            server = upList.get(index);
            
//            private int total = 0;             // 总共被调用的次数,目前要求每台被调用5次
//            private int currentIndex = 0;    // 当前提供服务的机器号
            if(total < 5)
            {
                server = upList.get(currentIndex);
                total++;
            }else {
                total = 0;
                currentIndex++;
                if(currentIndex >= upList.size())
                {
                  currentIndex = 0;
                }
            }            
            
            
            if (server == null) {
                /*
                 * The only time this should happen is if the server list were somehow trimmed.
                 * This is a transient condition. Retry after yielding.
                 */
                Thread.yield();
                continue;
            }
            if (server.isAlive()) {
                return (server);
            }
            // Shouldn't actually happen.. but must be transient or a bug.
            server = null;
            Thread.yield();
        }
        return server;
    }
    @Override
    public Server choose(Object key)
    {
        return choose(getLoadBalancer(), key);
    }
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig)
    {
        // TODO Auto-generated method stub
    }
}
除了将 IRule 实例直接注册到 IoC 容器这种方式,我们还可以自定义一个规则配置类:
// zze.config.MySelfRuleCofnig
import org.springframework.context.annotation.Bean;
import com.netflix.loadbalancer.IRule;
import org.springframework.context.annotation.Configuration;
import zze.springcloud.cfgbeans.MyRandomRule;
/*
注意,官方文档表示该类要定义在 @ComponentScan 注解扫描范围之外
 */
@Configuration
public class MySelfRuleCofnig
{
    @Bean
    public IRule myRule()
    {
        return new MyRandomRule();
    }
}
接下来可以通过在主启动类上通过指定注解让配置类对指定服务生效:
// zze.springcloud.Application_80
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import zze.config.MySelfRuleCofnig;
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "MICROSERVICECLOUD-PROVIDER-DEPT",configuration = MySelfRuleCofnig.class) // 针对 MICROSERVICECLOUD-PROVIDER-DEPT 服务实例采用的负载均衡策略配置类为 MySelfRuleCofnig
public class Application_80 {
    public static void main(String[] args) {
        SpringApplication.run(Application_80.class, args);
    }
}
      
      
    
      
      
      
评论区