Appearance
Feign 接口定义指南
一、概述
在 PigX 微服务架构中,服务间通信使用 OpenFeign。本指南说明如何定义和使用 Feign 接口。
二、Feign 接口结构
模块划分
pigx-standard/
├── pigx-standard-api/ # Feign 接口定义(供其他服务调用)
│ └── src/main/java/com/pig4cloud/pigx/standard/
│ ├── entity/ # 实体类
│ ├── dto/ # 数据传输对象
│ ├── vo/ # 视图对象
│ └── feign/ # Feign 接口
│ ├── RemoteStandardUserService.java
│ └── factory/ # Feign 降级工厂
│ └── RemoteStandardUserServiceFallbackFactory.java
└── pigx-standard-biz/ # Feign 接口实现(Controller 提供)
└── src/main/java/com/pig4cloud/pigx/standard/
└── controller/
└── StandardUserController.java三、定义 Feign 接口
1. 在 pigx-standard-api 中定义接口
java
package com.pig4cloud.pigx.standard.feign;
import com.pig4cloud.pigx.common.core.constant.ServiceNameConstants;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.feign.annotation.FeignClient;
import com.pig4cloud.pigx.common.security.annotation.Inner;
import com.pig4cloud.pigx.standard.entity.StandardUser;
import com.pig4cloud.pigx.standard.feign.factory.RemoteStandardUserServiceFallbackFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* 用户服务远程调用接口
*
* @author pigx-standard
* @date 2025-12-16
*/
@FeignClient(
contextId = "remoteStandardUserService",
value = ServiceNameConstants.STANDARD_SERVICE,
fallbackFactory = RemoteStandardUserServiceFallbackFactory.class
)
public interface RemoteStandardUserService {
/**
* 根据用户ID获取用户信息(内部调用)
* @param id 用户ID
* @param from 调用来源(内部标识)
* @return 用户信息
*/
@Inner
@GetMapping("/user/inner/{id}")
R<StandardUser> getById(@PathVariable("id") Long id, @RequestHeader(SecurityConstants.FROM) String from);
/**
* 根据用户名获取用户信息(内部调用)
* @param username 用户名
* @param from 调用来源(内部标识)
* @return 用户信息
*/
@Inner
@GetMapping("/user/inner/username/{username}")
R<StandardUser> getByUsername(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM) String from);
/**
* 创建用户(内部调用)
* @param user 用户信息
* @param from 调用来源(内部标识)
* @return 创建结果
*/
@Inner
@PostMapping("/user/inner")
R<Boolean> save(@RequestBody StandardUser user, @RequestHeader(SecurityConstants.FROM) String from);
}关键点:
@FeignClient: 指定服务名和降级工厂contextId: 唯一标识,避免多个 Feign 接口冲突value: 服务名称(在 Nacos 中注册的名称)@Inner: 标记为内部接口,不需要用户认证@RequestHeader(SecurityConstants.FROM): 内部调用标识
2. 定义服务名称常量
在 pigx-common-core 或自定义常量类中:
java
package com.pig4cloud.pigx.common.core.constant;
/**
* 服务名称常量
*/
public interface ServiceNameConstants {
/**
* 认证服务
*/
String AUTH_SERVICE = "pigx-auth";
/**
* UPMS 服务
*/
String UPMS_SERVICE = "pigx-upms-biz";
/**
* Standard 服务
*/
String STANDARD_SERVICE = "pigx-standard-biz";
}3. 定义降级工厂
java
package com.pig4cloud.pigx.standard.feign.factory;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.standard.entity.StandardUser;
import com.pig4cloud.pigx.standard.feign.RemoteStandardUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.openfeign.FallbackFactory;
import org.springframework.stereotype.Component;
/**
* 用户服务降级工厂
*
* @author pigx-standard
* @date 2025-12-16
*/
@Slf4j
@Component
public class RemoteStandardUserServiceFallbackFactory implements FallbackFactory<RemoteStandardUserService> {
@Override
public RemoteStandardUserService create(Throwable throwable) {
log.error("用户服务调用失败", throwable);
return new RemoteStandardUserService() {
@Override
public R<StandardUser> getById(Long id, String from) {
log.error("根据ID获取用户失败: id={}, 错误={}", id, throwable.getMessage());
return R.failed("获取用户信息失败: " + throwable.getMessage());
}
@Override
public R<StandardUser> getByUsername(String username, String from) {
log.error("根据用户名获取用户失败: username={}, 错误={}", username, throwable.getMessage());
return R.failed("获取用户信息失败: " + throwable.getMessage());
}
@Override
public R<Boolean> save(StandardUser user, String from) {
log.error("创建用户失败: user={}, 错误={}", user, throwable.getMessage());
return R.failed("创建用户失败: " + throwable.getMessage());
}
};
}
}降级说明:
- 当远程服务不可用时,自动触发降级
- 记录错误日志
- 返回友好的错误信息
四、实现 Feign 接口(控制器)
在 pigx-standard-biz 中提供实现
java
package com.pig4cloud.pigx.standard.controller;
import com.pig4cloud.pigx.common.core.util.R;
import com.pig4cloud.pigx.common.security.annotation.Inner;
import com.pig4cloud.pigx.standard.entity.StandardUser;
import com.pig4cloud.pigx.standard.service.StandardUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
/**
* 用户管理控制器
*
* @author pigx-standard
* @date 2025-12-16
*/
@RestController
@RequestMapping("/user")
@RequiredArgsConstructor
public class StandardUserController {
private final StandardUserService userService;
// ==================== 内部接口(Feign 调用) ====================
/**
* 根据用户ID获取用户信息(内部调用)
*/
@Inner
@GetMapping("/inner/{id}")
public R<StandardUser> getById(@PathVariable Long id) {
return R.ok(userService.getById(id));
}
/**
* 根据用户名获取用户信息(内部调用)
*/
@Inner
@GetMapping("/inner/username/{username}")
public R<StandardUser> getByUsername(@PathVariable String username) {
return R.ok(userService.getByUsername(username));
}
/**
* 创建用户(内部调用)
*/
@Inner
@PostMapping("/inner")
public R<Boolean> saveUser(@RequestBody StandardUser user) {
return R.ok(userService.save(user));
}
// ==================== 外部接口(需要权限) ====================
/**
* 根据ID查询用户(需要权限)
*/
@GetMapping("/{id}")
@PreAuthorize("@pms.hasPermission('standard_user_view')")
public R<StandardUser> getUserById(@PathVariable Long id) {
return R.ok(userService.getById(id));
}
// ... 其他外部接口
}接口划分:
/user/inner/*: 内部接口,添加@Inner注解,不需要权限验证/user/*: 外部接口,需要权限验证
五、使用 Feign 接口
1. 在其他服务中使用
添加依赖(在调用方服务的 pom.xml):
xml
<dependency>
<groupId>com.pig4cloud</groupId>
<artifactId>pigx-standard-api</artifactId>
<version>5.8.0</version>
</dependency>注入并使用:
java
package com.pig4cloud.pigx.audit.service.impl;
import com.pig4cloud.pigx.standard.feign.RemoteStandardUserService;
import com.pig4cloud.pigx.standard.entity.StandardUser;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
import com.pig4cloud.pigx.common.core.util.R;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class AuditServiceImpl {
private final RemoteStandardUserService remoteStandardUserService;
public void doSomething() {
// 调用 Standard 服务获取用户信息
R<StandardUser> result = remoteStandardUserService.getById(
1L,
SecurityConstants.FROM_IN // 内部调用标识
);
if (result.isSuccess()) {
StandardUser user = result.getData();
// 处理用户信息
} else {
// 处理失败情况
log.error("获取用户信息失败: {}", result.getMsg());
}
}
}2. 启动类配置
确保启动类已添加 @EnablePigxFeignClients:
java
@EnablePigxFeignClients // 扫描 Feign 客户端
@SpringBootApplication
public class PigxAuditApplication {
public static void main(String[] args) {
SpringApplication.run(PigxAuditApplication.class, args);
}
}六、Feign 配置
1. 超时配置
在 application.yml 或 Nacos 配置:
yaml
feign:
client:
config:
default:
connectTimeout: 5000 # 连接超时(毫秒)
readTimeout: 10000 # 读取超时(毫秒)
pigx-standard-biz: # 针对特定服务的配置
connectTimeout: 3000
readTimeout: 50002. 重试配置
yaml
feign:
client:
config:
default:
retryer: com.pig4cloud.pigx.common.feign.FeignRetryerConfiguration3. 日志配置
yaml
feign:
client:
config:
default:
loggerLevel: FULL # NONE, BASIC, HEADERS, FULL在 logback-spring.xml 中:
xml
<!-- Feign 日志 -->
<logger name="com.pig4cloud.pigx.standard.feign" level="DEBUG"/>七、最佳实践
1. 接口设计原则
java
// ✅ 好的设计:参数明确,返回类型清晰
@GetMapping("/user/inner/{id}")
R<StandardUser> getById(@PathVariable("id") Long id, @RequestHeader(SecurityConstants.FROM) String from);
// ❌ 不好的设计:参数模糊,容易出错
@GetMapping("/user")
R<StandardUser> getUser(@RequestParam Map<String, Object> params);2. DTO 使用
对于复杂的返回结果,使用 DTO:
java
// 定义 DTO
@Data
public class StandardUserDTO {
private Long id;
private String username;
private String realName;
private String deptName;
// 只包含需要的字段
}
// Feign 接口
@GetMapping("/user/inner/detail/{id}")
R<StandardUserDTO> getUserDetail(@PathVariable("id") Long id, @RequestHeader(SecurityConstants.FROM) String from);3. 批量查询
java
// Feign 接口
@PostMapping("/user/inner/batch")
R<List<StandardUser>> listByIds(@RequestBody List<Long> ids, @RequestHeader(SecurityConstants.FROM) String from);
// Controller 实现
@Inner
@PostMapping("/inner/batch")
public R<List<StandardUser>> listByIds(@RequestBody List<Long> ids) {
return R.ok(userService.listByIds(ids));
}4. 异常处理
java
// 在 Service 中统一处理 Feign 调用
public StandardUser getUserById(Long id) {
R<StandardUser> result = remoteStandardUserService.getById(id, SecurityConstants.FROM_IN);
if (!result.isSuccess()) {
throw new BusinessException("获取用户信息失败: " + result.getMsg());
}
return result.getData();
}八、调试技巧
1. 查看 Feign 请求日志
yaml
# application.yml
logging:
level:
com.pig4cloud.pigx.standard.feign: DEBUG2. 使用 Actuator 查看 Feign 信息
bash
# 查看 Feign 客户端列表
curl http://localhost:6064/actuator/beans | grep FeignClient3. 模拟 Feign 调用
bash
# 直接调用内部接口(需要添加内部调用标识)
curl -X GET http://localhost:6064/user/inner/1 \
-H "from: inner"九、常见问题
Q1: Feign 调用返回 404
原因:
- Controller 路径不匹配
- Feign 接口路径定义错误
- 服务未启动或未注册
解决:
bash
# 1. 检查服务是否注册
curl http://localhost:8848/nacos/v1/ns/instance/list?serviceName=pigx-standard-biz
# 2. 检查 Controller 路径
# Feign: @GetMapping("/user/inner/{id}")
# Controller: @GetMapping("/inner/{id}") + @RequestMapping("/user")
# 实际路径: /user/inner/{id} ✅Q2: Feign 调用超时
原因:
- 被调用服务响应慢
- 网络问题
- 超时配置过短
解决:
yaml
feign:
client:
config:
pigx-standard-biz:
connectTimeout: 10000
readTimeout: 30000Q3: 降级不生效
原因:
- 未配置降级工厂
- Hystrix/Sentinel 未启用
解决:
yaml
feign:
circuitbreaker:
enabled: trueQ4: @Inner 注解不生效
原因:
- 未添加
from请求头 - Security 配置问题
解决:
java
// 确保调用时传入 from 参数
remoteStandardUserService.getById(id, SecurityConstants.FROM_IN);十、检查清单
- [ ] Feign 接口已定义在 pigx-standard-api 模块
- [ ] 添加了
@FeignClient注解 - [ ] 定义了降级工厂
- [ ] Controller 提供了对应的实现
- [ ] 添加了
@Inner注解 - [ ] 启动类添加了
@EnablePigxFeignClients - [ ] 调用方已添加 pigx-standard-api 依赖
- [ ] Feign 配置正确(超时、重试等)
- [ ] 日志配置正确
- [ ] 测试调用成功
文档版本: v1.0 创建时间: 2025-12-16 最后更新: 2025-12-16