一、Feign概述
Feign是Spring Cloud提供的声明式、模板化的HTTP客户端,运行在consumerdua端, 它使得调用远程服务就像调用本地服务一样简单,只需要创建一个接口并添加一个注解即可。
Spring Cloud集成Feign并对其进行了增强,使Feign支持了Spring MVC注解;Feign默认集成了Ribbon,所以Fegin默认就实现了负载均衡的效果。(ribbon+restTemplate)+优化=feign
二、启动类
- 在ConsumerApplication启动类上,添加
@EnableFeignClients
注解,开启Feign接口扫描功能
@SpringBootApplication
@EnableDiscoveryClient
//@EnableFeignClients(basePackages = "com.bjpowernode.feign")
@EnableFeignClients//开启feign接口扫描
public class ConsumerApp {
public static void main(String[] args) {
SpringApplication.run(ConsumerApp.class);
}
}
三、feign的接口类
- 编写Feign客户端接口类UserFeign
- 注解@FeignClient声明Feign的客户端,指明服务名称
- 接口定义的方法,采用SpringMVC的注解。Feign会根据注解帮我们生成URL地址
- Feign会通过动态代理,帮我们生成实现类。
- fallbackFactory是工厂类,用于生成 fallback 类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码。
- fallback:定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback 指定的类必须实现 @FeignClient 标记的接口。
@FeignClient(value = "feign-provider", fallbackFactory = UserFeignFallback.class)
@RequestMapping(value = "/provider")
public interface UserFeign {
@RequestMapping(value = "/getUserById/{id}")
public User getUserById(@PathVariable(value = "id") Integer id);
- fallback就是调用失败或者请求超时时,调用的数据。实现类必须用@Component注解,以保证注入到spring容器
- 在使用FeignClient时,Spring会按name创建不同的ApplicationContext,通过不同的Context来隔离FeignClient的配置信息,在使用配置类时,不能把配置类放到Spring App Component scan的路径下,否则,配置类会对所有FeignClient生效
四、feign接口传参的三种方式
- ?传参 @RequestParam("id") 和controller中的RequestParam注解有所不同,必须指明参数名,不管相同否 【拼接?形式的url】
- restful传参 @PathVariable("id") 和controller中的PathVariable注解有所不同,必须指明参数名,不管相同否 【拼接restful形式的url】
- pojo @RequestBody controller中是注解放在形参前,可以json转对象,这里是对象转json 【获取请求体中的json串】
五、超时时间
- 通过启动类发现
OpenFeign
的默认的connectTimeout
是 10 秒,readTimeout
是 60 秒。模拟请求超时,睡一会
@Service
public class UserServiceImpl implements UserService {
@Override
public User getUser() {
//模拟网络延迟
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new User(1,"王粪堆",18);
}
}
会报错
其实因为
OpenFeign
集成了 Ribbon
,Ribbon 的默认超时连接时间、读超时时间都是 1 秒
如果 OpenFeign
没有设置对应得超时时间,那么将会采用 Ribbon
的默认超时时间
可以在yml文件设置OpenFeign的请求超时时间,和Ribbon的请求超时时间
openfeign可单独配置某个服务的超时时间
但是Ribbon和feign的设置,只有一个会生效,同时设置优先生效OpenFeign,注掉 OpenFeign
超时时间配置之后,就变成了使用设置的 Ribbon
的超时时间
超时总结:
不设置超时时间默认是Ribbon 的默认超时连接时间、读超时时间都是 1 秒。同时设置,生效Feign。
六、Feign 工作原理
- 在开发微服务应用时,我们会在主程序入口添加 @EnableFeignClients 注解开启对 Feign Client 扫描加载处理。根据 Feign Client 的开发规范,定义接口并加 @FeignClients 注解。
- 当程序启动时,会进行包扫描,扫描所有 @FeignClients 的注解的类,并将这些信息注入 Spring IOC 容器中。当定义的 Feign 接口中的方法被调用时,通过JDK的代理的方式,来生成具体的 RequestTemplate。当生成代理时,Feign 会为每个接口方法创建一个 RequetTemplate 对象,该对象封装了 HTTP 请求需要的全部信息,如请求参数名、请求方法等信息都是在这个过程中确定的。
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
………………
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
Set<String> basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
………………
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
String name = getClientName(attributes);
registerClientConfiguration(registry, name,
attributes.get("configuration"));
registerFeignClient(registry, annotationMetadata, attributes);
}
}
}
- 然后由 RequestTemplate 生成 Request,然后把 Request 交给 Client 去处理,这里指的 Client 可以是 JDK 原生的 URLConnection、Apache 的 Http Client 也可以是 Okhttp。最后 Client 被封装到 LoadBalanceclient 类,这个类结合 Ribbon 负载均衡发起服务之间的调用。
七、@FeignClient 注解:
- name:指定 Feign Client 的名称,如果项目使用了 Ribbon,name 属性会作为微服务的名称,用于服务发现。
- url:url 一般用于调试,可以手动指定 @FeignClient 调用的地址。
- decode404:当发生404错误时,如果该字段为 true,会调用 decoder 进行解码,否则抛出 FeignException。
- configuration:Feign 配置类,可以自定义 Feign 的 Encoder、Decoder、LogLevel、Contract。
- fallback:定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback 指定的类必须实现 @FeignClient 标记的接口。
- fallbackFactory:工厂类,用于生成 fallback 类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码。
- path:定义当前 FeignClient 的统一前缀。
八、Feign的优化
1、http连接池
Feign底层发起http请求
其底层客户端实现包括三个:
因此提高Feign的性能主要手段就是使用连接池代替默认的URLConnection。
- 消费者中加依赖(如果有公共的接口工程,可以直接加在接口的pom.xml中)
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
- 开启连接池(因为默认为true开启,添加依赖后就行,可不写连接池配置,还可以配其他配置,入最大连接数等)
开启后便切换为HttpClient
2、支持gzip压缩
现在打大多数主流浏览器都支持多种压缩格式,gzip广为好评。在application.yml中修改开启就可优化传输速度。
3、feign超时
上面第五节已说明,可以全局设置ribbon的超时时间(不设置默认为1s,),也可设置feign的全局超时时间和指定服务超时时间,可防止异常出现。
4、开启feign日志
两个需要同时配置。
超时时间和日志配置都没有智能提醒,这里提供一份供大家CV大法,防止手残
server:
port: 80
compression:
enabled: true #开启gzip压缩
spring:
cloud:
nacos:
discovery:
server-addr: 192.168.163.131:8848 #nacos的服务地址
#namespace: dev #可自定义,读取开发环境的配置文件 namespace隔离 服务隔离
#group: NACOS_GROUP #读取NACOS_GROUP这个分组下的配置文件,分组隔离 服务隔离
application:
name: feign-consumer #向注册中心注册的名字
#ribbon:
# ConnectTimeout: 5000 #请求连接的超时时间
# ReadTimeout: 5000 #请求处理的超时时间
feign:
client:
config:
default: # 全局配置,也可以换成指定的服务feign-provider
connectTimeout: 5000 #请求连接的超时时间
readTimeout: 5000 #请求处理的超时时间
loggerLevel: full #内容全部输出
logging:
level:
com.shenjian.feign: debug # 开启debug级别的日志