在微服务架构中,服务间的通信是核心环节。一旦服务被注册到注册中心,我们就需要一种机制来发现并调用这些服务。同时,为了保证高可用和高性能,单个服务通常会部署多个实例,这就引入了负载均衡的需求。本章将深入探讨Spring Cloud中实现服务调用和负载均衡的两种主流方式:Ribbon和OpenFeign。
10.1 Ribbon 负载均衡
内容讲解
Ribbon 是Netflix开源的一款客户端负载均衡器。它工作在服务的消费方,可以让我们轻松地将服务请求分发到多个服务实例上。当与Eureka等注册中心结合使用时,Ribbon可以自动从注册中心获取服务实例列表,并根据指定的负载均衡策略将请求发送到其中一个实例。
核心概念:
- 客户端负载均衡:与Nginx等服务端负载均衡不同,Ribbon将负载均衡的逻辑实现在了客户端。客户端自己维护一份服务提供者的地址列表,并自己决定调用哪一个实例。
- 集成RestTemplate:Spring Cloud通过
@LoadBalanced
注解,对标准的RestTemplate
进行了增强。当RestTemplate
的Bean被标记上这个注解后,它发出的请求就会被Ribbon的拦截器拦截,从而实现客户端负载均衡。
注意:Spring Cloud 2020.0.x版本之后,官方推荐使用
Spring Cloud LoadBalancer
替代Ribbon。但Ribbon的设计思想和使用方式仍然非常重要,且在老项目中广泛存在。
代码示例
假设我们有一个order-service
需要调用user-service
。
1. 在order-service
中配置RestTemplate
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced // 开启客户端负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
2. 在order-service
中发起调用
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate;
public String getUserById(Long userId) {
// 直接使用服务名(user-service)代替硬编码的IP和端口
// Ribbon会自动将其解析为某个实例的实际地址
String url = "http://user-service/users/" + userId;
return restTemplate.getForObject(url, String.class);
}
}
10.2 OpenFeign 声明式调用
内容讲解
虽然RestTemplate
结合Ribbon可以实现服务调用,但手动拼接URL和处理HTTP请求的方式显得有些繁琐。OpenFeign 是Spring Cloud提供的一种声明式的、模板化的HTTP客户端。它允许我们像调用本地方法一样调用远程HTTP服务。
核心优势:
- 声明式:只需定义一个Java接口,并使用注解来描述HTTP请求(URL、请求方法、参数、头信息等)。
- 高度集成:无缝集成了Ribbon(或
Spring Cloud LoadBalancer
)实现客户端负载均衡,集成了Hystrix(或Resilience4J)实现服务容错。 - 代码优雅:将网络调用的细节完全屏蔽,让业务代码更专注于业务逻辑。
代码示例
1. 在order-service
中添加OpenFeign
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2. 在主启动类上开启Feign功能
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients // 开启Feign扫描
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
3. 创建一个Feign Client接口
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
// value/name 指定了要调用的服务名
@FeignClient(value = "user-service")
public interface UserServiceClient {
// 接口中的方法签名要与服务提供方的Controller方法保持一致
@GetMapping("/users/{id}")
String getUserById(@PathVariable("id") Long id);
}
4. 在OrderService
中注入并使用UserServiceClient
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Autowired
private UserServiceClient userServiceClient;
public String findUser(Long userId) {
// 像调用本地方法一样调用远程服务
return userServiceClient.getUserById(userId);
}
}
10.3 Feign 核心原理
内容讲解
OpenFeign的魔法在于它使用了动态代理。当我们启动应用并开启@EnableFeignClients
时,Spring Cloud会扫描所有被@FeignClient
注解标记的接口。对于每一个接口,它会通过JDK的动态代理(或CGLIB)技术创建一个代理对象,并将其注册到Spring的IoC容器中。当我们注入并调用这个接口的方法时,实际上是调用了这个代理对象的方法。
架构图
10.4 负载均衡策略
内容讲解
Ribbon内置了多种负载均衡策略,我们可以根据业务场景选择合适的策略。常见的策略有:
- RoundRobinRule (默认):轮询。按顺序循环选择服务实例。
- RandomRule:随机。从列表中随机选择一个实例。
- AvailabilityFilteringRule:可用性过滤。过滤掉连接失败和并发过高的实例,然后轮询。
- WeightedResponseTimeRule:加权响应时间。根据实例的平均响应时间计算权重,响应时间越短,权重越大,被选中的概率越高。
- BestAvailableRule:最佳可用。过滤掉故障实例后,选择并发量最小的实例。
我们可以通过Java代码或配置文件的方式来修改负载均衡策略。
代码示例 (配置文件方式)
在order-service
的application.yml
中配置,为user-service
指定负载均衡策略。
user-service: # 指定要配置的服务名
ribbon:
# 指定负载均衡策略的全限定类名
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
本章总结
本章我们掌握了微服务架构中服务调用的关键技术。我们学习了如何使用RestTemplate
结合@LoadBalanced
注解实现基础的客户端负载均衡,这背后是Ribbon在发挥作用。更重要的是,我们深入学习了声明式服务调用框架OpenFeign,它极大地简化了服务调用的编码工作,提高了开发效率。我们还探讨了Feign的动态代理原理以及如何配置不同的负载均衡策略。掌握了服务调用,我们就打通了微服务之间通信的“任督二脉”,为构建复杂的业务流程奠定了基础。