Skip to content

RPC、服务调用与 OpenFeign

系统一旦从单体走向多服务,马上就会碰到一个非常现实的问题:一个服务怎么调用另一个服务。

这就是服务调用主线。

很多人一提到 RPC,又会马上联想到 Feign,但这两个词并不是完全等价的。

这篇文章重点讲:

  1. RPC 到底是什么
  2. HTTP 调用和 RPC 调用是什么关系
  3. OpenFeign 在 Spring Cloud 里扮演什么角色
  4. 它解决什么问题
  5. 服务调用里真正要注意的工程边界是什么

1. RPC 到底是什么

RPCRemote Procedure Call,即远程过程调用。

直接看成:让你像调用本地方法一样去发起远程服务调用。

这里最重要的不是“像方法调用”,而是:一次调用跨了进程、跨了机器、跨了网络。

所以 RPC 真正解决的是:服务和服务之间如何建立一套更稳定、更统一的调用模型。


2. 为什么服务调用不能简单理解成“发个 HTTP 请求”

当然,很多服务调用底层确实是 HTTP。

但企业开发真正要处理的,不只是“能不能打通”,还包括:

  1. 服务地址怎么发现
  2. 调用接口怎么声明
  3. 超时怎么控制
  4. 失败怎么重试或熔断
  5. 请求头、鉴权、链路信息怎么透传

所以服务调用主线更适合直接记成:远程调用 = 协议 + 发现 + 序列化 + 超时 + 容错 + 治理。


3. RPC 和 HTTP 是什么关系

这部分特别容易混。

更稳妥的理解是:

  1. RPC 是一种调用模型
  2. HTTP 是一种传输协议

RPC 可以基于 HTTP 实现,也可以基于其他协议实现。

所以不要把 “RPC” 和 “HTTP” 讲成非此即彼的关系。


4. OpenFeign 到底是什么

OpenFeign 可以理解成 Spring Cloud 生态里一种声明式的 HTTP 客户端

它最核心的价值是:把远程 HTTP 调用从手写请求代码,变成接口声明式调用。

你可以直接把它理解成:用接口描述远程服务,再由框架帮你生成调用实现。


5. 为什么很多项目喜欢用 Feign

如果不用 Feign,服务调用代码经常会散成这样:

  1. 手动拼 URL
  2. 手动处理请求头
  3. 手动序列化和反序列化
  4. 每个调用方都自己写一套 HTTP 模板代码

而 Feign 解决的是:把远程服务调用接口化、声明化。


6. 一个最基本的 Feign 示例

例如订单服务要调用库存服务:

java
/**
 * 声明库存服务的远程调用客户端。
 */
@FeignClient(name = "stock-service")
public interface StockClient {

    /**
     * 调用库存服务执行扣减。
     *
     * @param request 扣减库存请求体,包含商品 ID 和扣减数量
     */
    @PostMapping("/stocks/deduct")
    void deduct(@RequestBody DeductStockRequest request);
}

业务层里可以直接注入:

java
/**
 * 下单服务示例,演示业务层如何通过 Feign 客户端发起远程调用。
 */
@Service
public class OrderService {

    private final StockClient stockClient;

    /**
     * 注入库存服务客户端。
     *
     * @param stockClient 用于调用库存服务的 Feign 客户端
     */
    public OrderService(StockClient stockClient) {
        this.stockClient = stockClient;
    }

    /**
     * 创建订单,并同步通知库存服务扣减库存。
     *
     * @param command 下单命令,包含商品和数量等信息
     */
    public void createOrder(CreateOrderCommand command) {
        stockClient.deduct(new DeductStockRequest(command.getProductId(), command.getCount()));
    }
}

这个例子最想表达的是:调用方不再自己拼 HTTP 请求,而是通过接口方法表达远程调用。


7. @FeignClient 到底在做什么

@FeignClient 是 Feign 主线里最核心的注解。

它解决的是 告诉 Spring 这不是一个普通接口,而是一个远程服务调用接口

常见属性包括:

属性作用
name服务名
url直接指定调用地址
path统一前缀路径
configuration指定自定义配置
fallback / fallbackFactory指定降级处理

例如:

java
/**
 * 声明用户服务的远程调用客户端,并指定统一路径和降级实现。
 */
@FeignClient(
        name = "user-service",
        path = "/users",
        fallback = UserClientFallback.class)
