下载工作台
Spring Cloud 微服务

OpenFeign 与服务调用

试读上半部分 · 解锁后可读全文

第 4 章 · OpenFeign 声明式调用、超时与重试

本章目标:在 order-svc 中使用 OpenFeign 声明式调用 user-svcproduct-svc;理解 Feign 与 Spring Cloud LoadBalancer、Nacos 的协作;配置连接/读超时重试策略及错误解码;传递 Gateway 下发的 Header(如 X-Trace-Id);避免 Feign 常见坑(循环依赖、大报文、GET 幂等)。

学时建议:5~6 小时(含 2 小时下单链路联调)

前置spring-cloud-web ch01~ch03spring-boot-web ch07 REST 与 Result<T>;HTTP 客户端基础。


4.1 服务间调用为何用 OpenFeign

order-svc 创建订单时需要:

  1. 校验用户是否存在(user-svc
  2. 查询商品单价与上架状态(product-svc

若手写 RestTemplate/WebClient,每个 API 都要拼 URL、处理序列化、异常映射,冗长易错。

order-svc                          product-svc
   │                                    │
   │  Feign 接口 ProductClient          │
   │  @GetMapping("/api/v1/products/{id}")
   └──────────────────────────────────►│
方式优点缺点
RestTemplate简单、可控样板代码多
WebClient响应式、非阻塞学习曲线陡
OpenFeign接口 + 注解,接近本地调用需理解负载均衡与超时

4.2 依赖与启用

svc-order 模块:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

启动类:

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.example.svc.order.client")
public class OrderSvcApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderSvcApplication.class, args);
    }
}

4.3 定义 Feign Client 接口

4.3.1 调用 product-svc

@FeignClient(name = "product-svc", path = "/api/v1/products")
public interface ProductClient {

    @GetMapping("/{id}")
    Result<ProductDto> getById(@PathVariable("id") Long id);

    @GetMapping
    Result<PageResult<ProductDto>> list(
        @RequestParam("page") int page,
        @RequestParam("size") int size);
}

name 对应 Nacos 的 spring.application.name,Feign 通过 LoadBalancer 解析为 http://host:port

4.3.2 调用 user-svc

@FeignClient(name = "user-svc", path = "/api/v1/users")
public interface UserClient {

    @GetMapping("/{id}")
    Result<UserDto> getById(@PathVariable("id") Long id);
}

DTO 放在 svc-common 模块,字段与 ch12 单体保持一致,避免重复定义。


4.4 OrderService 编排示例

@Service
@RequiredArgsConstructor
@Slf4j
public class OrderCreateService {

    private final ProductClient productClient;
    private final UserClient userClient;
    private final OrderRepository orderRepository;

    @Transactional
    public OrderDto create(CreateOrderCommand cmd) {
        Result<UserDto> userRes = userClient.getById(cmd.getUserId());
        if (!userRes.isSuccess() || userRes.getData() == null) {
            throw new BusinessException(ErrorCode.USER_NOT_FOUND);
        }

        Result<ProductDto> productRes = productClient.getById(cmd.getProductId());
        ProductDto product = productRes.getData();
        if (product == null || !product.isOnSale()) {
            throw new BusinessException(ErrorCode.PRODUCT_UNAVAILABLE);
        }

        Order order = new Order();
        order.setUserId(cmd.getUserId());
        order.setProductId(cmd.getProductId());
        order.setAmount(product.getPrice().multiply(BigDecimal.valueOf(cmd.getQuantity())));
        order.setStatus(OrderStatus.PENDING_PAY);
        orderRepository.save(order);

        log.info("Order created id={} amount={}", order.getId(), order.getAmount());
        return OrderMapper.toDto(order);
    }
}

4.5 Feign 与 LoadBalancer 工作流程

ProductClient.getById(1)
        │
        ▼
Feign 动态代理
        │
        ▼
LoadBalancer 从 Nacos 取 product-svc 实例列表
        │
        ▼
选择实例 192.168.1.10:8082
        │
        ▼
HTTP GET http://192.168.1.10:8082/api/v1/products/1
组件职责
Nacos实例注册与推送
LoadBalancer客户端选择实例(轮询等)
FeignHTTP 编码、解码、接口映射

不要在 Feign URL 写死 http://127.0.0.1:8082,否则失去扩缩容能力。


4.6 超时配置

默认超时过短或过长都不合适,在 order-svc-dev.yaml

spring:
  cloud:
    openfeign:
      client:
        config:
          default:
            connectTimeout: 3000
            readTimeout: 5000
            loggerLevel: BASIC
          product-svc:
            readTimeout: 8000   # 商品搜索可略长
参数建议
connectTimeout3s 内应建立 TCP
readTimeout视下游 P99 延迟设定,通常 5~10s
全局 vs 单客户端核心路径单独调优

注意:读超时包含下游业务执行时间;过短导致误杀,过长占用线程。


4.7 重试策略

Feign 默认重试;需引入 spring-retry 并配置:

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>
spring:

以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。