Ribbon负载均衡器
- Ribbon负载均衡器
1、Ribbon负载均衡器
- Ribbon的负载均衡接口,定义了服务器的操作,主要是用于进行服务器的选择
- 在《Spring Cloud_6_负载均衡框架Ribbon》的案例中,客户端使用了RestClient类,在发送请求时,会使用负载均衡器(ILoadBalancer)接口,根据特定的逻辑来选择服务器,服务器列表可以使用listOfServers进行配置,也可以使用动态更新机制
- 试用负载均衡器
1.1、修改测试类
package com.atm.cloud;
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 MyChoseRESTClient {
public static void main(String[] args) {
// 创建负载均衡器
ILoadBalancer iLoadBalancer = new BaseLoadBalancer();
// 添加服务器
List<Server> serverList = new ArrayList<Server>();
serverList.add(new Server("localhost", 8080));
serverList.add(new Server("localhost", 8081));
iLoadBalancer.addServers(serverList);
//进行6次服务器选择
for (int i = 0; i < 6; i++) {
Server server=iLoadBalancer.chooseServer(null);
System.out.println(server);
}
}
}
- 代码中使用了BaseLoadBalancer这个负载均衡器,将两个服务器对象加入到负载均衡器中,再调用了6次chooseServer方法
- 根据结果可以判定,选择服务器的逻辑是一致的,在默认情况下,会使用RoundRobinRule的规则逻辑
1.2、自定义负载规则
- 由前面小节可知,选择那个服务器进行请求处理,由ILoadBalancer接口的chooserServer方法决sing,而在BaseLoadBalancer类中,则使用IRule接口的choose方法来决定选择哪一个服务器对象
- 如果想自定义负载均衡规定,可以编写一个IRule接口的实现类,实现自己的负载规定
package com.atm.cloud;
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 {
ILoadBalancer ibBalancer;
public MyRule() {
}
public MyRule(ILoadBalancer iLoadBalancer) {
this.ibBalancer = iLoadBalancer;
}
public Server choose(Object key) {
// 获取全部的服务器
List<Server> servers = ibBalancer.getAllServers();
/**
Random random=new Random();
int randNum=random.nextInt(10);
if(randNum>7){
getServerByPort(servers, 8081);
}
**/
// 至返回第一个Server对象
//return getServerByPort(servers, 8080);
return servers.get(0);
}
public Server getServerByPort(List<Server> servers,int port){
for (Server server : servers) {
if(server.getPort()==port){
return server;
}
}
return null;
}
public ILoadBalancer getLoadBalancer() {
return this.ibBalancer;
}
public void setLoadBalancer(ILoadBalancer iLoadBalancer) {
this.ibBalancer = iLoadBalancer;
}
}
- 在自定义规则类中,实现了choose方法,调用了ILoadBalancer的gerAllServers方法返回全部的服务器,为了简单起见,本例只返回第一个服务器
- 为了能在负载均衡器中使用自定义的规则,需要修改选择服务器的代码
package com.atm.cloud;
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 MyChoseRESTClient {
public static void main(String[] args) {
// 创建负载均衡器
//ILoadBalancer iLoadBalancer = new BaseLoadBalancer();
BaseLoadBalancer iLoadBalancer=new BaseLoadBalancer();
//设置自定义的负载规则
iLoadBalancer.setRule(new MyRule(iLoadBalancer));
// 添加服务器
List<Server> serverList = new ArrayList<Server>();
serverList.add(new Server("localhost", 8080));
serverList.add(new Server("localhost", 8081));
iLoadBalancer.addServers(serverList);
//进行6次服务器选择
for (int i = 0; i < 6; i++) {
Server server=iLoadBalancer.chooseServer(null);
System.out.println(server);
}
}
}
- 可以看到,请求6次所得到的服务器均为8080
- 以上是直接使用编码方式来设置负载规则,可以使用配置的方式来完成这些工作
- 修改Ribbon的配置,让请求的客户端,使用我们定义的负载规则
package com.atm.cloud;
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 MyRESTClient {
public static void main(String[] args) {
// 设置请求的服务器
ConfigurationManager.getConfigInstance().setProperty(
"my-client.ribbon.listOfServers",
"localhost:8080,localhost:8081");
// *****配置规则处理类
ConfigurationManager.getConfigInstance().setProperty(
"my-client.ribbon.NFLoadBalancerRuleClassName",
MyRule.class.getName());
// 获取REST请求客户端
RestClient client = (RestClient) ClientFactory
.getNamedClient("my-client");
// 创建请求实例
HttpRequest request = HttpRequest.newBuilder().uri("/person/1").build();
// 发送6次请求到服务器中
for (int i = 0; i < 6; i++) {
try {
HttpResponse response = client.executeWithLoadBalancer(request);
String result = response.getEntity(String.class);
System.out.println(result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
- 请求客户端中,基本与前面张杰的客户端基本一致,只是加入了“my-client.ribbon.NFLoadBalancerRuleClassName”属性,设置了自定义规则处理类为MyRule,这个配置项同样可以在配置文件中使用,包括SpringCloud的配置文件等
- 在实际环境中,如果要实现自定义的负载规则,可能还需要结合各种因素:具体业务的发生时间、服务器性能等
1.3、Ribbon自带的负载规则
Ribbon提供了若干个内置的负载规则,使用者完全可以直接使用
- RoundRobinRule:系统默认的规则,通过简单的轮询服务列表来选择服务器,其他的规则在很多情况下,仍然使用RoundRobinRule
AvailablilityFilteringRule:该各种会忽略以下服务器
无法连接的服务器:在默认情况下,如果3次连接失败,该服务器将会被置为“短路”的状态,该状态将持续30秒,如果再次连接失败,“短路”状态的持续时间将会以几何级增加。可以通过修改niws.loadbalance..connerctionFailureCountThreshold属性来配置连接失败的次数
并发数过高的服务器:如果连接到该服务器的并发数过高,也会被这个规则忽略,可以通过修改.ribbon.ActiveConnectionLimit属性来设定最高并发数WeightedResponseTimeRule:为每个服务器赋予一个权重值,服务器的响应时间越长,该权重值就越少,这个规则会随机选择服务器,这个权重值有可以能会决定服务器的选择
- ZoneAvoidanceRule:该规则以区域、可用服务器为基础,进行服务器选择。使用Zone对服务器进行分类,可以理解为机架或者机房
- BestAvailiableRule:忽略“短路”的服务器,并选择并发数较低的服务器
- RandomRule:随机选择可用服务器
- RetryRule:含有重试的选择逻辑,如果使用RoundRobinRule
以上提供的负载规则,基本上可以满足大部分的需求
1.4、Ping机制
- 在负载均衡器中,提供了Ping的机制,每隔一段时间,会去Ping服务器,判断服务器是否存活
- 该工作由IPing接口的实现类负责,如果单独使用Ribbon,在默认情况下,不会激活Ping机制,默认的实现类为DummyPing
package com.atm.cloud;
import java.util.ArrayList;
import java.util.List;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.PingUrl;
import com.netflix.loadbalancer.Server;
public class MyTestPingUrl {
public static void main(String[] args) throws InterruptedException {
// 创建负载均衡器
BaseLoadBalancer lb = new BaseLoadBalancer();
// 添加服务器
List<Server> servers = new ArrayList<Server>();
// 8080 端口连接正常
servers.add(new Server("localhost", 8080));
// 一个不存在的端口
servers.add(new Server("localhost", 8888));
lb.addServers(servers);
// 设置 IPing 实现类
lb.setPing(new PingUrl());
// 设置 Ping 时间间隔为 2 秒
lb.setPingInterval(2);
Thread.sleep(6000);
for (Server s : lb.getAllServers()) {
System.out.println(s.getHostPort() + " 状态:" + s.isAlive());
}
}
}
- 使用了代码的方法来设置负载均衡器使用PingUrl,设置了每隔2秒,就向两个服务器请求
- PingUrl实际是使用的是HttpClient
- 除了在代码中配置IPing类外,还可以在配置中设置IPing实现类
package com.atm.cloud;
import java.util.List;
import com.netflix.client.ClientFactory;
import com.netflix.config.ConfigurationManager;
import com.netflix.loadbalancer.PingUrl;
import com.netflix.loadbalancer.Server;
import com.netflix.niws.client.http.RestClient;
public class MyPingConfig {
public static void main(String[] args) throws InterruptedException {
// 设置请求的服务器
ConfigurationManager.getConfigInstance().setProperty(
"my-client.ribbon.listOfServers",
"localhost:8080,localhost:8888");
// 配置 Ping 处理类
ConfigurationManager.getConfigInstance().setProperty(
"my-client.ribbon.NFLoadBalancerPingClassName",
PingUrl.class.getName());
// 配置 Ping 时间间隔
ConfigurationManager.getConfigInstance().setProperty(
"my-client.ribbon.NFLoadBalancerPingInterval", 2);
// 获取 REST 请求客户端
RestClient client = (RestClient) ClientFactory
.getNamedClient("my-client");
Thread.sleep(6000);
// 获取全部服务器
List<Server> servers = client.getLoadBalancer().getAllServers();
System.out.println(servers.size());
// 输出状态
for (Server s : servers) {
System.out.println(s.getHostPort() + " 状态:" + s.isAlive());
}
}
}
- my-client.ribbon.NFLoadBalancerPingClassName:配置 IPing 的实现类
- my-client.ribbon.NFLoadBalancerPingInterval:配置 Ping 操作的时间间隔。
- 以上两个配置,同样可以使用在配置文件中
1.5、自定义Ping
- 实现自定义Ping较为简单,先实现IPing接口,再通过配置来设定具体的Ping实现类
package com.atm.cloud;
import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.Server;
public class MyPing implements IPing{
public boolean isAlive(Server server) {
System.out.println("这是自定义 Ping 实现类:" + server.getHostPort());
return true;
}
}
- 要使用自定义的Ping类,通 过 修 改client.nameSpace.NFLoadBalancerPingClassName 配置即可
1.6、其他配置
- NFLoadBalancerClassName:指定负载均衡器的实现类,可利用该配置,实现自己的负载均衡器。
NIWSServerListClassName:服务器列表处理类,用来维护服务器列表,Ribbon
已经实现动态服务器列表。NIWSServerListFilterClassName:用于处理服务器列表拦截。