导语
目前在开发中主要的负载均衡方案分为两种;一种是集中式的负载均衡,在生产者和消费者之间通过F5或者是Nginx来进行负载均衡,而另一种则是在客户端自己进行负载均衡,也就是说请求客户端可以根据自己的能力来进行负载。Ribbon就是属于客户端负载均衡的一种。
Ribbon介绍
Ribbon 的主要模块如下:
- ribbon-loadbalancer 负载均衡模块,可以单独的进行使用,也可以整合到其他模块中进行使用,其中主要提供的就是Ribbon自定义实现的一些负载均衡算法
- ribbon-eureka: 基于Eureka进行封装的模块,主要使用可以方便的集成到Eureka中。
- ribbon-transport:主要是基于Netty的多协议支持,例如TCP、UDP、HTTP等。
- ribbon-httpclient: 基于Apache HttpClient 进行封装的REST Ful风格的客户端,天然集成了负载均衡算法,可以在项目中直接进行接口的调用。
- ribbon-example:从名称上可以看到就是给出了一些ribbon的示例代码。
- ribbon-core:核心代码模块。
Ribbon的使用
通过下面一个小例子来看看Ribbon在实际项目中如何使用。
创建项目导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<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>com.nihui.springcloud.ribbon</groupId>
<artifactId>base-ribbon</artifactId>
<version>1.0-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-loadbalancer</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId>
<version>1.0.10</version>
</dependency>
</dependencies>
</project>
编写测试类
public class MainApplication {
public static void main(String[] args) {
List<Server> serviceList = Arrays.asList(
new Server("localhost",8080),
new Server("localhost",8083));
ILoadBalancer loadBalancer = LoadBalancerBuilder.newBuilder()
.buildFixedServerListLoadBalancer(serviceList);
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 connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
InputStream in = connection.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);
}
}
}
通过上面这个例子主要是看看,Ribbon如何进行负载均衡操作,其中在接口调用的最底层还是通过HttpURLConnection。当然因为是RESTFul风格的调用,也可以使用其他的HTTP客户端工具进行操作。
RestTemplate 整合 Ribbon使用
在上面的例子中使用了HTTP客户端进行调用,在Spring提供了一个用来进行HTTP调用的模板类。这就是RestTemplate。
那么怎么使用RestTemplate来整合Ribbon进行调用呢,首先就来说明一下Spring RestTemplate如何进行使用。
Get方式调用
首先要做的事情就是将RestTemplate注入到Spring的容器中,SpringBoot中支持的容器注入方式如下
@Configuration
public class BeanConfiguration{
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
建立一个用来测试的接口类,并且增加两个接口,
@RestController
public class HouseController {
@GetMapping("/house/data")
public HouseInfo getData(@RequestParam(name = "name") String name){
return new HouseInfo(1L,"北京","朝阳","花园小区");
}
@GetMapping("/house/data/{name}")
public String getName(@PathVariable(name = "name")String name){
return name;
}
}
建立一个用来调用该接口的类
@RestController
public class HouseClientController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/call/data")
public HouseInfo getData(@RequestParam("name") String name){
return restTemplate.getForObject("http://localhost:8081/house/data?name="+name,HouseInfo.class);
}
@GetMapping("/call/data/{name}")
public String getData2(@PathVariable("name") String name){
return restTemplate.getForObject("http://localhost:8081/house/data/{name}",String.class,name);
}
}
获取数据的方式通过getForObject方法来进行,这个方法有如下的三个重载方法
- url: 请求的API地址,有两种方式,一种是url字符串,一种是通过URI的方式
- responseType: 表示返回值类型
- uriVariables: PathVariable 参数,有两种参数,一种是可变参数,一种是通过Map的方式进行
@Override
@Nullable
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
@Override
@Nullable
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
}
@Override
@Nullable
public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.GET, requestCallback, responseExtractor);
}
当然除了使用上面这种方法,还可以用getForEntity的方式进行操作。
@GetMapping("/call/dataEntity")
public HouseInfo getData(@RequestParam("name") String name){
ResponseEntity<HouseInfo> entity = restTemplate.getForEntity("http://localhost:8080/house/data?name=" + name, HouseInfo.class);
if (entity.getStatusCodeValue()==200){
return entity.getBody();
}
return null;
}
getForEntity方法可以获取到返回状态,请求头信息等等,通过getBody获取到响应的内容,其他的都是与getForObject是相同的也是有三种实现重载的方式。
Post方式
在上述的代码中加入如下的的一个操作方法
@PostMapping("/house/save")
public Long addData(@RequestBody HouseInfo houseInfo){
System.out.println(houseInfo.getCity());
return 10001L;
}
在调用接口中编写如下的操作类使用postForObject()
@GetMapping("/call/add")
public Long add(){
HouseInfo houseInfo = new HouseInfo();
return restTemplate.postForObject("http://localhost:8080/call/add",houseInfo,Long.class);
}
当然postForObject方法也有如下的三种重载方法
@Override
@Nullable
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
@Override
@Nullable
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType,
Map<String, ?> uriVariables) throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
}
@Override
@Nullable
public <T> T postForObject(URI url, @Nullable Object request, Class<T> responseType)
throws RestClientException {
RequestCallback requestCallback = httpEntityCallback(request, responseType);
HttpMessageConverterExtractor<T> responseExtractor =
new HttpMessageConverterExtractor<>(responseType, getMessageConverters());
return execute(url, HttpMethod.POST, requestCallback, responseExtractor);
}
除了get和Post方法,RestTemplate还提供了PUT ,DELETE等操作方法,当然还有一个所有请求都可以发送的方法就是exchange方法。
总结
这篇博客中主要介绍了在Spring 中RestTemplate的使用,当然在实际开发中通过这种方式可以有效的化简在使用Ribbon的时候进行的调用方式,能更好的整合Ribbon。简化了开发的步骤,提高开发效率