介绍
feign是声明式的web service客户端,其实就是Http client 的封装,它是为了解决服务间调用的问题而使用的。
verstion: 用于指定版本,经实战验证下来不是太好用,因为一个工程中可能会有多个版本并存,这一个改就全改了,不再建议使用动态变理,可以直接编码进去
参数配置
在应用的相应配置文件(如: api-config.yaml)中添加自己的版本信息及使用服务的版本信息
api:
# 当前服务工程路径及版本,用于其他模块调用时配置
path: api
version: v1
# 引用的其他服务路径及版本
service:
operate: # 声明服务标识
name: pai-operate # 用于解决服务名变化
url: # 用于本地开发时直接指定,如: http://localhost:8080
path: api # 用于配合contextPath配置
version: v1 # 用于指定版本,经实战验证下来不是太好用,因为一个工程中可能会有多个版本并存,这一个改就全改了,不再建议使用动态变理,可以直接编码进去
SpringBoot Application启动类引用配置,并启用FeignClient
@EnablePaiFeignClients("cn.flyrise.*")
@PropertySource(value = {"classpath:api-config.yaml"}, factory = YamlPropertySourceFactory.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
feign使用示例
SpringBoot启动类添加注解@EnableFeignClients
添加接口声明API参数及路径
@FeignClient(
name = "pai-console",
contextId = "userInfoApi",
url = "${api.service.console.url:}",
fallbackFactory = RemoteUserInfoFallbackFactory.class
)
public interface IUserService {
/**
* 通过用户id获取用户信息
*
* @param id 用户id
* @return
*/
@GetMapping("${api.service.console.path:}/user/${api.service.console.version:v1}/info/{id}")
Reply<UserEntity> findUserById(@PathVariable("id") String id);
/**
* 通过用户id获取用户信息-无鉴权
*
* @param id 用户id
* @param from 内部调用凭证
* @return
*/
@GetMapping("${api.service.console.path:}/user/${api.service.console.version:v1}/inner/info/{id}")
Reply<UserEntity> findUserByIdNoAuth(@PathVariable("id") String id, @RequestHeader("from") String from);
/**
* 通过多个用户id获取多个用户信息-无鉴权
*
* @param ids 用户id集合
* @param from 内部调用凭证
* @return
*/
@PostMapping("${api.service.console.path:}/user/${api.service.console.version:v1}/inner/info/batch")
Reply<List<UserEntity>> findUserByIdsNoAuth(@RequestBody List<String> ids, @RequestHeader("from") String from);
/**
* 通过多个用户id获取多个用户信息
*
* @param ids 用户id集合
* @return
*/
@PostMapping("${api.service.console.path:}/user/${api.service.console.version:v1}/info/batch")
Reply<List<UserEntity>> findUserByIds(@RequestBody List<String> ids);
/**
* 当前登录用户的基本信息
*
* @return
*/
@GetMapping("${api.service.console.path:}/user/${api.service.console.version:v1}/info/me")
Reply<UserEntity> findMyInfo();
/**
* 根据用户id和企业entId获取用户权限
*
* @param userId 用户id
* @param entId 企业id
* @return
*/
@GetMapping("${api.service.console.path:}/user/${api.service.console.version:v1}/permission")
Reply<RoleAndPermEntity> findUserPermByEntId(@RequestParam("userId") String userId,
@RequestParam("entId") String entId
);
/**
* 获取简单的用户信息
*
* @param id 用户id
* @return
*/
@GetMapping("${api.service.console.path:}/user/${api.service.console.version:v1}/info/{id}")
Reply<SimpleUserModel> findSimpleUserById(@PathVariable("id") String id);
/**
* 获取简单的用户信息(多个)
*
* @param ids 用户id集合
* @return
*/
@PostMapping("${api.service.console.path:}/user/${api.service.console.version:v1}/info/batch")
Reply<List<SimpleUserModel>> findSimpleUserByIds(@RequestBody List<String> ids);
/**
* 根据用户id集合获取用户名
*
* @param ids 用户id集合
* @return
*/
@PostMapping("${api.service.console.path:}/user/${api.service.console.version:v1}/username/simple/batch")
Reply<Map<String, String>> getUsernames(@RequestBody List<String> ids);
/**
* 根据用户id集合获取用户昵称
*
* @param ids 用户id集合
* @return
*/
@PostMapping("${api.service.console.path:}/user/${api.service.console.version:v1}/nickName/simple/batch")
Reply<Map<String, String>> getNickNames(@RequestBody List<String> ids);
/**
* 获取用户信息 Map<String, UserEntity>
*
* @param ids 用户id集合
* @return
*/
@PostMapping("${api.service.console.path:}/user/${api.service.console.version:v1}/info/simple/batch")
Reply<Map<String, UserEntity>> getUserMapEntity(@RequestBody List<String> ids);
/**
* 根据企业id获取用户id集合(模糊查询)
*
* @param entId 企业id
* @param search 搜索条件(用户昵称)
* @return
*/
@GetMapping("${api.service.console.path:}/user/${api.service.console.version:v1}/id/list")
Reply<List<String>> getUserIdsByEntId(@RequestParam("entId") String entId, @RequestParam(value = "search", required = false) String search);
/**
* 根据用户昵称获取用户id集合(模糊查询)
*
* @param nickName 用户昵称
* @return
*/
@GetMapping("${api.service.console.path:}/user/${api.service.console.version:v1}/name/id/list")
Reply<List<String>> getUserIdsByNickName(@RequestParam("nickName") String nickName);
}
注意:
- 声明接口时需要注意,@PathVariable、@RequestParam声明参数的注解value值是必输的,若参数不加注解,feign默认会使用@RequestBody注解处理。
- 建议统一使用Reply<?>来接收response。这样在接口返回的数据异常时,可以显式地捕获异常。
- 若feign调用的接口为get请求,但controller使用对象接收参数。在feign的声明中,不可使用对象传参,要把对象拆开写。平台不推荐使用feign调用这种接口
实现接口的失败回调
fallbackFactory 代码例子:
/**
* @author wry
* @date 2021-3-25
*/
@Component
public class RemoteUserInfoFallbackFactory implements FallbackFactory<IUserService> {
private Reply fail() {
return Reply.fail("500", "用户信息API访问异常");
}
@Override
public IUserService create(Throwable throwable) {
return new IUserService() {
@Override
public Reply<UserEntity> findUserById(String id) {
return fail();
}
@Override
public Reply<UserEntity> findUserByIdNoAuth(String id, String from) {
return fail();
}
@Override
public Reply<List<UserEntity>> findUserByIdsNoAuth(List<String> ids, String from) {
return fail();
}
@Override
public Reply<List<UserEntity>> findUserByIds(List<String> ids) {
return fail();
}
@Override
public Reply<UserEntity> findMyInfo() {
return fail();
}
@Override
public Reply<RoleAndPermEntity> findUserPermByEntId(String userId, String entId) {
return fail();
}
@Override
public Reply<SimpleUserModel> findSimpleUserById(String id) {
return fail();
}
@Override
public Reply<List<SimpleUserModel>> findSimpleUserByIds(List<String> ids) {
return fail();
}
@Override
public Reply<Map<String, String>> getUsernames(List<String> ids) {
return fail();
}
@Override
public Reply<Map<String, String>> getNickNames(List<String> ids) {
return fail();
}
@Override
public Reply<Map<String, UserEntity>> getUserMapEntity(List<String> ids) {
return fail();
}
@Override
public Reply<List<String>> getUserIdsByEntId(String entId, String search) {
return fail();
}
@Override
public Reply<List<String>> getUserIdsByNickName(String nickName) {
return fail();
}
};
}
}
如何调用接口
@Resource
private IUserService userService;
@ApiOperation("获取用户名称")
@GetMapping("/info/getUsernames")
public Reply<?> getUsernames(@RequestBody List<String> list) {
return userService.getUsernames(list);
}
超时问题
方案一(全局 - 不建议):
在nacos增加全局配置,将影响所有feign调用
feign:
client:
config:
default: // 所有服务级别设置,default为任意服务
connectTimeout: 毫秒时间,建立连接的超时时间,一般只在发现服务时用到
readTimeout: 毫秒时间 ,接口请求的超时时间
remote-server-name: // contextId属性,指定调用remote-server-name服务时的超时时间
connectTimeout: 200
readTimeout: 3000
方案二(按需 - 建议):
在FeignClient的代接口类增加Request.Options
在调用时按需配置,注意要重置多态解决默认不传的场景
@FeignClient(name = "${spring.application.name}")
public interface IUserServiceClient {
@GetMapping(path = "/user/query")
Reply<UserVO> getUser(Request.Options options, @RequestParam("id") String id);
@GetMapping(path = "/user/query")
Reply<UserVO> getUser(@RequestParam("id") String id);
}
实际调用
@ApiOperation(value = "Feign查询用户信息")
@GetMapping(value = "/feign/query")
public Reply<UserVO> feignQuery(String id) {
//自定义接口超时时间
Request.Options options = new Request.Options(/*connectTimeout*/ 8, TimeUnit.SECONDS, /*readTimeout*/ 3, TimeUnit.SECONDS, /*followRedirects*/ true);
return client.getUser(options, id);
}
无需指定 Request.Options
的场景就直接调用 client.getUser(id)
即可。
文档更新时间: 2024-09-21 11:52 作者:朱灿奕