@
目录负载方案简介:
目前主流的负载方案分为以下两种:
1.集中式负载均衡,在消费者和服务提供方中间使用独立的代理方式进行负载,有硬件的(比如 F5),也有软件的(比如 Nginx)。
2.客户端自己做负载均衡,根据自己的请求情况做负载,Ribbon 就属于客户端自己做负载。
而Spring Cloud Ribbon 虽然只是一个工具类框架,它不像服务注册中心、配置中心、API 网关那样需要独立部署,但是它几乎存在于每一个 Spring Cloud 构建的微服务和基础设施中。因为微服务间的调用,API 网关的请求转发等内容,实际上都是通过 Ribbon 来实现的
接下来就带大家通过几行源码的debug查看单机的Ribbon默认算法是实现的
pom.xml:
<!-- https://mvnrepository.com/artifact/com.google.guava/guava --> <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>
public static void main(String[] args) { // 注册两个server,顺序分别是8081,8082 List<Server> 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); }
// 这段代码是其核心,这里注册了默认的算法机制和choose的api LoadBalancerBuilder.newBuilder().buildFixedServerListLoadBalancer(serverList);
接下来介绍我所认知的几个核心关键点
// 用来注册默认的负载均衡算法类,以配置类对象的形式初始化用到的属性 LoadBalancerBuilder.newBuilder()
具体流程:
在这里会看到一大坨默认的注册信息,核心参数:"NFLoadBalancerRuleClassName" 请记住这个字段
咱们这里只看有关默认算法的代码,其他的不做扩展
public void loadDefaultValues() { xxxxxxxxxx this.putDefaultStringProperty(CommonClientConfigKey.NFLoadBalancerRuleClassName, this.getDefaultNfloadbalancerRuleClassname()); xxxxxxxxxx }
他继承自ClientConfigEnabledRoundRobinRule对象 所以在后期创建的时候会执行其构造方法和类的属性。
ILoadBalancer baseLoadBalancer = LoadBalancerBuilder .newBuilder() .buildFixedServerListLoadBalancer(serverList);
在下面又通过ClientFactory.instantiateInstanceWithClientConfig(ruleClassName, config);方法进行className的实例化,拿到AvailabilityFilteringRule对象的实例,并且内部Rule默认为RoundRobinRule对象做负载均衡算法。
除了RoundRobinRule以外还有其他的算法
Server server = baseLoadBalancer.chooseServer();
往下直接看606行的choose方法
继续看choose方法
在点进去
这里核心的方法是44行的"this.incrementAndGetModulo(serverCount);"方法,它通过你传入的server列表个数拿到负载均衡后的索引值。
算法实现是每次都拿这个AtomicInteger类型值做当前server数组的下标,每次choose都get出当前下标,进行+1再取模 达到雨露均沾的负载均衡效果,并且使用cas方式操作值的变化。
private AtomicInteger nextServerCyclicCounter;
取出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);
这里有意思的是,它内部算法先进行+1,然后就是1+1再%长度的运算。所以第一次取出来的就是server服务列表中的第二个server了...
存的顺序是8081,8082 默认取出来顺序是8082 8081 8082 8081
写到这里就结束了,基本是对默认的负载算法有一个流程的认识。如果不太了解Ribbon单机试玩版如何操作的话 看完可以帮助大家~
在结尾给大家附上一个通过RxJava实现的代码:
pom.xml:
<dependency> <groupId>io.reactivex</groupId> <artifactId>rxjava</artifactId> <version>1.0.10</version> </dependency>
Java:
// 服务列表 List<Server> 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.<String>builder().withLoadBalancer(loadBalancer).build() .submit(new ServerOperation<String>() { public Observable<String> 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); }