Spring Cloud Ribbon 是一个基于 HTTP 和 TCP 的客户端负载均衡工具,它基于 Netflix Ribbon 实现,可以轻松地将面向服务的 REST 模板请求(RestTemplate)自动转换成客户端负载均衡的服务调用。

Spring Cloud Ribbon 是一个工具类框架,它不像服务注册中心、配置中心、API 网关那样需要独立部署,它存在于每一个 Spring Cloud 构建的微服务和基础设施中。

RestTemplate 详解

在 Eureka 一文中已经通过引入 Ribbon 实现了服务消费者的客户端负载均衡功能。其中,通过在创建 RestTemplate 对象的方法上配置 @LoadBalanced 开启客户端负载均衡。

GET 请求

在 RestTemplate 中,对 GET 请求有两类方法可以使用。

getForEntity 函数

该方法返回的是 ResponseEntity,该对象是 Spring 对 HTTP 请求响应的封装。该方法提供了以下三种不同的重载实现。

  • getForEntity(String url, Class responseType, Object… uriVariables):该方法提供了三个参数,其中 url 为请求的地址,responseType 为请求响应体 body 的包装类型,uriVariables 为 url 中的参数绑定。比如 getForEntity(“http://USER-SERVICE/user?name={1}“, String.class, “zhangsan”),其中第三个参数 zhangsan 会替换 url 中的 {1} 占位符。
  • getForEntity(String url, Class responseType, Map uriVariables):该方法中提供的参数中,只有 uriVariables 的参数类型与上面的方法不同。这里使用了 Map 类型,所以使用该方法进行参数绑定时需要在占位符中制定 Map 中参数的 key 值,比如:
  1. RestTemplate restTemplate = new RestTemplate();
  2. Map<String, String> params = new HashMap<>();
  3. param.put("name", "zhangsan");
  4. ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://USER-SERVICE/user?name={name}",
  5. String.class, params);
  • getForEntity(URI url, Class responseType):该方法使用 URI 对象来替代之前的 url 和 urlVariables 参数来制定访问地址和参数绑定。URI 是 JDK java.net 包下的一个类,它表示一个统一的资源标识符引用。比如:
  1. RestTemplate restTemplate = new RestTemplate();
  2. UriComponents uriComponents = UriComponentsBuilder.fromUriString(
  3. "http://USER-SERVICE/user?name={name}")
  4. .build()
  5. .expand("zhangsan")
  6. .encode();
  7. URI uri = uriComponents.toUri();
  8. ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class).getBody();

getForObject 函数

它与 getForEntity 函数类似,也提供了三种不同的重载实现,只不过请求直接返回包装好的对象内容。比如:

  1. RestTemplate restTemplate = new RestTemplate();
  2. User user = restTemplate.getForObject(uri, User.class);
  • getForObject(String url, Class responseType, Object… uriVariables)
  • getForObject(String url, Class responseType, Map uriVariables)
  • getForObject(URI url, Class responseType)

POST 请求

在 RestTemplate 中,对 POST 请求是可以通过如下三类方法进行调用实现。

postForEntity 函数

该方法同 GET 请求中的 getForEntity 类似,会在调用后返回 ResponseEntity 对象,其中 T 为请求响应的 body 类型。比如:

  1. RestTemplate restTemplate = new RestTemplate();
  2. User user = new User("zhangsan", 30);
  3. ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://USER-SERVICE/user", user, String.class);
  4. String body = responseEntity.getBody();

postForEntity 函数也实现了三种不同的重载方法。

  • postForEntity(String url, @Nullable Object request, Class responseType, Object… uriVariables)
  • postForEntity(String url, @Nullable Object request, Class responseType, Map uriVariables)
  • postForEntity(URI url, @Nullable Object request, Class responseType)

这些方法中的参数用法大部分与 getForEntity 一致,需要注意的是新增加的 request 参数,该参数可以是一个普通的对象,也可以是一个 HttpEntity 对象。如果是一个普通对象,而非 HttpEntity 对象的时候,RestTemplate 会将请求对象转换为一个 HttpEntity 对象来处理。

postForObject 函数

该方法同 GET 请求中的 getForObject 类似,它的作用是简化 postForEntity 的后续处理。通过直接将请求响应的 body 内容包装成对象来返回使用。比如:

  1. RestTemplate restTemplate = new RestTemplate();
  2. User user = new User("zhangsan", 30);
  3. String postResult = restTemplate.postForObject("http://USER-SERVICE/user", user, String.class);

postForObject 函数也实现了三种不同的重载方法:

  • postForObject(String url, @Nullable Object request, Class responseType, Object… uriVariables)
  • postForObject(String url, @Nullable Object request, Class responseType, Map uriVariables)
  • postForObject(URI url, @Nullable Object request, Class responseType)

postForLocation 函数

该方法实现了以 POST 请求提交资源,并返回新资源的 URI,比如:

  1. User user = new User("zhangsan", 30);
  2. URI reponseURI = restTemplate.postForLocation("http://USER-SERVICE/user", user);

