Ribbon
1. 负载均衡(Load Balance)
SpringCloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具
LB(负载均衡)简单的说就是讲用户的请求平摊的分配到多个服务上,从而达到系统HA(高可用).常见的有Nginx,LVS,硬件F5等.其分为:
- 进程内LB:将LB逻辑继承到消费方,消费方从服务注册中心获取那些地址可用,然后自己再从这些地址中选出一个合适的服务器
- 集中式LB:即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件F5,也可以是软件Nginx),由该设施负责把访问请求通过某种策略转发至提供方
在相应中间件,例如dubbo和SpringCloud中均提供了负载均衡.SpringCloud的负载均衡算法可以自定义
2. pom.xml
<!--Ribbon需要跟Eureka整合-->
<dependency>
<groupId>org.springframework.cloud<groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
3. application配置
//追加eureka的服务注册地址
eureka:
client:
register-with-eureka:false
service-url:
defaultZone:http://eureka.com #这里改成eureka服务地址
4. java代码
//客户端启动类添加
@EnableEurekaClient
//如果是Rest风格在RestTemplate上添加@LoadBalanced注解
@Bean
@LoadBalanced //开启负载均衡
public RestTemplate getRestTemplate(){
return new RestTemplate
}
//将调用地址改为服务端在EurekaServer注册的服务名
如果是集群在Eureka注册名要相同
5. IRule
根据特定算法从服务列表中选取一个要访问的服务,默认规则如下
-
RoundRobinRule
轮询 -
RandomRule
随机 -
AvailabilityFilteringRule
会过滤掉多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阀值的服务,然后对剩余服务列表按照轮询策略进行访问 -
WeightedResponseTimeRule
根据平均响应时间计算所有服务的权重,响应时间越快的服务权重越大被选中的概率越高.刚启动时如果统计信息不足,则使用RoundRobinRule策略,等统计信息足够,会切换到此算法 -
RetryRule
先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务 -
BestAvailableRule
回显过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务 -
ZoneAvoidanceRule
默认规则,符合判断server所在区域的性能和server的可用性选择服务器
修改轮询算法
@Bean
public IRule myRule(){
return new RandomRule(); //要更换的算法
}
自定义算法
//启动类添加@RibbonClient
@RibbonClient(name="service",configuration=MySelfRule.class)
//name:为哪一个微服务提供的算法
//configuration:算法类
自定义算法配置细节:
这个自定义配置类不能放在@ComponentScan所扫描的当前包以及子包下(不能与启动类在一块 ),否则我们定义的这个配置类会被所有Ribbon客户端所共享,从而达不到特殊化定制的目的
自定义算法示例:
点击查看源码
//实现一个随机并且不能与上一次重复的负载均衡算法
public class RandomRule extends AbstractLoadBalancerRule {
//上一次的访问服务的下标
int prev = -1;
@edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE")
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) {
return null;
}
//原有的随机逻辑
//int index = chooseRandomInt(serverCount);
//server = upList.get(index);
//自定义的逻辑
int index = chooseRandomInt(serverCount);
//记录第一次使用的服务器
prev = prev == -1 ? index : prev;
//如果与上一次相等跳出循环(第一次直接跳出循环)
if(prev == index){
continue;
} else {
//得到了符合逻辑的服务器
server = upList.get(index);
//重新赋值
prev = index;
}
if (server == null) {
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;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
}