"阅读本文大约需要 4 分钟"
在之前几篇 Spring Cloud 的 5 分钟指南里我们已经依次介绍了多个 Spring Cloud 的核心组件, 其中涵盖了微服务架构中必须的基础功能, 例如服务发现, 熔断, 中心化配置. 今天介绍的主角同样是由 Netflix 出品的 Ribbon, 官方给 Ribbon 的定义是一个用于远程调用的库, 而它更为人熟知的作用是可以进行客户端的 Load Balance.
Load Balance 对于任何一个分布式系统都是必要的基础功能, 从实现角度而言, 可以分为客户端 LB 和服务器端 LB. 在日常工作中大家接触最多的应该是基于服务器的 LB 解决方案, 例如通过 nginx 的反向代理, 或是商用的 F5. 但是今天的例子中会演示如何使用 Ribbon 搭建一个具有 LB 的客户端应用. 让我们开始吧.
基础概念
依然使用 https://start.spring.io/ 来快速搭建一个 Spring Boot 的项目同时如下图添加 Ribbon 的依赖:
Ribbon 添加依赖. PNG
在编写具体代码前我们先介绍 Ribbon 中的核心概念.
Rule - 这个组件决定了从一堆候选服务器中返回那个服务器地址进行远程调用的操作.
Ping - 在后台运行的组件, 确认哪些服务器是存活可用的.
ServerList - 当前可以用作 LB 的服务器列表, 这个列表可以是静态的, 也可以是动态的. 如果是动态列表 (例如从 Eurka 服务器获取), 则会有一个后台线程按照时间间隔刷新列表.
Ribbon 内建提供了 3 中 Rule 供我们使用:
RoundRobinRule: 最简单的规则, 会在 ServerList 中依次调用.
AvailabilityFilteringRule: 在这种规则下 Ribbon 集成了 Hystrix 的功能, 默认情况下调用某个远程方法失败 3 次后, 断路器的开关会被打开, 而之后的请求中 Ribbon 会跳过这个服务器地址, 直到 30 秒之后断路器关闭后才会重新加入调用列表.
WeightedResponseTimeRule: 将响应时间作为权重的负载规则, 当某个服务器的响应时间越长, 则它的权重就越低. 具体选择服务器时, 结合权重进行随机选择.
配置
Spring Cloud Ribbon 支持多种配置方式, 最常用的是以 application.YAML 等配置文件和编程式的方式进行配置. 我们先来熟悉一下配置的格式.<clientName>.<nameSpace>.<propertyName>=<value> 的格式来描述参数的, 其中 clientName 代表了基于 Ribbon 客户端的名称, 而 nameSpace 则是类似配置组的概念, 代表了一组配置的参数.
我们先来看一下官方示例的配置:
- # Max number of retries on the same server (excluding the first try)
- sample-client.ribbon.MaxAutoRetries=1
- # Max number of next servers to retry (excluding the first server)
- sample-client.ribbon.MaxAutoRetriesNextServer=1
- # Whether all operations can be retried for this client
- sample-client.ribbon.OkToRetryOnAllOperations=true
- # Interval to refresh the server list from the source
- sample-client.ribbon.ServerListRefreshInterval=2000
- # Connect timeout used by Apache HttpClient
- sample-client.ribbon.ConnectTimeout=3000
- # Read timeout used by Apache HttpClient
- sample-client.ribbon.ReadTimeout=3000
- # Initial list of servers, can be changed via Archaius dynamic property at runtime
- sample-client.ribbon.listOfServers=www.microsoft.com:80,www.yahoo.com:80,www.google.com:80
然后看一下官方的示例代码:
- public static void main(String[] args) throws Exception {
- ConfigurationManager.loadPropertiesFromResources("sample-client.properties"); // 1
- System.out.println(ConfigurationManager.getConfigInstance().getProperty("sample-client.ribbon.listOfServers"));
- RestClient client = (RestClient) ClientFactory.getNamedClient("sample-client"); // 2
- HttpClientRequest request = HttpClientRequest.newBuilder().setUri(new URI("/")).build(); // 3
- for (int I = 0; I < 20; I++) {
- HttpClientResponse response = client.executeWithLoadBalancer(request); // 4
- System.out.println("Status code for" + response.getRequestedURI() + ":" + response.getStatus());
- }
- ZoneAwareLoadBalancer lb = (ZoneAwareLoadBalancer) client.getLoadBalancer();
- System.out.println(lb.getLoadBalancerStats());
- ConfigurationManager.getConfigInstance().setProperty(
- "sample-client.ribbon.listOfServers", "www.linkedin.com:80,www.google.com:80"); // 5
- System.out.println("changing servers ...");
- Thread.sleep(3000); // 6
- for (int I = 0; I < 20; I++) {
- HttpClientResponse response = client.executeWithLoadBalancer(request);
- System.out.println("Status code for" + response.getRequestedURI() + ":" + response.getStatus());
- response.releaseResources();
- }
- System.out.println(lb.getLoadBalancerStats()); // 7
- }
从
sample-client.properties
中加载配置信息.
通过 ClientFactory 初始化名为 sample-client 的客户端.
构建一个 http request 对象, 包含了调用远程 endpoint 的相关信息.
执行远程调用.
修改可用的服务器列表.
线程休眠 3 秒后, 重新调用远程方法.
输出 LB 的执行信息.
从运行的输出结果我们可以看到, 在第一次调用中, 会依次输出我们在配置文件中的服务器信息, 即 microsoft,yahoo 和 google. 在我们修改配置后, 第二次的调用就会看到服务器列表变为了 linkedin 和 google.
与 Spring Cloud 集成
微服务体系架构中远程调用非常常见, 因此 Spring Cloud 集成了 Ribbon, 便于开发人员优化远程调用的过程. 与 Spring Cloud 集成的过程中, 我们先编写以下的配置文件:
- spring:
- application:
- name: spring-cloud-ribbon
- server:
- port: 8888
- ping-server:
- ribbon:
- eureka:
- enabled: false
- listOfServers: localhost:9092,localhost:9999
- ServerListRefreshInterval: 15000
如同之前提到的, 我们配置了 clientName 为 ping-server,namespace 为 ribbon 的参数, 可选的服务器列表为本地的 localhost:9092 和 localhost:9999, 其他的都为默认配置.
Spring Cloud 已经为我们对 Ribbon 做了周全的配置, 只需要添加几个注解就能使用了. 在下面的示例中, 我们使用 Spring 提供的 RestTemplate 进行远程调用, 通过一些简单的注解就能让原本普通的 RestTemplate 具有负载均衡和熔断的效果, 请看以下的代码:
- @SpringBootApplication
- @RestController
- @RibbonClient(
- name = "ping-a-server",
- configuration = RibbonConfiguration.class)
- public class ServerLocationApp {
- @LoadBalanced
- @Bean
- RestTemplate getRestTemplate() {
- return new RestTemplate();
- }
- @Autowired
- RestTemplate restTemplate;
- @RequestMapping("/server-location")
- public String serverLocation() {
- return this.restTemplate.getForObject(
- "http://ping-server/locaus", String.class);
- }
- public static void main(String[] args) {
- SpringApplication.run(ServerLocationApp.class, args);
- }
- }
其中 @RibbonClient 会按照我们之前的配置信息返回 Ribbon 的包装对象, 而 @LoadBalanced 注解会把创建的 RestTemplate 对象代理为一个具有 Ribbon 功能的对象, 而使用者是感觉不出两者不同的.
结语
通过一些简单的配置和代码就能在我们的系统的远程调用中加入负载均衡和熔断的功能, 我们会在之后的高级篇中介绍如何与 Eureka 集成, 从 Eureka Server 中动态的获取可用服务器列表, 想要了解更多 Ribbon 和微服务的知识就赶快专注我的公众号吧!
13688092-1d0f5f4dcf0ba004.PNG
来源: http://www.jianshu.com/p/795a54ace95b