首先是对FeignClient里的常用属性

  1. contextId ,当有多个服务调用方法不想写在一个接口里,就要使用到
  
  2. name:指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现
  3. url: url一般用于调试,可以手动指定@FeignClient调用的地址
  4. fallback: 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现 
      @FeignClient标记的接口
  5. fallbackFactory: 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复
      的代码 
  6. path: 定义当前FeignClient的统一前缀

使用FeignClient的原因

无非让一个微服务的接口被另一个服务访问,微服务的一个特点就是业务隔离,那就要尽量的做到数据库隔离,虽 然不是真的隔离,但是要尽量在代码上做隔离。举例,系统有一个file-server,是系统的公共服务,附件的下载,上传,获取内容等接口实现。对应操作的表是系统附件表。现在客户的信息里有附,正常是在客户与附件之间做连表查询,将附件信息拿到,但是在微服务里这样子做是不合规的。现在如果进行如下的引用
在controller类里定义的接口方法

//在controller类里定义的接口方法   
@RequestMapping(value = "/add" ,method = RequestMethod.GET)
public Integer add(@RequestParam Integer a, @RequestParam Integer b) {
 ServiceInstance instance = client.getLocalServiceInstance();
 Integer r = a + b;
 logger.info("/add, host:" + instance.getHost() + ", service_id:" + instance.getServiceId() + ", result:" + r);
 return r;
}

使用@FeignClient

@FeignClient("compute-service")
public interface ComputeClient {

    @RequestMapping(method = RequestMethod.GET, value = "/add")
    Integer add(@RequestParam(value = "a") Integer a, @RequestParam(value = "b") Integer b);
}

然后在另一个微服务里的controller层调用

@Autowired
ComputeClient computeClient;
@RequestMapping(value = "/add", method = RequestMethod.GET)
public Integer add() {
    return computeClient.add(10, 20);
}

只能说使用成功了,但是不规范,这是网上随处可见的错误规范,一开始我也是错误的在controll层引用,但是被技术经理叼了,反正就是不合规。理由很简单,给前端的接口不要重复。如果你这边controller调用原来的服务,不管是哪个微服务的,前端是可以直接访问的,那这样子的意义何在。接口给你调用,是让你用来获取有用的信息来处理,然后返回给前端。所以应该在server层调用。

规范的使用@FignClient注解

以公共服务中的文件服务和客户业务模块进行服务间的通信为例
在文件服务的api模块里写如下

FeignClient(contextId = "fileService", value = ServicellaneConstants.FTLE_SERNVTCE,fallbackFactory=RenoteFileFallbackFactory.class)
public interface RemoteFileservice {
/**
*上传文件*
* aparam file 文件信息*areturn结果
*/
@PostMapping(value = "/upload",consumes = MediaType.WULTIPART_FORN_DATA_VALUE)
public RcLong uplocad(CRequestParan("module ) String nodole ,ORequestParan("sunceId ) String souneId, OlequestFart(value = "file ") liltigartFile file);

此时在客户业务板块,即客户需要和附件进行相关信息的1对多查询,不能连表查询,那就调用上面的RemoteFileservice ,一开始我在客户模块里写了一个FileController,然后写接口,接口里调用,直接NO。应该在业务层里调用就好了,用注解@Autowired,将你需要处理的数据获取到并处理好。

接下来看看fallbackFactory,主要是为降级处理就是出错了应该返回什么。代码仅供参考,可能还会有其它 的写法

//注意FallbackFactory<你注解对应的接口类名>
@component
public class RemoteFileFallbackFactory implements FallbackFactory<RemoteFileService>
private static final Logger log = LoggerFactory.getLogger(RemoteFileFallbackFactory.class);
0verride
public RemoteFileservice create(Throwable throwable){
//打印出错日志
log.error("文件朋务调用失败:{}", throwable.getHessage());return new RemoteFileserviceo
{
Override
public R<Long> upload(String module,String sourceId,MultipartFile file){
return R.fail("上传失败:" + throwable.getMessage(o);
}
0verride
public byte[ ] getFileBytes(Long fileId) { return new byte[0];}
@0verride
public R getURL(Long[] ids) { return R.fail("获取文件URL失败:" + throwable.getHessage());}
@0verride
public R<List<SysFileApivo>> getURL(List<Long> ids) {
return R.fail("获取文件URL失败:" + throwable.getMessage());
}
};

最后直接在所需要的服务里直接引用,例如:

public class StudentServiceImpl implements StudentService{
  @Autowired
  private RemoteFileservice  remoteFileservice ;
}

正常时拿着sourceId去找到对应的文件进行处理,且要遵循,能一次调用就一次调用,不要一个一个去调用数据。例如列表展示头像,一共10条数据,对应10条图片数据,应该一次性访问拿到,再通过sourceId来进行分离对应。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