近日,在项目中使用到了Feign项目,却出现各种问题,于是将遇到的错误写下来,希望能帮到你。
spring boot版本为1.5.9 ,cloud版本也不高。
接口无法注入:
遇到这种情况的原因可能是 注解加的不对,或者接口中参数的获取没有绑定具体参数(巨坑)。
接下来详细的说收怎么回事。
先从主启动类开始找:主启动类上是不是加入了@EnableFeignClients(basePackages= {"com.demo"}),basePackages指定接口所在的位置,注意,basePackages别写错了, 再有是不是加入了@ComponentScan("com.demo") ,该注解指定扫描的包,如果不加,可能会浏览器访问的时候报404错误。
如果以上都没有问题,那么就去接口中看看,接口上要指明你的微服务名称,代表去 Eureka注册中心中根据微服务名去找到ip和端口访问服务。 @FeignClient(value="DEMO") value是指定的微服务名。
另外还有一个巨坑的地方: 接口中定义的方法中,形如 @RequestParam的这种要绑定参数。 @RequestParam("id")
这个为什么这样,我搜了下没有找到原因,这里没法解释,抱歉。-----
我想,你不会是 由于pom文件中没有依赖 相关依赖报的错。。。。。。
另外,记一次自己犯的一次特别憨憨的错误(2020-03-15, springcloud版本: Hoxton.SR1):
Feign调用超时:
背景: 使用Feign进行服务调用,自信代码并没有什么出错的地方,无奈消费者通过feign调用服务提供方一直报超时,我仔细的一遍又一遍的查看了代码,找不到什么问题,直接通过postman调用服务提供方(本机localhost的端口)方法, 却能调通。百思不得其解,我去注册中心看了下服务提供者的情况,一下子找到了问题的原因 .......服务提供者的ip不对
原来是我本机的wifi换了, 然而我的服务提供方并没有重启,在注册中心上的ip地址仍旧是之前wifi1下的ipv4地址,而实际上我换了wifi后 ,服务提供者的ip地址已经变了。。。 重新重启了服务提供者微服务,feign调用正常了。。多么痛的领悟!!
Feign调用丢失请求头信息
SpringCloud版本 Greenwich.SR3
1) 单点系统中,接口中的feign远程调用会丢失请求头信息(cookie等),这是由于feign远程调用重新创建了一个新的请求,该请求并未携带老请求的任何请求头信息。解决办法: 新建一个配置类,增加一个feign的拦截器,在拦截器中将老请求的请求头信息设置到新请求中。
/**
* 解决feign远程调用,丢失请求头信息的问题
* @return
*/
@Bean
public RequestInterceptor requestInterceptor(){
return requestTemplate -> {
// requestTemplate
System.out.println("feign远程调用前先进行RequestInterceptor。。。");
ServletRequestAttributes attributes =(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(attributes!=null) {
HttpServletRequest request = attributes.getRequest();// 老请求
if (request != null) {
// 同步请求头数据,Cookie
String cookie = request.getHeader("Cookie");
// 给新请求设置cookie
requestTemplate.header("Cookie", cookie);
}
}
};
}
2) 异步Feign调用丢失请求头问题
接口中可以同时存在多个feign调用,如果是同步状态,等待每个feign调用结束,那么接口的响应时间比较久。将代码改为异步方式(CompletableFuture.runAsync等),则会大大提高吞吐率。
简易代码如下所示:
public void createOrder(){
// 创建订单需要减库存,发短信、邮件给用户 ,联系快递公司等业务
// ... 具体业务操作
CompletableFuture<Void> productTask =CompletableFuture.runAsync(() -> {
productFeignService.lockProduct(); // 远程调用商品服务锁定商品,减库存
}, executor);
CompletableFuture<Void> smsTask=CompletableFuture.runAsync(() -> {
smsFeignService.sendMsg(); // 远程调用消息服务给用户发送短信,邮件等
}, executor);
CompletableFuture<Void> thridTask=CompletableFuture.runAsync(() -> {
thridFeignService.createExpressOrder(); // 远程调用三方服务创建快递订单
}, executor);
// .... 业务操作
}
此种情况下, feign调用也会丢失请求头信息,这是由于 CompletableFuture.runAsync开辟了一个新的线程,与主线程数据并不共享。解决办法: 在主线程中通过 RequestContextHolder获取当前主线程的请求参数,然后再CompletableFuture.runAsync 中在通过RequestContextHolder 将请求参数设置到当前的线程中。代码如下所示:
public void createOrder(){
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
// 创建订单需要减库存,发短信、邮件给用户 ,联系快递公司等业务
// ... 具体业务操作
CompletableFuture<Void> productTask =CompletableFuture.runAsync(() -> {
// 复制主线程的 线程共享数据
RequestContextHolder.setRequestAttributes(attributes);
productFeignService.lockProduct(); // 远程调用商品服务锁定商品,减库存
}, executor);
CompletableFuture<Void> smsTask=CompletableFuture.runAsync(() -> {
// 复制主线程的 线程共享数据
RequestContextHolder.setRequestAttributes(attributes);
smsFeignService.sendMsg(); // 远程调用消息服务给用户发送短信,邮件等
}, executor);
CompletableFuture<Void> thridTask=CompletableFuture.runAsync(() -> {
// 复制主线程的 线程共享数据
RequestContextHolder.setRequestAttributes(attributes);
thridFeignService.createExpressOrder(); // 远程调用三方服务创建快递订单
}, executor);
// .... 业务操作
}