Skip to content
Home
Go back

Spring Cloud OpenFeign:让远程调用像本地方法

回顾:RestTemplate 的痛点

上篇做完负载均衡,order-service 调用 payment-service 的代码长这样:

@RestController
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/order")
    public String createOrder() {
        String result = restTemplate.getForObject(
            "http://payment-service/pay", String.class);
        return "Order created. " + result;
    }
}

能跑,但有两个问题:

这就是 OpenFeign 要解决的问题。

OpenFeign 是什么

OpenFeign 是一个声明式 HTTP 客户端。说人话就是:你只需要定义一个 Java 接口、加上注解说明要调哪个服务、哪个路径,Feign 自动帮你生成实现类去发 HTTP 请求。

它不是一种新的通信协议,底层还是 HTTP。它做的事情是消除模板代码——把「如何调用」的细节交给框架,你只关心「要调什么」。

它的体现形式就是接口 + 注解:

@FeignClient(name = "payment-service")
public interface PaymentClient {

    @GetMapping("/pay")
    String pay();
}

三条信息就够:

Feign 在背后做的事:拿服务名去 Nacos 查实例列表 → 负载均衡选一台 → 构造 HTTP 请求 → 发出去 → 把响应转成你声明的返回类型。

代码对比

同一个调用,两种写法并排看:

RestTemplate 版:

@Autowired
private RestTemplate restTemplate;

public void doSomething() {
    String result = restTemplate.getForObject(
        "http://payment-service/pay", String.class);
    // 处理 result...
}

OpenFeign 版:

// 定义接口
@FeignClient(name = "payment-service")
public interface PaymentClient {
    @GetMapping("/pay")
    String pay();
}

// 使用:直接注入,像本地方法一样调用
@Autowired
private PaymentClient paymentClient;

public void doSomething() {
    String result = paymentClient.pay();
    // 处理 result...
}

差别一目了然:

维度RestTemplateOpenFeign
调用方式字符串拼 URL + 类型强转接口方法调用
编译检查路径拼错运行时才发现接口签名由编译器检查
代码量每次调用都要写模板只定义一次接口
可读性HTTP 细节淹没业务逻辑业务逻辑清晰可见

动手:改造 order-service

引入依赖

order-servicepom.xml 中加入:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

启动类上添加 @EnableFeignClients

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

定义 Feign 接口

@FeignClient(name = "payment-service")
public interface PaymentClient {

    @GetMapping("/pay")
    String pay();
}

替换 Controller 代码

@RestController
public class OrderController {

    @Autowired
    private PaymentClient paymentClient;

    @GetMapping("/order")
    public String createOrder() {
        String result = paymentClient.pay();
        return "Order created. " + result;
    }
}

RestTemplate@LoadBalanced 相关代码可以全删了。

Feign 与负载均衡

你可能注意到了:@FeignClient(name = "payment-service") 里写的是服务名,没有写端口。Feign 怎么知道往哪发?

答案:Feign 内部集成了负载均衡能力。它去 Nacos 查询 payment-service 的实例列表,然后走 Spring Cloud LoadBalancer 选出一个实例来调用。之前我们给 RestTemplate 配的 @LoadBalanced,在 Feign 这边什么额外配置都不需要,天然支持。

这也是组件之间协同设计的体现——注册中心、负载均衡、声明式调用三者无缝衔接,你只关注业务接口的定义。

小结与下篇预告

这一篇我们完成了从 RestTemplate 到 OpenFeign 的升级:

  1. 理解 OpenFeign 的本质——声明式 HTTP 客户端,不是新协议,是语法糖
  2. 用接口 + 注解代替字符串拼 URL,编译器帮你检查正确性
  3. 同屏对比两种写法,直观感受代码量的差异
  4. Feign 自动继承负载均衡,不需要额外配置

现在服务间调用已经足够干净。但你打开各个服务的 application.yml 看看:每个服务里都散落着 Nacos 地址、端口号等配置。当服务数量多起来,这些分散的配置会变成管理噩梦。

下一篇,我们引入 Nacos 配置中心,把配置统一管起来。



Previous Post
Spring Cloud Nacos 配置中心:改了配置不必重启
Next Post
Spring Cloud 负载均衡:当一个服务有很多实例