接下来在整个微服务架构中,我们比较关心的就是服务间的服务改如何调用,有哪些调用方式?
服务通信技术
Spring框架提供的RestTemplate类可用于在应用中调用Rest服务,它简化了与HTTP服务的通信方式,统一了RESTful的标准,封装了HTTP链接,我们只需要传入URL及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。
点对点服务调用
仅仅使用RestTemplate进行服务调用。
/**
* 在users微服务中不通过服务注册中心指定调用products微服务中某一服务器暴露出来的接口
*/
@GetMapping("user/showProducts")
public String showProducts() {
RestTemplate restTemplate = new RestTemplate();
// 访问指定URL,获取响应信息
String result = restTemplate.getForObject("http://localhost:9001/product/showProducts", String.class);
log.info("result: [{}]", result);
return result;
}
存在的问题:
- 直接使用RestTemplate方式调用,而没有经过服务注册中心获取服务地址,当服务宕机时不能高效剔除。
- 代码写死,耦合度太高,不利于维护。
- 调用服务时没有负载均衡需要自己实现负载均衡策略。
负载均衡调用
使用RestTemplate+Ribbon进行服务调用。
1. Ribbon
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现。通过Spring Cloud的封装,可以让我们轻松地将面向服务的REST模版请求自动转换成客户端负载均衡的服务调用。
2. 使用Ribbon
-
引入依赖
如果注册中心使用的Consul,那么不需要引入依赖,因为Consul中已经继承了Ribbon。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
-
Ribbon使用方式
- DiscoveryClient
- LoadBalancerClient
- @LoadBalanced
-
DiscoveryClient + RestTemplate
@Autowired private DiscoveryClient discoveryClient; /** * 在users微服务中通过服务注册中心指定调用products微服务中某一服务器暴露出来的接口 * @return 响应信息 */ @GetMapping("user/showProductsByDiscoveryClient") public String showProductsByDiscoveryClient() { RestTemplate restTemplate = new RestTemplate(); String result = "result is null"; // 指定服务名,从服务注册中心获取该服务集群中当前所有可用服务器 List<ServiceInstance> instances = discoveryClient.getInstances("products"); for (ServiceInstance instance : instances) { // 获取每一个服务器的服务名 String host = instance.getHost(); // 获取每一个服务器的端口 int port = instance.getPort(); // 访问指定URL,获取响应信息 if ("localhost".equals(host) && port == 9001) { result = restTemplate.getForObject(instance.getUri() + "/product/showProducts", String.class); } } return result; }
-
LoadBalancerClient + RestTemplate
@Autowired private LoadBalancerClient loadBalancerClient; /** * 在users微服务中通过服务注册中心负载均衡调用products微服务中某一服务器暴露出来的接口 * @return 响应信息 */ @GetMapping("user/showProductsByLoadBalancerClient") public String showProductsByLoadBalancerClient() { RestTemplate restTemplate = new RestTemplate(); String result = "result is null"; // 指定服务名,从服务注册中心负载均衡地获取某一个可用服务器 ServiceInstance instance = loadBalancerClient.choose("products"); // 访问由负载均衡算法提供的URL,获取响应信息 result = restTemplate.getForObject(instance.getUri() + "/product/showProducts", String.class); return result; }
-
@LoadBalanced + RestTemplate(推荐)
创建RestTemplateConfig类
@Configuration public class RestTemplateConfig { /** * 构建能通过服务注册中心负载均衡调用服务器的RestTemplate * @return 实例 */ @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } }
Controller中使用
@Autowired private RestTemplate restTemplate; /** * 在users微服务中通过服务注册中心负载均衡调用products微服务中某一服务器暴露出来的接口 * @return 响应信息 */ @GetMapping("user/showProductsByLoadBalanced") public String showProductsByLoadBalanced() { String result = "result is null"; // 在URL中传递服务名,然后底层拆解URL获取服务名再使用LoadBalancerClient实现服务调用 result = restTemplate.getForObject("http://products/product/showProducts", String.class); return result; }
Ribbon负载均衡策略
1. 过程
当A服务集群中某一台服务器调用B服务集群中的某一台服务器的某一个接口时,A服务器会从服务注册中心拿到所有B服务中正常的服务器的列表,并将列表缓存到A服务器中。然后根据A服务器中的负载均衡策略从列表中选择一个服务器,再调用该服务器中的接口。
2. 常用负载均衡策略
- RoundRobinRule轮训策略(默认):按顺序循环选择。
- RandomRule随机策略:随机选择。
- AvailabilityFilteringRule可用过滤策略(常用):会先过滤由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数量超过阈值(临界值)的服务,然后对剩余的服务列表按照轮询策略进行访问。
- WeightedResponseTimeRule响应时间加权策略(常用):根据平均响应的时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高,刚启动时如果统计信息不足,则使用RoundRobinRule轮询策略。
- RetryRule重试策略:先按照RoundRobinRule的策略获取服务,如果获取失败则在指定时间内进行重试,获取可用的服务。
- BestAviableRule最低并发策略:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。
3. 修改服务负载均衡策略
调用方配置application.yml
服务id:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.策略名