回顾: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;
}
}
能跑,但有两个问题:
- 拼接 URL 容易出错。
"http://payment-service/pay"是一个魔法字符串,如果接口路径改了,编译器不会提醒你 - 模板代码重复。每个调用方都要写同一套
restTemplate.getForObject(url, Xxx.class),业务逻辑淹没在 HTTP 调用细节里
这就是 OpenFeign 要解决的问题。
OpenFeign 是什么
OpenFeign 是一个声明式 HTTP 客户端。说人话就是:你只需要定义一个 Java 接口、加上注解说明要调哪个服务、哪个路径,Feign 自动帮你生成实现类去发 HTTP 请求。
它不是一种新的通信协议,底层还是 HTTP。它做的事情是消除模板代码——把「如何调用」的细节交给框架,你只关心「要调什么」。
它的体现形式就是接口 + 注解:
@FeignClient(name = "payment-service")
public interface PaymentClient {
@GetMapping("/pay")
String pay();
}
三条信息就够:
@FeignClient(name = "payment-service")— 告诉 Feign 去 Nacos 找名为payment-service的服务@GetMapping("/pay")— 告诉 Feign 调用哪个路径- 方法签名
String pay()— 告诉 Feign 返回值类型
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...
}
差别一目了然:
| 维度 | RestTemplate | OpenFeign |
|---|---|---|
| 调用方式 | 字符串拼 URL + 类型强转 | 接口方法调用 |
| 编译检查 | 路径拼错运行时才发现 | 接口签名由编译器检查 |
| 代码量 | 每次调用都要写模板 | 只定义一次接口 |
| 可读性 | HTTP 细节淹没业务逻辑 | 业务逻辑清晰可见 |
动手:改造 order-service
引入依赖
在 order-service 的 pom.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 的升级:
- 理解 OpenFeign 的本质——声明式 HTTP 客户端,不是新协议,是语法糖
- 用接口 + 注解代替字符串拼 URL,编译器帮你检查正确性
- 同屏对比两种写法,直观感受代码量的差异
- Feign 自动继承负载均衡,不需要额外配置
现在服务间调用已经足够干净。但你打开各个服务的 application.yml 看看:每个服务里都散落着 Nacos 地址、端口号等配置。当服务数量多起来,这些分散的配置会变成管理噩梦。
下一篇,我们引入 Nacos 配置中心,把配置统一管起来。