上篇介绍了Ribbon的几种负载均衡算法,在实际工作中有可能会用自己定义的算法,根据自己的编写的算法来进行负载均衡
Ribbon是客户端的负载均衡技术,即消费者微服务端的负载均衡
启动项目查看Eureka Server:
一个微服务有3个不同的实例
先来针对一个服务使用不同的负载均衡策略。关键注解:@RibbonClient,此注解是放在启动类上
上面截图@RibbonClient的意思是,针对YPPCLOUD-DEPT这个微服务使用MyRibbon我们自己定义的负载均衡策略以及算法
注意一点:自定义负载均衡的策略以及算法不能放在@ComponentScan所扫描的包下,不然会被所有Ribbon所共享(即所有的微服务都会使用该策略,包含YPPCLOUD-DEPT)
复习Spring Boot知识:都知道启动类自动扫描启动类平级以及子包里面所有的配置信息以及bean。关键点在于@SpringBootApplication这个注解,点击@SpringBootApplication注解进去看,是用了@ComponentScan。
所以我们要新建一个包,用上我们自定义的负载均衡类:
MyRibbon.java:
package com.ypp.myribbon;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
@Configuration
public class MyRibbon {
@Bean
public IRule rule(){
return new RandomRule();
}
}
Ribbon默认使用的轮训的负载均衡策略,MyRibbon这里我们使用的是随机的负载均衡策略,但是这个随机负载均衡策略是针对YPPCLOUD-DEPT这个微服务下面的实例,其他的微服务还是使用的默认的轮训负载均衡策略,因为我们刚才启动类配置只针对YPPCLOUD-DEPT它配置的MyRibbon。
访问下YPPCLOUD-DEPT,看看下面的实例是否以随机进行访问
ypp-springcloud3代表8003这个实例,数据库也是ypp-springcloud3
ypp-springcloud2代表8002这个实例,数据库也是ypp-springcloud2
ypp-springcloud代表8001这个实例,数据库也是ypp-springcloud
每个实例我都是单独的数据库,以下是访问的结果,显而易见是随机,并没有任何规则或者顺序
自定义负载均衡策略的算法
Ribbon提供了一个规范核心组件IRule,这个在上篇文章有说,就像java的JPA,然后hibernate来进行实现。
我们来对IRule进行自己的策略和算法,源码地址在github有。地址:https://github.com/Netflix/ribbon/tree/master/ribbon-loadbalancer/src/main/java/com/netflix/loadbalancer
就拿默认的轮训来定义我们自己的,比如每个实例访问5次或者10次,或者一个实例访问是5天,然后是其他的实例每个也是5天这样的规则
RoundRobinRule这是默认轮训的源码,我们在它的基础上更改,改源码要比看源码更牛。不是只会调用API,多年工作经验的只会使用以及调用API等,那不是有多年工作经验,而是一个经验用了多年,差距甚大。这也就是同样的工作经验,为什么别人那么牛,为什么能当架构师,而自己只能一直成为码农的区别。作为程序员,至少都要有一个目标,成为码神,才能走得更远
RoundRobinRule继承了AbstractLoadBalancerRule,AbstractLoadBalancerRule 实现了IRule,源码自己去看,不多说
我们自定义一个MyRoundRobinRule类,也来继承AbstractLoadBalancerRule,然后把RoundRobinRule里面的源码直接拷贝过来自己更改
更改后的MyRoundRobinRule.java:
package com.ypp.myribbon;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.RoundRobinRule;
import com.netflix.loadbalancer.Server;
@Configuration
public class MyRoundRobinRule extends AbstractLoadBalancerRule {
private AtomicInteger nextServerCyclicCounter;
private static final boolean AVAILABLE_ONLY_SERVERS = true;
private static final boolean ALL_SERVERS = false;
// 总共被调用的次数,目前要求每台被调用5次
private int total = 0;
// 当前提供服务的机器号
private int currentIndex = 0;
private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);
public MyRoundRobinRule() {
nextServerCyclicCounter = new AtomicInteger(0);
}
public MyRoundRobinRule(ILoadBalancer lb) {
this();
setLoadBalancer(lb);
}
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
}
Server server = null;
int count = 0;
while (server == null && count++ < 10) {
List<Server> upList = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = upList.size();
int serverCount = allServers.size();
if ((upCount == 0) || (serverCount == 0)) {
log.warn("No up servers available from load balancer: " + lb);
return null;
}
if (total < 5) {
server = upList.get(currentIndex);
total++;
} else {
total = 0;
currentIndex++;
if (currentIndex >= upList.size()) {
currentIndex = 0;
}
}
if (server == null) {
/* Transient. */
Thread.yield();
continue;
}
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
// Next.
server = null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: " + lb);
}
return server;
}
/**
* Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}
* .
*
* @param modulo
* The modulo to bound the value of the counter.
* @return The next value.
*/
private int incrementAndGetModulo(int modulo) {
for (;;) {
int current = nextServerCyclicCounter.get();
int next = (current + 1) % modulo;
if (nextServerCyclicCounter.compareAndSet(current, next))
return next;
}
}
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
@Override
public void initWithNiwsConfig(IClientConfig arg0) {
}
}
以上代码我定义的每个实例访问5次的轮训,核心代码:
if (total < 5) {
server = upList.get(currentIndex);
total++;
} else {
total = 0;
currentIndex++;
if (currentIndex >= upList.size()) {
currentIndex = 0;
}
}
然后修改我们自己的Ribbon配置类为:
再次请求访问,看看是不是每个实例访问5次的轮训
成功
源码地址:https://pan.baidu.com/s/1K0vykAELrZ8S_JlYt4LotA 提取码:w9hk