Cpte-Boot/cpte-wms-service/FEIGN_USAGE.md

9.2 KiB
Raw Blame History

WMS 微服务 Feign 使用指南

一、OpenFeign 简介

OpenFeign 是一个声明式的 Web Service 客户端,它让微服务之间的调用变得更简单。 使用 Feign 只需要创建接口并在接口上添加注解即可,无需手动构建 HTTP 请求。

二、项目中的 Feign 配置

2.1 依赖配置

已在所有微服务的 pom.xml 中添加以下依赖:

<!-- OpenFeign 服务间调用 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>4.1.3</version>
</dependency>

<!-- OkHttp 作为 Feign 的 HTTP 客户端 -->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
    <version>13.5</version>
</dependency>

2.2 Feign Client 配置

配置类: org.cpte.feign.config.FeignClientConfiguration

主要配置项:

  • HTTP 客户端: 使用 OkHttp
  • 连接超时: 5 秒
  • 读取超时: 10 秒
  • 日志级别: BASIC可调整为 FULL 查看完整日志)
  • 重试机制: 最多重试 3 次

2.3 Fallback 降级处理

为每个 Feign Client 都配置了 FallbackFactory当服务调用失败时会执行降级逻辑

  • BasicServiceFallbackFactory - 基础服务降级
  • InventoryServiceFallbackFactory - 库存服务降级
  • ScheduleServiceFallbackFactory - 调度服务降级

三、已有的 Feign Client

3.1 基础服务 Client

接口: org.cpte.feign.client.BasicServiceClient

功能:

  • 获取物品信息(按 ID/编码)
  • 获取区域信息(按 ID/编码)
  • 获取点位信息(按 ID/编码)
  • 批量查询物品/点位列表

使用示例:

@Autowired
private BasicServiceClient basicServiceClient;

// 获取物品信息
Result<Map<String, Object>> result = basicServiceClient.getItemById("item123");

// 批量获取点位
List<String> pointCodes = Arrays.asList("P001", "P002", "P003");
Result<List<Map<String, Object>>> points = basicServiceClient.getPointByCodes(pointCodes);

3.2 库存服务 Client

接口: org.cpte.feign.client.InventoryServiceClient

功能:

  • 查询库存(单个/批量)
  • 增加库存
  • 扣减库存
  • 预占库存
  • 释放预占库存
  • 查询库存流水

使用示例:

@Autowired
private InventoryServiceClient inventoryServiceClient;

// 查询库存
Result<Map<String, Object>> stock = inventoryServiceClient.queryInventory("item123", "point456");

// 增加库存
Map<String, Object> params = new HashMap<>();
params.put("itemId", "item123");
params.put("quantity", 100);
Result<Boolean> result = inventoryServiceClient.increaseInventory(params);

3.3 调度服务 Client

接口: org.cpte.feign.client.ScheduleServiceClient

功能:

  • 创建 AGV 上架任务
  • 创建 AGV 下架任务
  • 创建 AGV 搬运任务
  • 查询任务状态
  • 取消任务
  • 等待任务完成

使用示例:

@Autowired
private ScheduleServiceClient scheduleServiceClient;

// 创建上架任务
Map<String, Object> params = new HashMap<>();
params.put("fromPoint", "RECEIVE_AREA");
params.put("toPoint", "POINT001");
params.put("taskId", "TASK_001");
Result<String> result = scheduleServiceClient.createPutTask(params);

// 等待任务完成
Result<Map<String, Object>> taskResult = scheduleServiceClient.waitTaskComplete("TASK_001", 300000L);

四、完整业务流程示例

4.1 入库流程

@Service
public class InboundService {
    
    @Autowired
    private BasicServiceClient basicServiceClient;
    
    @Autowired
    private InventoryServiceClient inventoryServiceClient;
    
    @Autowired
    private ScheduleServiceClient scheduleServiceClient;
    
    @Transactional(rollbackFor = Exception.class)
    public Result<String> inbound(String itemId, String pointId, Integer quantity) {
        // 1. 验证物品和库位
        Result<Map<String, Object>> itemResult = basicServiceClient.getItemById(itemId);
        if (!itemResult.isSuccess()) {
            return Result.error("物品不存在");
        }
        
        Result<Map<String, Object>> pointResult = basicServiceClient.getPointById(pointId);
        if (!pointResult.isSuccess()) {
            return Result.error("库位不存在");
        }
        
        // 2. 增加库存
        Map<String, Object> params = new HashMap<>();
        params.put("itemId", itemId);
        params.put("pointId", pointId);
        params.put("quantity", quantity);
        Result<Boolean> increaseResult = inventoryServiceClient.increaseInventory(params);
        if (!increaseResult.isSuccess()) {
            return Result.error("增加库存失败");
        }
        
        // 3. 创建 AGV 上架任务
        Map<String, Object> taskParams = new HashMap<>();
        taskParams.put("fromPoint", "RECEIVE_AREA");
        taskParams.put("toPoint", pointId);
        taskParams.put("taskId", "PUT_" + System.currentTimeMillis());
        taskParams.put("taskType", "PUT");
        Result<String> taskResult = scheduleServiceClient.createPutTask(taskParams);
        
        return Result.OK("入库成功", taskResult.getResult());
    }
}

