用springboot+springcloud开发也有一年多了,记录一下心得,和一些需要注意的地方。
1.feign调用时,方法参数的@RequestParam里面必须要有value值
比如,你这个样子写就是不行滴:
@FeignClient("test-service") //这个地方是你要调用的服务的应用名(spring.application.name配置)
public interface TestClient {
@RequestMapping(value = "/xxxxxx", method = {RequestMethod.POST})
public RespData testQuery(@RequestParam(value = "platformId") Long platformId,//必须要有value值
@RequestParam(value = "groupId") String groupId);
}
这个地方和springmvc的@RequestParam处理方式不一样,springmvc的这个注解不填写value的时候,value默认就是后面的参数名;如果feign调用的时候不填,就会默认封装成map的方式去调用目标服务,这个时候由于目标方法接收参数的形式不是map,就会报这个错:
Caused by: java.lang.IllegalStateException: RequestParam.value() was empty on parameter 0
所以,feign调用时,@RequestParam里面必须要有value值
2.feign调用时,方法的@RequestMapping里面必须指定method,而且只能指定一个调用方式
@RequestMapping(value = "/xxxxxx", method = {RequestMethod.POST})//这个地方需要设置method,确定feign具体的请求方式
这个也需要注意,写springmvc顺手了,会出现不设置method或者将method的值设置为多个
3.如果方法的返回值是对象,对象必须要有无参构造函数
@RequestMapping(value = "/xxxxxx", method = {RequestMethod.POST})
public Student testQuery(@RequestParam(value = "platformId") Long platformId)//Student对象必须要有无参构造函数
feign将json反序列化为对象时默认使用的是fastjson,如果采用了@JsonCreator注解标注构造函数,那么在转对象时就会调用被@JsonCreator注解标注的构造函数;如果没有使用@JsonCreator注解标注,就默认调用对象的无参构造函数,对象没有无参构造函数就会报错:
com.fasterxml.jackson.databind.JsonMappingException
在开发时,也要注意这个坑!!!
4.feign调用,带上请求头信息的解决方案
项目就是微服务架构,服务之间调用的情况非常多,不同的服务涉及的接口访问权限也不一样,比如platform-service就需要系统用户登录,item-service都可以访问,user-service就需要普通用户登录。我们的解决方案就是每个服务都有自己的拦截器。
现在的问题就是一个服务调另外一个服务的时候,必须将请求头中的cookie信息带上,不然会被拦截器拦截。
现在提供两种解决方案:
第一种,就是所有的feign请求都会带上请求头中的信息(参考地址)
//定义一个拦截器,拦截到请求,然后获取请求头中的信息设置到requestTemplate中
//注意这个方法所在的类需要加@Configration注解
@Bean
public RequestInterceptor headerInterceptor() {
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String values = request.getHeader(name);
requestTemplate.header(name, values);
}
}
}
};
}
//然后在application.yml中配置
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE //注意这个地方中间有个空格
第二种,设置某一次feign调用带上请求头中的信息(我们是采用的这个)
//定义一个类 实现RequestInterceptor接口,重写apply方法
@Override
public void apply(RequestTemplate requestTemplate) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes == null) {
return;
}
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
Enumeration<String> values = request.getHeaders(name);
while (values.hasMoreElements()) {
String value = values.nextElement();
requestTemplate.header(name, value);
}
}
}
}
//在具体生成feign时用这个方法生成
Feign.builder().encoder(new JacksonEncoder()).decoder(new JacksonDecoder())
.options(new Request.Options(1000, 3500)).requestInterceptor(new FeignClientsConfigurationCustom())//定义的请求拦截器类
.retryer(new Retryer.Default(5000, 5000, 3))
.target(clazz,domain)//clazz->返回的feignClient接口 domain->feign前面的请求地址
目前就这些,有的忘了,后面遇到会补充上。