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

335 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# WMS 微服务 Feign 使用指南
## 一、OpenFeign 简介
OpenFeign 是一个声明式的 Web Service 客户端,它让微服务之间的调用变得更简单。
使用 Feign 只需要创建接口并在接口上添加注解即可,无需手动构建 HTTP 请求。
## 二、项目中的 Feign 配置
### 2.1 依赖配置
已在所有微服务的 `pom.xml` 中添加以下依赖:
```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/编码)
- 批量查询物品/点位列表
**使用示例**:
```java
@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`
**功能**:
- 查询库存(单个/批量)
- 增加库存
- 扣减库存
- 预占库存
- 释放预占库存
- 查询库存流水
**使用示例**:
```java
@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 搬运任务
- 查询任务状态
- 取消任务
- 等待任务完成
**使用示例**:
```java
@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 入库流程
```java
@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 出库流程
```java
@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 配置
```yaml
# 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 查看详细请求响应:
```yaml
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 日志
```yaml
logging:
level:
org.cpte.feign.client: DEBUG
feign: DEBUG
```
### 7.2 监控服务调用
- 使用 Actuator 端点监控 HTTP 请求
- 集成 Prometheus + Grafana 监控服务调用指标
- 使用 SkyWalking 进行链路追踪
### 7.3 本地测试
本地开发时,可以通过 Host 映射或配置中心指定服务地址:
```yaml
# 本地开发环境
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 等工具,便于问题排查