4.2 出库流程

@Service
public class OutboundService {
    
    @Autowired
    private InventoryServiceClient inventoryServiceClient;
    
    @Autowired
    private ScheduleServiceClient scheduleServiceClient;
    
    @Transactional(rollbackFor = Exception.class)
    public Result<String> outbound(String itemId, String pointId, Integer quantity, String orderNo) {
        // 1. 预占库存
        Map<String, Object> reserveParams = new HashMap<>();
        reserveParams.put("itemId", itemId);
        reserveParams.put("pointId", pointId);
        reserveParams.put("quantity", quantity);
        reserveParams.put("orderNo", orderNo);
        Result<Boolean> reserveResult = inventoryServiceClient.reserveInventory(reserveParams);
        if (!reserveResult.isSuccess()) {
            return Result.error("预占库存失败");
        }
        
        // 2. 创建 AGV 下架任务
        Map<String, Object> taskParams = new HashMap<>();
        taskParams.put("fromPoint", pointId);
        taskParams.put("toPoint", "SHIPMENT_AREA");
        taskParams.put("taskId", "REMOVE_" + orderNo);
        taskParams.put("taskType", "REMOVE");
        Result<String> taskResult = scheduleServiceClient.createRemoveTask(taskParams);
        
        return Result.OK("出库成功", taskResult.getResult());
    }
}

五、配置说明

5.1 application.yml 配置

# Feign 配置
feign:
  client:
    config:
      default:
        connectTimeout: 5000    # 连接超时 5 秒
        readTimeout: 10000      # 读取超时 10 秒
        loggerLevel: BASIC      # 日志级别
  okhttp:
    enabled: true               # 启用 OkHttp
  compression:
    request:
      enabled: true             # 启用请求压缩
    response:
      enabled: true             # 启用响应压缩

# 服务地址配置K8s 环境使用服务名)
feign:
  client:
    wms-basic:
      url: http://wms-basic-service:80
    wms-inventory:
      url: http://wms-inventory-service:80
    wms-schedule:
      url: http://wms-schedule-service:80

5.2 日志级别调整

开发环境可调整为 FULL 查看详细请求响应:

logging:
  level:
    org.cpte.feign.client: DEBUG
feign:
  client:
    config:
      default:
        loggerLevel: FULL

六、注意事项

6.1 服务调用超时

  • 默认连接超时 5 秒,读取超时 10 秒
  • 对于耗时操作(如等待 AGV 任务),使用异步方式或增加超时时间
  • 建议设置合理的超时时间,避免线程阻塞

6.2 服务降级

  • 所有 Feign Client 都配置了 FallbackFactory
  • 降级处理会返回友好的错误信息
  • 生产环境建议实现更完善的降级策略(如返回缓存数据)

6.3 事务处理

  • 跨服务调用时,本地事务无法保证分布式一致性
  • 建议使用最终一致性方案(如消息队列、补偿机制)
  • 关键业务操作需要实现幂等性

6.4 性能优化

  • 启用 HTTP 压缩减少网络传输
  • 使用连接池提高连接复用率
  • 批量查询代替循环调用
  • 合理使用缓存减少服务调用

七、调试技巧

7.1 查看 Feign 日志

logging:
  level:
    org.cpte.feign.client: DEBUG
    feign: DEBUG

7.2 监控服务调用

  • 使用 Actuator 端点监控 HTTP 请求
  • 集成 Prometheus + Grafana 监控服务调用指标
  • 使用 SkyWalking 进行链路追踪

7.3 本地测试

本地开发时,可以通过 Host 映射或配置中心指定服务地址:

# 本地开发环境
feign:
  client:
    wms-basic:
      url: http://localhost:8001
    wms-inventory:
      url: http://localhost:8004
    wms-schedule:
      url: http://localhost:8005

八、最佳实践

  1. 统一响应格式: 所有服务接口返回统一的 Result 对象
  2. 参数校验: 在调用前校验参数,减少无效调用
  3. 批量操作: 优先使用批量接口,减少调用次数
  4. 超时控制: 根据业务场景设置合理的超时时间
  5. 降级策略: 实现完善的降级逻辑,提高系统可用性
  6. 幂等性: 关键操作实现幂等性,支持重试
  7. 链路追踪: 集成 SkyWalking 等工具,便于问题排查