【Feign】远程调用底层原理

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})。
  • 处理参数:
    • 解析方法参数 (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 代码中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值