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