public interface UserClient {

    /**
     * 按用户 ID 查询用户信息。
     *
     * @param id 用户主键 ID
     * @return 查询到的用户信息
     */
    @GetMapping("/{id}")
    UserDTO getById(@PathVariable Long id);
}

8. Feign 为什么经常和注册发现一起出现

因为在微服务环境里,调用方通常不会写死一个 IP 地址。

更常见的做法是:按服务名发起调用,再由注册发现体系找到可用实例。

所以 Feign 和注册中心经常配套出现,本质原因是:

  1. Feign 解决“怎么声明调用”
  2. 注册发现解决“服务实例在哪”

9. Feign 只是“更优雅的 HTTP”吗

从表面上看是这样,但工程上还不止。

因为 Feign 常常还会和下面这些能力协同:

  1. 负载均衡
  2. 超时配置
  3. 重试
  4. 熔断和降级
  5. 请求拦截器
  6. 日志和链路透传

所以更适合直接记成:Feign 是声明式服务调用入口,但真正可用的企业级调用链,还需要治理能力一起配合。


10. 常见注解和扩展点

注解 / 组件作用
@EnableFeignClients开启 Feign 客户端扫描
@FeignClient标记远程调用接口
RequestInterceptor统一拦截请求,透传 token / traceId
fallback指定调用失败时的兜底实现

例如启动类:

java
/**
 * 开启 Feign 客户端扫描的应用启动类。
 */
@SpringBootApplication
@EnableFeignClients
public class OrderApplication {
}

例如统一透传请求头:

java
/**
 * Feign 调用链上的公共配置。
 */
@Configuration
public class FeignConfig {

    /**
     * 为每次 Feign 调用统一追加请求头。
     *
     * @return 一个会向请求模板写入来源标识的拦截器
     */
    @Bean
    public RequestInterceptor requestInterceptor() {
        return template -> template.header("X-Source", "order-service");
    }
}

11. fallback 到底在解决什么问题

远程调用一定要接受一个现实:下游服务可能随时失败、超时或不可用。

这时 fallback 解决的是:当远程调用失败时,系统有没有一个可控的兜底行为。

例如:

java
/**
 * 用户服务调用失败时的降级实现。
 */
@Component
public class UserClientFallback implements UserClient {

    /**
     * 当远程调用失败时,返回一个兜底的用户对象。
     *
     * @param id 用户主键 ID
     * @return 降级后的默认用户信息
     */
    @Override
    public UserDTO getById(Long id) {
        return new UserDTO(id, "unknown");
    }
}

但要注意:fallback 不是让你把失败隐藏掉,而是让失败行为更可控。


12. RPC / Feign 这条线真正容易出问题的地方是什么

真正的问题通常不是“接口能不能调通”,而是:

  1. 超时时间设得不合理
  2. 重试把下游打得更重
  3. 失败没有隔离,故障扩散
  4. DTO 设计混乱,服务边界模糊
  5. 把同步调用链拉得太长

所以企业里要注意的是:远程调用最难的部分不是语法,而是边界、超时、容错和依赖关系设计。


13. RPC、Feign 和 Dubbo 这类框架怎么区分

这部分也很常被问到。

可以直接这样理解:

  1. RPC 是大类概念
  2. Feign 更偏 Spring Cloud 里的声明式 HTTP 调用
  3. Dubbo 更偏专门的 RPC 框架体系

Feign 是企业微服务里很常见的一种调用方式,但它不等于整个 RPC 世界。


14. 工程上最容易混的几个边界

14.1 误区一:Feign 就是 RPC 的全部

不是。

Feign 只是服务调用体系里一种常见实现方式。

14.2 误区二:服务调用成功率低,只要加重试就行

不一定。

下游已经过载时,重试反而可能放大问题。

14.3 误区三:远程调用看起来像本地方法,就可以按本地方法心态设计

这是非常危险的。

远程调用的本质始终是:有网络、有序列化、有超时、有失败。


15. 一句话总结

RPC / 服务调用这条线,本质上是在解决多服务之间如何建立统一、可治理的远程调用模型;而 OpenFeign 在 Spring Cloud 里最重要的价值,是把 HTTP 服务调用声明式化,但真正要把它用好,仍然要把注册发现、超时、容错和调用边界一起设计好。

基于 VitePress 构建的个人技术笔记。