Ribbon简述
是什么
spring cloud ribbon 是一套客户端的负载均衡工具
主要用来:客户端的软件负载均衡算法和服务的调用
做什么
负载均衡+RestTemplate调用
LB负载均衡:将用户请求平摊到多个服务器上,从而达到系统的HA(高可用)
ribbon是本地负载均衡:在调用微服务接口时,会在注册中心中获取注册信息服务列表之后缓存到jvm本地,从而在本地实现rpc的远程调用;
nginx是服务器负载均衡:客户端所用请求交给nginx,然后由nginx实现转发请求.即负载均衡是由服务端实现的。
Ribbon核心组件IRule
负载均衡方式
IRule接口实现类关系图:
RoundRobinRule:轮询
RandomRule:随机
RetryRule:先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务
WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越多大,越容易被选择
BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例
ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器
切换负载均衡方式
已知,当我们搭建好的Eureka微服务(参考Eureka
搭建微服务架构),默认的负载均衡方式是轮询,本案例要做的是将默认的轮询方式切换为随机方式。
pom
由于eureka自带了 Ribbon的依赖,故我们不需要额外的引入。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
如何替换算法,把轮询改为随机
官网明确警告:这个自定义配置类不能放在@componentScan所扫描的当前包以及子包下。否则我们的自定义配置类会被所有的Ribbon客户端所共享,达不到特殊化制定的目的。(负载均衡方式,一般配置在消费者端,因此我们只需要对消费者端的代码做一下修改即可)
已知,@SpringBootApplication已经将@componentScan的注解引入,故此,我们不能将我创建的配置类被该注解扫描到
从我的代码结构,可以看到,我的配置类MySelfRule创建的位置与主启动类不在同步一包下的,这就避免了MySelfRule被扫描进去。
MySelfRule:
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
// 定义为随机
return new RoundRobinRule();
}
}
主启动类:
@SpringBootApplication
@EnableEurekaClient
//CLOUD-PAYMENT-SERVICE是服务注册中心的服务名
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OraderMain80 {
public static void main(String[] args) {
SpringApplication.run(OraderMain80.class,args);
}
}
在主启动类添加@RibbonClient。
自定义负载均衡
在平时的工作中,往往现有的负载均衡策略无法满足需求,因此我们有必要了解如何自定义负载均衡。
要写一个自定义的负载均衡,我们需要先了解负载均衡的原理。下面将通过刨析RoundRobinRule轮询方式来深入了解。
RoundRobinRule的工作原理
由上述的配置类可知,我们若想切换一个新的负载均衡策略,我们只需要改变配置类注册bean的类型即可。如下:
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
// 定义为随机
return new RandomRule();
}
}
因此,我们可以从这里入手,要想自定义一个负载均衡策略,只需要自行创建一个与RandomRule同性质的类并注入到容器中供即可。
什么是同性质?
我们参考上述截图,可见AbstractLoadBalancerRule抽象类之下有几个实现类,分别对应了上述类图关系的其中已有的负载均衡策略,而且他们都实现了相关的三个方法。从这里可以发现,我们若要自定义一个负载均衡,完完全全可以参考该类的实现,只需要将核心的实现方法替换一下就可以了,这里就是我们可以从源码看到的信息。
(友情提示:有时候看源码,不能单纯的看,还要懂得会套用)
我们继续看RandomRule实现:
public class RandomRule extends AbstractLoadBalancerRule {
/**
* Randomly choose from all living servers
*/
@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) {
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
}
int index = chooseRandomInt(serverCount);
server = upList.get(index);
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;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
// TODO Auto-generated method stub
}
}
上面的源码很简单,也就是随机获取一个服务int index = chooseRandomInt(serverCount);,getLoadBalancer()方法是父类的实现,因此我们只需要看choose(ILoadBalancer lb, Object key)方法的实现即可。(我们也可以想到,我们自定义的负载均衡类,也只重点实现该方法即可)
至此,没什么好说的,看到这我们已经知道要实现一个自定义的负载均衡策略,已经很明了了。好人做到底,随便贴一下轮询方式的算法原理:
自定义负载均衡实现
如上述截图,具体只需要参考实现抽象类的核心方法就可以了。