postForLocation 函数也实现了三种不同的重载方法:

  • postForLocation(String url, @Nullable Object request, Object… uriVariables)
  • postForLocation(String url, @Nullable Object request, Map uriVariables)
  • postForLocation(URI url, @Nullable Object request)

由于 postForLocation 函数会返回新资源的 URI,该 URI 就相当于指定了返回类型,所以此方法实现的 POST 请求不需要像 postForEntity 和 postForObject 那样指定 responseType。

PUT 请求

在 RestTemplate 中,对 PUT 请求可以通过 put 方法进行调用实现,比如:

  1. RestTemplate restTemplate = new RestTemplate();
  2. Long id = 10001L;
  3. User user = new User("zhangsan", 30);
  4. restTemplate.put("http://USER-SERVICE/user/{1}", user, id);

put 方法也实现了三种不同的重载方法:

  • put(String url, @Nullable Object request, Object… uriVariables)
  • put(String url, @Nullable Object request, Map uriVariables)
  • put(URI url, @Nullable Object request)

put 函数为 void 类型,所以没有返回内容,也就没有其他函数定义的 responseType 参数,除此之外的其他传入参数定义与用法与 postForObject 基本一致。

DELETE 请求

在 RestTemplate 中,对 DELETE 请求可以通过 delete 方法进行调用实现,比如:

  1. RestTemplate restTemplate = new RestTemplate();
  2. Long id = 10001L;
  3. restTemplate.put("http://USER-SERVICE/user/{1}", id);

delete 方法也实现了三种不同的重载方法:

  • delete(String url, Object… uriVariables)
  • delete(String url, Map uriVariables)
  • delete(URI url)

参数配置

对于 Ribbon 的参数配置通常有两种方式:全局配置以及指定客户端配置。

  • 全局配置:采用 ribbon.= 格式进行配置。其中, 代表了 Ribbon 客户端配置的参数名, 则代表了对应参数的值。比如,配置 Ribbon 创建连接的超时时间:
  1. ribbon.ConnectTimeout=250
  • 指定客户端配置:采用 .ribbon.= 格式进行配置。其中, 的含义同全局配置相同,而 代表了客户端的名称。比如,如果没有服务治理框架的帮助,我们需要为该客户端指定具体的实例清单,可以指定服务名来做详细的配置:
  1. hello-service.ribbon.listOfServers=localhost:8001,localhost:8002,localhost:8003

全局配置可以作为默认值进行设置,当指定客户端配置了相应的 key 的值时,将覆盖全局配置的内容。

重试机制

为什么需要使用重试机制

由于 Spring Cloud Eureka 实现的服务治理机制强调了 CAP 原理中的 AP(可用性与可靠性),在极端情况下它宁愿接受故障实例也不要丢掉。比如,当服务注册中心的网络发生故障断开时,Eureka 会因为超过 85%(默认)的实例丢失心跳而会触发保护机制,注册中心将会保留此时的所有节点。

不论是由于触发了保护机制还是服务剔除的延迟,引起服务调用到故障实例,都需要能够增强对这类问题的容错。所以,我们在服务调用的时候通常会加入重试机制。

重试配置

  1. spring.cloud.loadbalancer.retry.enabled=true
  2. hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
  3. hello-service.ribbon.ConnectTimeout=250
  4. hello-service.ribbon.ReadTimeout=1000
  5. hello-service.ribbon.OkToRetryOnAllOperations=true
  6. hello-service.ribbon.MaxAutoRetriesNextServer=2
  7. hello-service.ribbon.MaxAutoRetries=1

各项参数的配置说明如下所示。

  • spring.cloud.loadbalancer.retry.enabled:该参数用来开启重试机制,它默认是开启的。该参数的源码定义如下:
  1. @ConfigurationProperties("spring.cloud.loadbalancer.retry")
  2. public class LoadBalancerRetryProperties {
  3. private boolean enabled = true;
  4. ...
  5. }
  • hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds:断路器的超时时间需要大于 Ribbon 的超时时间,不然不会触发重试。
  • hello-service.ribbon.ConnectTimeout:请求连接的超时时间。
  • hello-service.ribbon.ReadTimeout:请求处理的超时时间。
  • hello-service.ribbon.OkToRetryOnAllOperations:对所有操作请求都进行重试。默认值为 false,只对 GET 请求进行重试。该参数的使用源码参考 RibbonLoadBalancedRetryPolicy#canRetry 方法。
  • hello-service.ribbon.MaxAutoRetriesNextServer:切换实例的重试次数,不包括首次调用。
  • hello-service.ribbon.MaxAutoRetries:对当前实例的重试次数,不包括首次调用。

Ribbon 重试需要引入 spring-retry

  1. <dependency>
  2. <groupId>org.springframework.retry</groupId>
  3. <artifactId>spring-retry</artifactId>
  4. </dependency>