SpringCloudRibbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于NetflixRibbon实现。通过SpringCloud封装,可以轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用,作为一个工具类,Ribbon不需要独立部署。
根据负载均衡示意图开始创建Ribbon负载均衡项目。
复制eureka-provider-8081,重命名为ribbon-provider-8201并配置成maven项目。
在application.yml文件中修改端口号。
server: port: 820112复制代码类型:[java]
为了方便运行时识别对应的provider,在PetsServiceImpl.java中改写查询部分——name+端口号。
依照上面的过程配置ribbon-provider-8202,ribbon-provider-8203。
启动项目,在postman中查看查询结果。
在这里,默认的顺序是1、3、2。从这个结果可以看出负载均衡默认是轮询的。这种轮询的状态是可以修改的。
Ribbon的内置负载均衡策略一共有7种。
RoundRobinRule(默认)
轮询策略,默认采用的策略。经过一轮轮询没有找到可用的provider,最多重复10轮,如果还没有找到则返回null。
RandomRule
随机策略,从多个备选provider种,随机选择一个。
RetryRule
重试策略,先根据默认策略进行轮询,获取失败后,在一个默认时间后进行重试。默认时长为500ms。
AvailabilityFilteringRule
可用过滤算法,过滤掉那些不可用的provider(熔断、连接极限、连接失败),对剩余的provider进行轮询。
ZoneAvoidanceRule
zone回避策略,使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个provider,根据provider所在zone及provider的可用性,对provider进行选择。
WeightedResponseTimeRule
权重响应时间策略,根据响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性越低。
通过修改配置文件或JavaConfig类达到更换内置负载均衡策略的目的。
复制feign-consumer-8080,重命名为ribbon-consumer-8080并配置为maven文件。
修改配置文件
# 修改负载均衡策略 # 提供者的微服务名称 familycloud-provider-depart: # 指定要使用的负载均衡策略 ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule123456复制代码类型:[java]
启动项目,在postman种进行测试,得到的结果是随机的。
改写PetsConfigure.java
@Configuration public class PetsConfigure { // 指定Ribbon使用随机算法策略 @Bean public IRule RibbonBalanceRule(){ return new RandomRule(); } }12345678复制代码类型:[java]
启动项目,在postman种进行测试,得到的结果跟更换内置负载均衡策略结果都为随机。
Ribbon可以自定义负载均衡策略,负载均衡算法类需要实现IRule接口。
要实现的是从所有可用的provider中排除掉指定端口号的provider,剩
余provider进行随机选择。
定义CRule类
在获取排除了指定端口的所有剩余Servers的部分可以使用普通代码方式实现也可以使用Lambda方式实现。
普通代码方式实现
package com.javafamily.balance; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.Server; import java.util.ArrayList; import java.util.List; import java.util.Random; /** * 从所有可用的provider中排除掉指定端口号的provider,剩余provider进行随机选择。 */ public class CRule implements IRule { private ILoadBalancer lb; // 记录所有要排除的端口号 private List<Integer> excludePorts; public CRule() { } public CRule(List<Integer> excludePorts) { this.excludePorts = excludePorts; } @Override public Server choose(Object key) { // 获取所有UP状态的server List<Server> servers = lb.getReachableServers(); // 获取到排除了指定端口的所有剩余Servers List<Server> availableServers = getAvailableServers(servers); // 对剩余的Servers通过随机方式获取一个Server return getAvailableRandomServer(availableServers); } // 获取到排除了指定端口的所有剩余Servers // 使用普通代码方式实现 private List<Server> getAvailableServers(List<Server> servers) { // 若没有指定要排除的port,则直接返回所有Server if (excludePorts == null || excludePorts.size() == 0) { return servers; } // 用于存放真正可用的Server List<Server> aservers = new ArrayList<>(); for (Server server : servers) { boolean isExclude = false; // 将当前遍历Server的端口号与要排除的端口号进行对比 for (Integer port : excludePorts) { if (server.getPort() == port) { isExclude = true; break; } } if (!isExclude) { aservers.add(server); } } return aservers; } // 对剩余的Servers通过随机方式获取一个Server private Server getAvailableRandomServer(List<Server> servers) { // 获取一个[0,servers.size())的随机整数 int index = new Random().nextInt(servers.size()); return servers.get(index); } @Override public void setLoadBalancer(ILoadBalancer lb) { this.lb = lb; } @Override public ILoadBalancer getLoadBalancer() { return lb; } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081复制代码类型:[java]
package com.javafamily.balance; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.Server; import java.util.List; import java.util.Random; import java.util.stream.Collectors; /** * 从所有可用的provider中排除掉指定端口号的provider,剩余provider进行随机选择。 */ public class CRule implements IRule { private ILoadBalancer lb; // 记录所有要排除的端口号 private List<Integer> excludePorts; public CRule() { } public CRule(List<Integer> excludePorts) { this.excludePorts = excludePorts; } @Override public Server choose(Object key) { // 获取所有UP状态的server List<Server> servers = lb.getReachableServers(); // 获取到排除了指定端口的所有剩余Servers List<Server> availableServers = getAvailableServers(servers); // 对剩余的Servers通过随机方式获取一个Server return getAvailableRandomServer(availableServers); } // 获取到排除了指定端口的所有剩余Servers // 使用Lambda方式实现 private List<Server> getAvailableServers(List<Server> servers) { // 若没有指定要排除的port,则直接返回所有Server if (excludePorts == null || excludePorts.size() == 0) { return servers; } // 用于存放真正可用的Server List<Server> aservers = servers.stream() // 将list变为stream // filter():只要是能使filter()参数结果为true的元素就能通过过滤 // noneMatch():用于判断stream中的元素是否全部都不符合。只要找到一个符合的元素该方法就返回false .filter(server -> excludePorts.stream().noneMatch(port -> server.getPort() == port)) // 将最终的stream变为list .collect(Collectors.toList()); return aservers; } // 对剩余的Servers通过随机方式获取一个Server private Server getAvailableRandomServer(List<Server> servers) { // 获取一个[0,servers.size())的随机整数 int index = new Random().nextInt(servers.size()); return servers.get(index); } @Override public void setLoadBalancer(ILoadBalancer lb) { this.lb = lb; } @Override public ILoadBalancer getLoadBalancer() { return lb; } }12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273复制代码类型:[java]
// 修改负载均衡策略为:自定义策略 @Bean public IRule loadBalanceRule() { List<Integer> excludePorts = new ArrayList<>(); // 只对8201、8202进行随机 excludePorts.add(8203); return new CRule(excludePorts); }12345678复制代码类型:[java]
重新启动项目,在postman中进行测试,得到只有8201、8202没有8203进行随机的结果。
gitee
https://gitee.com/javainfamily/spring-cloud
联系客服