Springcloud踩坑记---使⽤feignclient远程调⽤服务404
公司项⽬进⾏微服务改造,由之前的dubbo改⽤SpringCloud,微服务之间通过FeignClient进⾏调⽤,今天在测试的时候,eureka注册中⼼有相应的服务,但feignclient就是⽆法调通,⼀直报404错误,排查过程如下:
⼀、问题:
  服务提供⽅定义的接⼝如下:
/**
* ⿊⽩名单查询接⼝
*
* @author LiJunJun
* @since 2018/10/18
*/
@Component(value = "blackAndWhiteListFeignClient")
@FeignClient(value = "pear-cache-service", path = "v1/cache/limitlist")
public interface IBlackAndWhiteListFeignClient {
/**
* 获取⿊⽩名单⼿机号分组编号
*
* @param trace 请求流⽔
* @param phoneNum 电话号码
* @return电话号码所在分组
*/
@RequestMapping(value = "/blackAndWhiteList", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
ResultData<String> blackAndWhiteList(@RequestHeader(name = "Trace") String trace, @RequestParam("phoneNum") String phoneNum);
}
接⼝实现类如下:
/**
* ⿊⽩名单controller
*
* @author LiJunJun
* @since 2018/10/18
springcloud和springboot
*/
@ProtectedLdApi
@RestController
@RequestMapping(value = "v1/cache/limitlist")
@Api(value = "⿊⽩名单缓存", description = "⿊⽩名单缓存相关接⼝")
public class BlacklAndWhiteListController extends AbstractController implements IBlackAndWhiteListFeignClient {
/**
* ⽇志记录器
*/
private final static Log LOGGER = new Log(BlacklAndWhiteListController.class);
/**
* 注⼊tedis
*/
@Autowired
private JedisSentinelPoolExt jedisSentinelPool;
/**
* 获取⿊⽩名单⼿机号分组编号
*
* @param trace 请求流⽔
* @param phoneNum 电话号码
* @return电话号码所在分组
*/
@Override
@ApiOperation(value = "获取⿊⽩名单⼿机号分组编号", notes = "根据电话号码从缓存中获取⿊⽩名单分组")
@RequestMapping(value = "/blackAndWhiteList", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_UTF8_VALUE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public ResultData<String> blackAndWhiteList(@RequestHeader(name = "Trace") String trace, @RequestParam("phoneNum") String phoneNum) {
}
}
调⽤⽅如下:
public class MessageListController {
private static final Log LOGGER = new Log(MessageListController.class);
@Autowired
private IBlackAndWhiteListFeignClient blackAndWhiteListFeignClient;
@RequestMapping(value = "/testBlackAndWhiteList", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResultData<String> testBlackAndWhiteList() {
LOGGER.info("开始调⽤缓存接⼝");
ResultData<String> res = blackAndWhiteListFeignClient.blackAndWhiteList("asdqwezxxc", "B180********");
LOGGER.info("调⽤结果:" + ResultData());
return res;
}
调⽤结果:
华丽丽的404了,很头疼,经过各种度娘,发现导致这个问题有两个原因,以下是解决⽅法:
⼆、问题分析
经过百度,说将SpringBoot配置⽂件⾥⾯t-path注释掉即可,抱着试⼀哈的态度,注释了,重启,调⽤,结果惊喜的发现,依旧报错了,但仔细⼀看,错误代码已经不是404,变成了415,这就相当于调通了,但是,Content-Type的类型不对,于是,返回去看代码(此时已经肯定,今天能把feignclient接⼝调通),
仔细⼀看发现,接⼝上定义的@RequestMapping中,只定义了produces = MediaType.APPLICATION_JSON_UTF8_VALUE,⽽实现类中,@RequestMapping定义了consumes、produces均为"application/json;charset=UTF-8"
我们知道,consumes定义了⽅法接受的Http的请求类型,produces则定义了Http请求返回的类型;
然后我们说下FeignClient,它的底层实现,就是根据定义的FeignClient接⼝,来组装Http请求进⾏远程调⽤,⽽Http默认的Content-type是x-www-form-urlencoded类型化的,到这⼉,问题就呼之欲出了:
再来回顾上⾯我们定义的接⼝,并没有指定请求类型(consumes),那么FeignClient组装的Http请求的类型就是默认的x-www-form-urlencoded类型,但我们的实现类上,却定义了consumes=MediaType.APPLICATION_JSON_UTF8_VALUE,也就是说,仅接受json类型的请求,这就是为什么415的原因了;
三、解决⽅法
知道了问题的原因,解决起来就很简单了,我们可以在FeignClient的接⼝定义上,指定consumes,这样,FeignClient在组装Http请求的时候,就会在header⾥⾯设置请求类型为application/json,这样,问题就完美解决;
再来看调⽤结果:
完美返回
四、总结
feignclient接⼝定义是⼀个模板化的,其组装的Http请求完全按照你定义的接⼝去组装,如你在参数中,⽤@RequestHeader去接收⼀个参数,其组装请求时,就会将你传⼊的参数放⾄Header中,你指定的consumes为json,其组装的请求Content-Type就是 application/json类型的,完全不需要调⽤⽅感知,就像调⽤普通⽅法⼀样,不得不说,很强⼤,只要⽣成的Http请求正确,服务提供⽅提供的Rest接⼝能和FeignClient组装的Http请求,就能够完成远程调⽤。
五、遗留问题
为什么需要将服务提供⽅的t-path去掉才能实现调⽤,今天暂时没有研究,但⼀定有解决⽅案,SpringCloud不会这么low的,解决⽅案研究出来会补上。