我们先单独使用 ribbon 来看下:
- 建立服务端,简单的实现一个 controller, 还是响应中返回请求的服务所在端口等信息.简单代码如下:
package org.crazyit.cloud;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
@RequestMapping(value = "/person/{personId}", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public Person findPerson(@PathVariable("personId") Integer personId,
HttpServletRequest request) {
Person p = new Person();
p.setId(personId);
p.setName("Crazyit");
p.setAge(30);
p.setMessage(request.getRequestURL().toString());
return p;
}
@RequestMapping(value = "/", method = RequestMethod.GET)
@ResponseBody
public String hello() {
return "hello";
}
}
分别用 8080,和 8081 两个端口号启动上述服务。
- 使用 ribbon 来调用上述服务.代码如下:
pom.xml 为
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.crazyit.cloud</groupId> <artifactId>first-ribbon-client</artifactId> <version>0.0.1-SNAPSHOT</version> <dependencies> <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-httpclient</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>com.netflix.ribbon</groupId> <artifactId>ribbon-loadbalancer</artifactId> <version>2.2.2</version> <scope>compile</scope> </dependency> <dependency> <groupId>commons-configuration</groupId> <artifactId>commons-configuration</artifactId> <version>1.10</version> </dependency> <dependency> <groupId>com.netflix.ribbon</groupId> <artifactId>ribbon-loadbalancer</artifactId> <version>2.2.2</version> </dependency> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>18.0</version> </dependency> <dependency> <groupId>com.netflix.archaius</groupId> <artifactId>archaius-core</artifactId> <version>0.7.4</version> </dependency> </dependencies> </project>
具体调用代码:
package org.crazyit.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.loadbalancer.BaseLoadBalancer;
import com.netflix.niws.client.http.RestClient;
public class TestRestClient {
public static void main(String[] args) throws Exception {
// 设置请求的服务器
ConfigurationManager.getConfigInstance().setProperty(
"my-client.ribbon.listOfServers",
"localhost:8080,localhost:8081");
// 获取REST请求客户端
RestClient client = (RestClient) ClientFactory
.getNamedClient("my-client");
// 创建请求实例
HttpRequest request = HttpRequest.newBuilder().uri("/person/1").build();
// 发 送10次请求到服务器中
for (int i = 0; i < 6; i++) {
HttpResponse response = client.executeWithLoadBalancer(request);
String result = response.getEntity(String.class);
System.out.println(result);
}
}
}
输出:
{"id":1,"name":"Crazyit","age":30,"message":"http://localhost:8081/person/1"}
{"id":1,"name":"Crazyit","age":30,"message":"http://localhost:8080/person/1"}
{"id":1,"name":"Crazyit","age":30,"message":"http://localhost:8081/person/1"}
{"id":1,"name":"Crazyit","age":30,"message":"http://localhost:8080/person/1"}
{"id":1,"name":"Crazyit","age":30,"message":"http://localhost:8081/person/1"}
{"id":1,"name":"Crazyit","age":30,"message":"http://localhost:8080/person/1"}
上述直接硬编码配置了服务器地址,也可以放到配置文件中 application.properties,然后通过 ConfigurationManager.loadPropertiesFromResources(xxxx);
来获取例如:
my-client.ribbon.listOfServers=localhost:8080,localhost:8081
格式: <client>.<nameSpace>.<property>=<value>
其中 client 是客户的名称,nameSpace 是该配置的命名空间,默认为 ribbon,property 是属性名,value 是属性值; 如果对所有客户端都生效也可以省略客户名 client.
另外也可以手动创建负载均衡器以及配置服务器
package org.crazyit.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 ChoseServerTest {
public static void main(String[] args) {
// 创建负载均衡器
// BaseLoadBalancer lb = new BaseLoadBalancer();
ILoadBalancer lb = new BaseLoadBalancer();
// 添加服务器
List<Server> servers = new ArrayList<Server>();
servers.add(new Server("localhost", 8080));
servers.add(new Server("localhost", 8081));
lb.addServers(servers);
// 进行6次服务器选择
for(int i = 0; i < 6; i++) {
Server s = lb.chooseServer(null);
System.out.println(s);
}
}
}
对应的输出:
localhost:8081
localhost:8080
localhost:8081
localhost:8080
localhost:8081
localhost:8080
结果是一样的, 默认使用的是 RoundRobinRule 规则。
自定义负载规则
我们知道选择哪个服务器进行处理是 ILoadBalancer 接口的 chooseServer 方法决定,而在 BaseLoadBalancer 类中,则使用 IRule 接口的 choose 方法来决定选择哪一个服务器对象。如果自定义负载均衡规则,可以编写一个 IRule 的实现类。
1.首先实现 IRule 接口
package org.crazyit.cloud;
import java.util.List;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.Server;
/**
* 自定义的规则类
* @author 杨恩雄
*
*/
public class MyRule implements IRule {
ILoadBalancer lb;
public MyRule() {
}
public MyRule(ILoadBalancer lb) {
this.lb = lb;
}
public Server choose(Object key) {
// 获取全部的服务器
List<Server> servers = lb.getAllServers();
// 只返回第一个Server对象
return servers.get(0);
}
public void setLoadBalancer(ILoadBalancer lb) {
this.lb = lb;
}
public ILoadBalancer getLoadBalancer() {
return this.lb;
}
}
2. 指定负载策略(硬编码)
package org.crazyit.cloud;
import java.util.ArrayList;
import java.util.List;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
public class TestMyRule {
public static void main(String[] args) {
// 创建负载均衡器
BaseLoadBalancer lb = new BaseLoadBalancer();
// 设置自定义的负载规则
lb.setRule(new MyRule(lb));
// 添加服务器
List<Server> servers = new ArrayList<Server>();
servers.add(new Server("localhost", 8080));
servers.add(new Server("localhost", 8081));
lb.addServers(servers);
// 进行6次服务器选择
for(int i = 0; i < 6; i++) {
Server s = lb.chooseServer(null);
System.out.println(s);
}
}
}
结果输出:
localhost:8080
localhost:8080
localhost:8080
localhost:8080
localhost:8080
localhost:8080
2.指定负载策略(配置)
package org.crazyit.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.loadbalancer.BaseLoadBalancer;
import com.netflix.niws.client.http.RestClient;
public class TestMyRuleConfig {
public static void main(String[] args) throws Exception {
// 设置请求的服务器
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();
// 发 送10次请求到服务器中
for (int i = 0; i < 6; i++) {
HttpResponse response = client.executeWithLoadBalancer(request);
String result = response.getEntity(String.class);
System.out.println(result);
}
}
}
输出:
{"id":1,"name":"Crazyit","age":30,"message":"http://localhost:8080/person/1"}
{"id":1,"name":"Crazyit","age":30,"message":"http://localhost:8080/person/1"}
{"id":1,"name":"Crazyit","age":30,"message":"http://localhost:8080/person/1"}
{"id":1,"name":"Crazyit","age":30,"message":"http://localhost:8080/person/1"}
{"id":1,"name":"Crazyit","age":30,"message":"http://localhost:8080/person/1"}
{"id":1,"name":"Crazyit","age":30,"message":"http://localhost:8080/person/1"}
Ribbon 自带的负载规则
- RoundRobinRule: 系统默认的规则,通过简单地轮询服务列表来选择服务器,其他规则在很多情况下仍然使用 RoundRobinRule;
- AvailabilityFilteringRule: 该规则会忽略以下服务器。
无法连接的服务器:在默认情况下,如果 3 次连接失败,该服务器将会被置为"短路"状态,改状态将持续 30 秒;如果再次连接失败,"短路"状态的持续时间将会以几何级数增加,可以通过修改niws.loadbalancer.<clientName>.connectionFailureCountThreshold
属性来配置连接失败的次数。
并发过高的服务器:如果连接到该服务器的并发数过高,也会被这个规则忽略,可以通过修改<clientName>.ribbon.ActiveConnectionsLimit
属性设定最高并发数。 - WeightedResponseTimeRule: 权重,服务器的响应时间越长,该权重值就应配置越少。
- ZoneAvoidanceRule: 该规则以区域、可用服务器为基础进行选择,可以理解为机架或者机房。
- BestAvailableRule: 忽略"短路"的服务器,并选择并发数较低的服务器.
- RandomRule: 随机选择可用的服务器。
- RetryRule: 含有重试的选择逻辑,如果使用 RoundRobinRule 选择的服务器无法连接,那么将会重新选择服务器。
Ping 机制
负载均衡器中提供了 Ping 机制,每个一段时间会去 ping 服务器,该工作由 IPing 接口的实现类负责,如果单独使用 Ribbon,默认情况不激活 Ping 机制,默认实现类为 DummyPing,以下示例使用另一个 IPing 实现类 PingUrl
package org.crazyit.cloud;
import java.util.ArrayList;
import java.util.List;
import com.netflix.config.ConfigurationManager;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.PingUrl;
import com.netflix.loadbalancer.Server;
public class TestPingUrl {
public static void main(String[] args) throws Exception {
// 创建负载均衡器
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());
}
}
}
输出:
localhost:8080 状态:true
localhost:8888 状态:false
除了硬编码也可以配置来实现:
package org.crazyit.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;
import static com.netflix.client.ClientFactory.*;
public class TestPingUrlConfig {
public static void main(String[] args) throws Exception {
// 设置请求的服务器
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) 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());
}
}
}
输出结果一致:
2
localhost:8080 状态:true
localhost:8888 状态:false
自定义 Ping
package org.crazyit.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
package org.crazyit.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 MyPingTest {
public static void main(String[] args) throws Exception {
// 设置请求的服务器
ConfigurationManager.getConfigInstance().setProperty(
"my-client.ribbon.listOfServers",
"localhost:8080,localhost:8888");
// 配置Ping处理类
ConfigurationManager.getConfigInstance().setProperty(
"my-client.ribbon.NFLoadBalancerPingClassName",
MyPing.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());
}
}
}
输出
这是自定义 Ping 实现类:localhost:8080
这是自定义 Ping 实现类:localhost:8888
这是自定义 Ping 实现类:localhost:8080
这是自定义 Ping 实现类:localhost:8888
这是自定义 Ping 实现类:localhost:8080
这是自定义 Ping 实现类:localhost:8888
这是自定义 Ping 实现类:localhost:8080
这是自定义 Ping 实现类:localhost:8888
这是自定义 Ping 实现类:localhost:8080
这是自定义 Ping 实现类:localhost:8888
2
localhost:8080 状态:true
localhost:8888 状态:true
其他配置:
NFLoadBalancerClassName:指定负载均衡器的实现类,可利用该配置实现自己的负载均衡器。
NIWSServerListClassName: 服务器列表处理类,用来维护服务器列表,Ribbon 已经实现动态服务器列表。
NIWSServerListFilterClassName: 用于处理服务器列表拦截。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于