Feign远程调用
在feign远程调用中,有以下概念:服务消费者、服务提供者、feign客户端。
1、基本使用
- 1、在服务消费者引入OpenFeign依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 2、在服务消费者的启动类上开启feign客户端
@SpringBootApplication
@EnableFeignClients(basePackages = "cn.itcast.demo.client") // 启用 Feign 客户端扫描
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
------------------------------------------------------
@EnableFeignClients的作用:
1、扫描项目中标记了 @FeignClient 的接口
2、为所有 @FeignClient 注解的接口创建动态代理实现类。这些代理类封装了 HTTP 请求的细节(如序列化、负载均衡、服务发现等)。
3、将生成的 Feign 客户端 Bean 注册到 Spring 容器中,后续可通过 @Autowired 直接注入使用。
- 3、新增一个服务定义 Feign 客户端接口
@FeignClient(name = "user-service") // 指定要调用的远程服务在注册中心的应用名称 (e.g., Eureka, Nacos)
public interface UserServiceClient {
// 定义要调用的远程接口方法,使用 Spring MVC 注解描述 HTTP 请求
// 示例1: GET 请求,路径参数
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id); // @PathVariable 映射路径参数
// 示例2: POST 请求,请求体
@PostMapping("/users")
User createUser(@RequestBody User user); // @RequestBody 映射请求体
}
- 4、在服务消费者中注入feign客户端,调用其接口里的方法
@Service
public class OrderService {
@Autowired
private UserServiceClient userServiceClient; // 注入 Feign 客户端 ★★★★★★★★
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.使用 Feign 客户端调用 user-service 的远程接口,就像调用本地方法一样!
User user = userServiceClient.findId(order.getUserId());
// 3.将user赋值给order对象
order.setUser(user);
// 4.返回
return order;
}
}
- 5、服务提供者user-service里就是定义相应的controller接收http请求
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/{id}")
public User queryById(@PathVariable("id") Long id,
@RequestHeader(value = "route",required = false) String route,
@RequestHeader(value = "Truth",required = false) String truth) {
System.out.println("route:"+route);
System.out.println("truth:"+truth);
return userService.queryById(id);
}
}
2、动态代理
结论1:@Autowired 注入的 UserServiceClient 实例,并不是你自己编写的那个接口的普通实现类。注入的实际上是代理对象。
Spring Cloud OpenFeign 在应用启动时(因为 @EnableFeignClients),会扫描带有 @FeignClient 注解的接口。Feign 的核心机制是为这个接口动态生成一个代理对象 (Proxy Object),并将这个代理对象注册为 Spring Bean。
结论2:当执行 userServiceClient.getUserById(request.getUserId()) 时,实际上是在调用这个代理对象的 getUserById 方法。当调用代理对象的 getUserById 方法时,InvocationHandler方法调用处理器的invoke方法会被触发,它的核心任务就是将你的方法调用翻译成一个具体的 HTTP 请求。
- 解析注解: 读取
@FeignClient(name="user-service")
确定目标服务名。读取方法上的@GetMapping("/users/{id}")
确定 HTTP 方法和路径模板。 - 构建 URL:
- 利用服务发现 (如 Eureka, Nacos) 根据
name="user-service"
查找可用的服务实例列表。 - 利用负载均衡器 (如 Ribbon 或 Spring Cloud LoadBalancer) 从列表中选出一个具体的实例地址 (e.g.,
http://192.168.1.100:8080
)。 - 将服务实例的基础 URL (
http://192.168.1.100:8080
) 和方法注解中的路径 (/users/{id}
) 拼接成完整的请求 URL (e.g.,http://192.168.1.100:8080/users/{id}
)。
- 利用服务发现 (如 Eureka, Nacos) 根据
- 处理参数:
- 解析方法参数 (
request.getUserId()
返回的值,假设是123
)。 - 根据
@PathVariable("id")
注解,将参数值123
填充到 URL 模板的{id}
位置,得到最终 URL:http://192.168.1.100:8080/users/123
。 - 如果有
@RequestParam
或@RequestBody
,处理器会相应地处理查询参数或构建请求体。
- 解析方法参数 (
- 设置 HTTP 细节:
- 确定 HTTP 方法为 GET。
- (可选) 设置必要的 HTTP Headers (如
Content-Type: application/json
,Accept: application/json
,或自定义 Header)。
- 发送 HTTP 请求: 使用 Feign 底层集成的 HTTP 客户端 (默认是 JDK 的
HttpURLConnection
,但常被替换为 Apache HttpClient 或 OkHttp) 向构建好的 URL (http://192.168.1.100:8080/users/123
) 发送 HTTP GET 请求。
结论3:接收并处理 HTTP 响应:将响应体 (JSON 字符串) 反序列化成 Java 对象。这个对象的类型就是你在接口方法中定义的返回类型 (User)。 处理器将这个 User 对象作为 getUserById 方法的返回值。代理对象将这个返回值传递回你的 OrderService 代码中。