负载方案简介:
目前主流的负载方案分为以下两种:
1.集中式负载均衡,在消费者和服务提供方中间使用独立的代理方式进行负载,有硬件的(比如 F5),也有软件的(比如 Nginx)。
2.客户端自己做负载均衡,根据自己的请求情况做负载,Ribbon 就属于客户端自己做负载。
而Spring Cloud Ribbon 虽然只是一个工具类框架,它不像服务注册中心、配置中心、API 网关那样需要独立部署,但是它几乎存在于每一个 Spring Cloud 构建的微服务和基础设施中。因为微服务间的调用,API 网关的请求转发等内容,实际上都是通过 Ribbon 来实现的
接下来就带大家通过几行源码的debug查看单机的Ribbon默认算法是实现的
1.工程中引入谷歌提供的guava工具类和Ribbon组件的依赖,
pom.xml:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon-core</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon-loadbalancer</artifactId>
<version>2.2.2</version>
</dependency>
2.测试类:
public static void main(String[] args) {
// 注册两个server,顺序分别是8081,8082
List serverList = Lists.newArrayList(
new Server("localhost", 8081),
new Server("localhost", 8082)
);
ILoadBalancer baseLoadBalancer = LoadBalancerBuilder.newBuilder().buildFixedServerListLoadBalancer(serverList);
Server localhost = baseLoadBalancer.chooseServer("localhost");
Server localhost2 = baseLoadBalancer.chooseServer("localhost");
Server localhost3 = baseLoadBalancer.chooseServer("localhost");
Server localhost4 = baseLoadBalancer.chooseServer("localhost");
System.out.println(localhost);
System.out.println(localhost2);
System.out.println(localhost3);
System.out.println(localhost4);
}
//代码效果参考:http://www.zidongmutanji.com/zsjx/464857.html
3.BoadBalancerBuilder.newBilder()源码逐行深入
// 这段代码是其核心,这里注册了默认的算法机制和choose的api
LoadBalancerBuilder.newBuilder().buildFixedServerListLoadBalancer(serverList);
接下来介绍我所认知的几个核心关键点
// 用来注册默认的负载均衡算法类,以配置类对象的形式初始化用到的属性
LoadBalancerBuilder.newBuilder()
具体流程:
3.1 执行newBilder()方法,进行创建"LoadBalancerBuilder"对象
在这里插入图片描述
3.2 点进去,在创建“LoadBalancerBuilder”对象时,会初始化其类属性,这里有个关键点,在第16行会执行一个配置默认参数值的属性,并且返回其配置类
在这里插入图片描述
3.3 此时会执行一个"default"默认的注册配置信息
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3.4 进入this.loadDefaultValues()方法
在这里会看到一大坨默认的注册信息,核心参数:"NFLoadBalancerRuleClassName" 请记住这个字段
咱们这里只看有关默认算法的代码,其他的不做扩展
public void loadDefaultValues() {
xxxxxxxxxx
this.putDefaultStringProperty(CommonClientConfigKey.NFLoadBalancerRuleClassName, this.getDefaultNfloadbalancerRuleClassname());
xxxxxxxxxx
}
在这里插入图片描述
3.5 AvailabilityFilteringRule对象
他继承自ClientConfigEnabledRoundRobinRule对象 所以在后期创建的时候会执行其构造方法和类的属性。
在这里插入图片描述
3.6 看到这里我们就知道了,RIbbon的默认算法是轮询的算法机制。
在这里插入图片描述
- buildFixedServerListLoadBalancer(servers)源码逐行深入
ILoadBalancer baseLoadBalancer =
4.1 点进去会看到 rule肯定是null,然后他会拿到刚刚解析的配置类进行从配置类信息中创建Rule的操作LoadBalancerBuilder .newBuilder() .buildFixedServerListLoadBalancer(serverList);
在这里插入图片描述
4.2 这里就看到我们在3.3中提到的”NFLoadBalancerRuleClassName“key拿到默认负载算法的Class对象。
在下面又通过ClientFactory.instantiateInstanceWithClientConfig(ruleClassName, config);方法进行className的实例化,拿到AvailabilityFilteringRule对象的实例,并且内部Rule默认为RoundRobinRule对象做负载均衡算法。
在这里插入图片描述
除了RoundRobinRule以外还有其他的算法
在这里插入图片描述
5.既然默认的算法机制搞清楚了 就可以进行choose server了
5.1 执行多个重载的choose方法
Server server = baseLoadBalancer.chooseServer();
往下直接看606行的choose方法
在这里插入图片描述
继续看choose方法
在这里插入图片描述
在点进去
在这里插入图片描述
5.2 执行增量取模算法
这里核心的方法是44行的"this.incrementAndGetModulo(serverCount);"方法,它通过你传入的server列表个数拿到负载均衡后的索引值。
在这里插入图片描述
算法实现是每次都拿这个AtomicInteger类型值做当前server数组的下标,每次choose都get出当前下标,进行+1再取模 达到雨露均沾的负载均衡效果,并且使用cas方式操作值的变化。
private AtomicInteger nextServerCyclicCounter;
在这里插入图片描述
5.3 算法执行完后进行检查处理
取出server后会进行server的检查判断:
1.如果server是null或者isAlive为false,则continue这次循环,继续下一次轮询
2.如果轮询次数超过10次,则会输入wran警告,并且返回null
log.warn("No available alive servers after 10 tries from load balancer: " + lb);
6.默认算法需要注意点
在这里插入图片描述
这里有意思的是,它内部算法先进行+1,然后就是1+1再%长度的运算。所以第一次取出来的就是server服务列表中的第二个server了...
存的顺序是8081,8082 默认取出来顺序是8082 8081 8082 8081
写到这里就结束了,基本是对默认的负载算法有一个流程的认识。如果不太了解Ribbon单机试玩版如何操作的话 看完可以帮助大家~
7.使用RxJava构建Ribbon
在结尾给大家附上一个通过RxJava实现的代码:
pom.xml:
io.reactivex
rxjava
1.0.10
Java:
// 服务列表
List serverList = Lists.newArrayList(new Server("localhost", 8081), new Server("localhost", 8083));
// 构建负载实例
ILoadBalancer loadBalancer = LoadBalancerBuilder.newBuilder().buildFixedServerListLoadBalancer(serverList);
// 调用 5 次来测试效果
for (int i = 0; i < 5; i++) {
String result = LoadBalancerCommand.builder().withLoadBalancer(loadBalancer).build()
.submit(new ServerOperation() {
public Observable call(Server server) {
try {
String addr = "http://" + server.getHost() + ":" + server.getPort() + "/user/hello";
System.out.println(" 调用地址:" + addr);
URL url = new URL(addr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.connect();
InputStream in = conn.getInputStream();
byte[] data = new byte[in.available()];
in.read(data);
return Observable.just(new String(data));
} catch (Exception e) {
return Observable.error(e);
}
}
}).toBlocking().first();
System.out.println(" 调用结果:" + result);