Ribbon简介
Ribbon是一个在云服务中久经沙场的客户端IPC库,它提供以下的一些特性:
- 负载均衡
- 故障容错
- 在异步和动态的模型中支持多协议通讯(HTTP、TCP、UDP)
- 缓存与批处理
引入Ribbon依赖,可以去Ribbon的maven仓库获取,下面是一个maven引入示例:
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon</artifactId>
<version>2.2.2</version>
</dependency>
Ribbon所包含的模块
- ribbon:在其他Ribbon模块和Hystrix上集成负载均衡、容错、缓存/批处理的api
- ribbon-loadbalancer:可以独立或与其他模块一起使用的负载均衡器的api
- ribbon-eureka:使用Eureka客户端为云提供动态服务器列表的api
- ribbon-transport:使用带有负载均衡功能的RxNetty支持HTTP、TCP和UDP协议的传输客户端
- ribbon-httpclient:构建在Apache HttpClient之上,与负载均衡器集成的REST客户端
- ribbon-example:提供了一些示例
- ribbon-core:客户端配置api和其他共享api
我们将会使用蓝色字体标记的几个模块用作演示。
负载均衡器组件
Ribbon提供的基本功能
- 向通信客户端提供公共DNS名称或单个服务器的IP
- 根据特定的逻辑选择服务器
Ribbon提供的高级功能
- 通过将客户端划分为区域(如数据中心的机架),在相同区域中减少服务器延迟,从而在客户端和服务器之间建立关联
- 保持服务器的统计信息,避免出现高延迟或频繁故障的服务器
- 保持区域的统计信息,避免可能出现停机的区域
负载均衡器的三大子模块
- Rule:确定从列表返回哪个服务的逻辑组件
- Ping:在后台运行的组件以确保服务的活跃度
- ServerList:这可以是静态的或动态的。如果它是动态的(由DynamicServerListLoadBalancer使用),后台线程将在特定的时间间隔刷新和过滤列表
创建Ribbon程序
如架构图所示,Ribbon充当负载均衡器的作用,能够让我们的服务消费者调用到自己想使用的服务,服务消费者不用关心中间具体的操作,只需要将要调用的服务信息告诉负载均衡器,Ribbon就会从相应的服务集群中选择一个可以使用的服务器,供服务消费者调用。
我们创建两个简单maven项目,ribbon-server和ribbon-client,ribbon-server充当服务端,提供服务,并启动两个不同的服务实例(端口不同),ribbon-client充当客户端,发起服务调用。为了方便项目的搭建,我们这里使用Spring Boot(如果不清楚Spring Boot的同学,可以查看Spring Boot的简单使用(二),做一个大致了解)来快速开始,访问Spring Boot官网,获取最新的依赖,我这里使用的最新稳定版本是2.0.1,加入到我们ribbon-server项目的pom.xml中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
在ribbon-server下新建com.init.springCloud包,创建启动类ServerApp,用控制台输入不同的端口来启动不同的项目,代码如下:
package com.init.springCloud;
import java.util.Scanner;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@SpringBootApplication
public class ServerApp {
public static void main(String[] args) {
@SuppressWarnings("resource")
Scanner scan = new Scanner(System.in);
String port = scan.nextLine();
new SpringApplicationBuilder(ServerApp.class).properties("server.port=" + port).run(args);
}
}
再创建一个控制器类ServerController来返回当前服务的信息,这里我们让服务返回一个Person类的信息,两个类的代码如下:
package com.init.springCloud;
import lombok.Data;
//这里使用了lombok,不使用这个的同学可以去掉下面这个注解,然后手动添加getter、setter方法
@Data
public class Person {
private Integer id; //主键ID
private String name; //姓名
private String info; //信息,根据URL地址查看服务的来源
}
package com.init.springCloud;
import javax.servlet.http.HttpServletRequest;
import org.springframework.http.MediaType;
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 ServerController {
@RequestMapping(value = "/search/{id}", method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Person searchPerson(@PathVariable Integer id,HttpServletRequest request){
Person person = new Person();
person.setId(id);
person.setName("Spirit");
person.setInfo(request.getRequestURL().toString());
return person;
}
}
运行ServerApp类的main()方法,输入8082,启动第一个server服务;再次启动main()方法,输入8083,启动第二个server服务。
然后为ribbon-client项目引入Ribbon的依赖,进入Ribbon依赖仓库,引入Ribbon的核心包ribbon-core和http请求包ribbon-httpclient,版本的话,我这里引入的是最新稳定版本2.2.5
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon-core</artifactId>
<version>2.2.5</version>
</dependency>
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon-httpclient</artifactId>
<version>2.2.5</version>
</dependency>
然后同样在ribbon-client项目下创建com.init.springCloud包,新建RibbonTest类,参照Ribbon的GitHub文档,创建负载请求,代码如下:
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");
//设置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();
}
}
}
}
笔者这里在引用Netflix提供的Ribbon快速开始文档的时候,出了些问题,由于文档比较久了,示例并不能直接使用,所以做了一些修改,同时,ribbon-httpclient是基于Apache的Http请求的,Ribbon弃用了这个方法,转而使用自己的内部http方法,时间关系,我在查阅文档的时候并没有找到这个替代的方法,就放弃了,如果有解决了这个问题的小伙伴,可以分享给我。
运行RibbonTest类的main()方法,我们可以看到测试类的方法是轮询请求了两个服务:
轮流去请求两个服务,是因为调用了Ribbon的Rule规则里面的RoundRobinRule,这是Ribbon的默认请求规则。Ribbon更加详细的信息后续再更。
最后,大家有什么不懂的或者其他需要交流的内容,也可以进入我的QQ讨论群一起讨论:654331206
Spring Cloud系列:
Spring Cloud服务管理框架Eureka简单示例(三)