LoadBalancer的简单轮询规则
在上一篇博客中,我们使用Ribbon实现了负载,并且轮询请求了服务,我们就接着分析一下Ribbon里面实现负载均衡的LoadBalancer(负载均衡器),去看看它底层是怎么做的,以及这些请求规则如何配置。
首先拿到我们上一篇博客中的ribbon-client项目,在com.init.springCloud包下新建LoadBalancerTest类,在这个类里面新建一个基础的Ribbon负载均衡器,然后创建一个服务列表,并把服务列表装载到基础负载均衡器里,之后让负载均衡器多次执行选择服务,我们通过输出服务信息查看它的选取规则:
package com.init.springCloud; import java.util.ArrayList; import java.util.List; import com.netflix.loadbalancer.BaseLoadBalancer; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; public class LoadBalancerTest { public static void main(String[] args) { ILoadBalancer loadBalancer = new BaseLoadBalancer(); List<Server> servers = new ArrayList<Server>(); servers.add(new Server("localhost",8082)); servers.add(new Server("localhost",8083)); loadBalancer.addServers(servers); for(int i=0; i<10; i++){ //参数是负载均衡器可用来确定要返回哪个服务器的对象。置空表示不使用 Server chosedServer = loadBalancer.chooseServer(null); System.out.println("选择的服务是:"+chosedServer); } } }
运行LoadBalancerTest的main()方法,可以查看到控制台轮询选择了两个服务。
通过跟踪LoadBalancer的chooseServer方法,我们可以查看到负载均衡器默认使用的是RoundRobinRule
自定义负载均衡器的规则
参照上面的RoundRobinRule,我们也实现IRule接口,来创建一个自己的规则,在com.init.springCloud包下新建MyRule类,实现IRule接口,编写一个出现8082端口服务概率为20%的规则类:
package com.init.springCloud; import java.util.List; import java.util.Random; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.Server; public class MyRule implements IRule { private ILoadBalancer lb; @Override public Server choose(Object key) { Random random = new Random(); Integer num = random.nextInt(10);//在0-9这10个随机数里取值 //获取传输负载均衡器里所有的服务 List<Server> servers = lb.getAllServers(); if(num>7){//返回8082端口服务 return chooseServerByPort(servers,8082); } //返回8083端口服务 return chooseServerByPort(servers,8083); } private Server chooseServerByPort(List<Server> servers,Integer port){ for (Server server : servers) { if(server.getPort() == port){ return server; } } return null; } @Override public void setLoadBalancer(ILoadBalancer lb) { this.lb = lb; } @Override public ILoadBalancer getLoadBalancer() { return lb; } }
之后再创建一个类MyRuleTest,让负载均衡器加载我们新建的规则类,实现我们自己的负载,内容和LoadBalancerTest类大致:
package com.init.springCloud; import java.util.ArrayList; import java.util.List; import com.netflix.loadbalancer.BaseLoadBalancer; import com.netflix.loadbalancer.Server; public class MyRuleTest { public static void main(String[] args) { BaseLoadBalancer loadBalancer = new BaseLoadBalancer(); MyRule myRule = new MyRule(); loadBalancer.setRule(myRule); List<Server> servers = new ArrayList<Server>(); servers.add(new Server("localhost",8082)); servers.add(new Server("localhost",8083)); loadBalancer.addServers(servers); for(int i=0; i<10; i++){ //参数是负载均衡器可用来确定要返回哪个服务器的对象。置空表示不使用 Server chosedServer = loadBalancer.chooseServer(null); System.out.println("选择的服务是:"+chosedServer); } } }
运行MyRuleTest的main()方法,可以看到负载均衡器选择8082端口的概率就变小了
Ribbon的组件可以用编程方式设置,也可以是客户端配置属性的一部分,并通过反射创建。这些相关的属性在配置文件里面需要以"客户端名称"."命名空间"."属性名"的方式实现。Ribbon提供了以下属性的设置:
- NFLoadBalancerClassName 负载均衡器类名称设置
- NFLoadBalancerRuleClassName 规则类名称设置
- NFLoadBalancerPingClassName Ping类名称设置
- NIWSServerListClassName 服务列表类名称设置
- NIWSServerListFilterClassName 服务列表过滤器名称设置
我们为了验证创建的自定义规则能否生效,使用上面的配置方法去修改ribbon-client项目的RibbonTest类,增加一行配置自定义负载均衡器规则的代码,RibbonTest类最新代码:
package com.init.springCloud; import com.netflix.client.ClientException; import com.netflix.client.ClientFactory; import com.netflix.client.http.HttpRequest; import com.netflix.client.http.HttpResponse; import com.netflix.config.ConfigurationManager; import com.netflix.niws.client.http.RestClient; public class RibbonTest { public static void main(String[] args) { //设置要请求的服务器 ConfigurationManager.getConfigInstance().setProperty( "sample-client.ribbon.listOfServers", "localhost:8082,localhost:8083"); //配置自定义的负载均衡器规则 ConfigurationManager.getConfigInstance().setProperty( "sample-client.ribbon.NFLoadBalancerRuleClassName", MyRule.class.getName()); //设置REST请求客户端 RestClient client = (RestClient) ClientFactory.getNamedClient("sample-client"); //创建请求实例 HttpRequest request = HttpRequest.newBuilder().uri("/search/1").build(); //连续发送10次请求到服务器 for(int i=0; i<10; i++){ try { HttpResponse response = client.executeWithLoadBalancer(request); String result = response.getEntity(String.class); System.out.println("请求结果:"+result); } catch (ClientException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } } }
之后启动ribbon-server,在控制台键入两个端口8082和8083,运行两个不同的服务实例,再回来运行ribbon-client项目RibbonTest的main()方法,可以看到控制台输出的结果,证明我们配置的规则类成功了。
Ribbon内置的规则
- AvailabilityFilteringRule: 这条规则将跳过被认为是“电路跳闸”的服务器或具有高并发连接数的服务器。
- BestAvailableRule:这个规则会跳过“电路跳闸”的服务器,然后选取一个并发请求数最低的服务。
- PredicateBasedRule:将服务器过滤逻辑委托给{@link AbstractServerPredicate}实例的规则。过滤后,服务器会以循环的方式从过滤列表中返回。
- RandomRule:一种随机分配流量的负载平衡策略。
- RetryRule:在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server。
- RoundRobinRule:这条规则简单地通过轮询来选择服务器。它通常被用作更高级规则的默认规则或后退。
- WeightedResponseTimeRule:对于这个规则,每个服务器根据其平均响应时间给定一个权重。响应时间越长,得到的权重就越小。规则随机选择一个服务器,其中的可能性由服务器的权重决定。
- ZoneAvoidanceRule:使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的Server。
常用的一些规则我用蓝色字体凸显出来了,其实大部分规则底层使用的都是RoundRobinRule。
Spring Cloud系列:
Spring Cloud服务管理框架Eureka简单示例(三)
Spring Cloud服务管理框架Eureka项目集群(四)