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 的参数类型与上面的方法不同。这里使用了 Map 类型,所以使用该方法进行参数绑定时需要在占位符中制定 Map 中参数的 key 值,比如:uriVariables)
RestTemplate restTemplate = new RestTemplate();
Map<String, String> params = new HashMap<>();
param.put("name", "zhangsan");
ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://USER-SERVICE/user?name={name}",
String.class, params);
- getForEntity(URI url, Class
responseType) :该方法使用 URI 对象来替代之前的 url 和 urlVariables 参数来制定访问地址和参数绑定。URI 是 JDK java.net 包下的一个类,它表示一个统一的资源标识符引用。比如:
RestTemplate restTemplate = new RestTemplate();
UriComponents uriComponents = UriComponentsBuilder.fromUriString(
"http://USER-SERVICE/user?name={name}")
.build()
.expand("zhangsan")
.encode();
URI uri = uriComponents.toUri();
ResponseEntity<String> responseEntity = restTemplate.getForEntity(uri, String.class).getBody();
getForObject 函数
它与 getForEntity 函数类似,也提供了三种不同的重载实现,只不过请求直接返回包装好的对象内容。比如:
RestTemplate restTemplate = new RestTemplate();
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
RestTemplate restTemplate = new RestTemplate();
User user = new User("zhangsan", 30);
ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://USER-SERVICE/user", user, String.class);
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 内容包装成对象来返回使用。比如:
RestTemplate restTemplate = new RestTemplate();
User user = new User("zhangsan", 30);
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,比如:
User user = new User("zhangsan", 30);
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 方法进行调用实现,比如:
RestTemplate restTemplate = new RestTemplate();
Long id = 10001L;
User user = new User("zhangsan", 30);
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 方法进行调用实现,比如:
RestTemplate restTemplate = new RestTemplate();
Long id = 10001L;
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 创建连接的超时时间:
ribbon.ConnectTimeout=250
- 指定客户端配置:采用
.ribbon. = 格式进行配置。其中, 和 的含义同全局配置相同,而 代表了客户端的名称。比如,如果没有服务治理框架的帮助,我们需要为该客户端指定具体的实例清单,可以指定服务名来做详细的配置:
hello-service.ribbon.listOfServers=localhost:8001,localhost:8002,localhost:8003
全局配置可以作为默认值进行设置,当指定客户端配置了相应的 key 的值时,将覆盖全局配置的内容。
重试机制
为什么需要使用重试机制
由于 Spring Cloud Eureka 实现的服务治理机制强调了 CAP 原理中的 AP(可用性与可靠性),在极端情况下它宁愿接受故障实例也不要丢掉。比如,当服务注册中心的网络发生故障断开时,Eureka 会因为超过 85%(默认)的实例丢失心跳而会触发保护机制,注册中心将会保留此时的所有节点。
不论是由于触发了保护机制还是服务剔除的延迟,引起服务调用到故障实例,都需要能够增强对这类问题的容错。所以,我们在服务调用的时候通常会加入重试机制。
重试配置
spring.cloud.loadbalancer.retry.enabled=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
hello-service.ribbon.ConnectTimeout=250
hello-service.ribbon.ReadTimeout=1000
hello-service.ribbon.OkToRetryOnAllOperations=true
hello-service.ribbon.MaxAutoRetriesNextServer=2
hello-service.ribbon.MaxAutoRetries=1
各项参数的配置说明如下所示。
- spring.cloud.loadbalancer.retry.enabled:该参数用来开启重试机制,它默认是开启的。该参数的源码定义如下:
@ConfigurationProperties("spring.cloud.loadbalancer.retry")
public class LoadBalancerRetryProperties {
private boolean enabled = true;
...
}
- 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
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>