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更加详细的信息后续再更。
Spring Cloud系列:
Spring Cloud服务管理框架Eureka简单示例(三)
Spring Cloud服务管理框架Eureka项目集群(四)