前言:
新公司的项目中,服务之间的调用用到了 spring cloud 的 openfeign组件;虽然之前有自己学习过一些spring cloud组件使用,但是时间长了,记忆也模糊了,所以今天决定在博客上面记录下今天使用openFegin遇到的问题,防止日后又忘记了。
openFegin简介:
spring cloud 将openFeign集成到spring boot应用中,为微服务之间的相互调用提供了解决方案,最开始是利用openFeign的声明式方式定义wed客户端,再之后通过Ribbon和Eureka实现负载均衡的HTTP客户端;openFeigns是一个声明式,模块化的HTTP客户端,可以实现调用远程接口如调用方法一样简单。
openFegin的注解介绍:
1. @FeginClient // 标注在接口上声明该接口为Fegin客户端 fallback容错类需要标注@Component,否则运行时无法注册容错类
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface FeignClient {
@AliasFor("name")
String value() default "";
/** @deprecated */
@Deprecated
String serviceId() default "";
// 标注上下文的名称
String contextId() default "";
//指定FeignClient的名称,如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现
@AliasFor("value")
String name() default "";
String qualifier() default "";
// url一般用于调试,可以手动指定@FeignClient调用的地址
String url() default "";
// 当发生http 404错误时,如果该字段位true,会调用decoder进行解码,否则抛出FeignException
boolean decode404() default false;
// Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract
Class<?>[] configuration() default {};
// 定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的
类必须实现@FeignClient标记的接口
Class<?> fallback() default void.class;
// 工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重
复的代码
Class<?> fallbackFactory() default void.class;
// 定义当前FeignClient的统一前缀
String path() default "";
boolean primary() default true;
}
2.@EnableFeginClients // 标注在Spring boot应用入口上开启一个Fegin客户端
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
// FeignClientsRegistrar 实现了 ImportBeanDefinitionRegistrar 接口 该接口是注册Feign客服的
@Import({FeignClientsRegistrar.class})
public @interface EnableFeignClients {
String[] value() default {};
// 可以指定扫描包的路径
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
// 指定默认的配置类
Class<?>[] defaultConfiguration() default {};
// 指定Feign客户端
Class<?>[] clients() default {};
}
使用openFeign注意事项:
1.在FeignClient中参数只能有一个@RequestBody 注解多个@RequestParam 注解接收参数。
2.FeignClient无法直接传递HttpServletRequest request,如需要传递request获取头部参数信息,可以去实现Fegin的RequestInterceptor拦截器,原因好像是因为Feign发送请求的时候是另外一条线程去去处理,所以服务器接收到的request是另外的一个Request, 实现Fegin的拦截器代码如下:
/**
* @author CJL
* @description: Fegin拦截器 设置所有的请求头信息
* @date 2021/3/17
*/
@Component
public class FeginRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null) {
while (headerNames.hasMoreElements()) {
String key = headerNames.nextElement();
String value = request.getHeader(key);
if (key.equalsIgnoreCase("content-length")){
continue;
}
requestTemplate.header(key,value);
}
}
}
}
}
需要注意的是服务相互调用的时间的控制,太短的话可能出现时间超时的问题,可以根据实际情况在配置文件中配置调用查询的时间:
feign:
hystrix:
enabled: false
client:
config:
default:
connectTimeout: 30000 #单位毫秒
readTimeout: 30000 #单位毫秒
在@FeignClient 客户端中可以通过 configuration 单独配置该客户端的请求拦截器,已达到实现我们想要的效果。