Fegin的是使用
Fegin简介
Feign是一个声明式的Web服务客户端。这使得Web服务客户端的写入更加方便 要使用Feign创建一个界面并对其进行注释。它具有可插拔注释支持,包括Feign注释和JAX-RS注释。Feign还支持可插拔编码器和解码器。Spring Cloud添加了对Spring MVC注释的支持,并在Spring Web中使用默认使用的HttpMessageConverters。Spring Cloud集成Ribbon和Eureka以在使用Feign时提供负载均衡的http客户端
Feign的目标
feign是声明式的web service客户端,它让微服务之间的调用变得更简单了,类似controller调用service。Spring Cloud集成了Ribbon和Eureka,可在使用Feign时提供负载均衡的http客户端。
基础概念
声明式REST服务调用
通过spring官方文档可以了解到,Feign是一个声明式web 服务调用服务,他使得一切web服务得以简化。我们只需要创建一个接口并用注解和JAX-RS注解的方式来配置它,即可完成对服务提供方的接口绑定。
远程过程调用协议(RPC)
RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。 —– 百度百科
RPC代表
- JAVA RMI (二进制协议)
- WebService (文本协议)
消息传递
RPC是一种请求-响应协议,一次RPC在客户端初始化,再由客户端将请求消息传递到远程的服务端,执行指定的带有参数的过程。经过远程服务端执行过后,将结果作为响应内容返回到客户端。
存根
在一次分布式计算RPC中,客户端和服务端转换参数的一段代码,由于存根的参数转化,RPC执行过程如同本地执行函数调用。存根必须在客户端和服务端两端均装载,并且必须保持兼容。
Feign的原理
- 启动时,程序会进行包扫描,扫描所有包下所有@FeignClient注解的类,并将这些类注入到spring的IOC容器中。当定义的Feign中的接口被调用时,通过JDK的动态代理来生成RequestTemplate。
- RequestTemplate中包含请求的所有信息,如请求参数,请求URL等
- RequestTemplate生成Request,然后将Request交给client处理,这个client默认是JDK的HTTPUrlConnection,也可以是OKhttp、Apache的HTTPClient等
- 最后client封装成LoadBaLanceClient,结合ribbon负载均衡地发起调用
简单实现Feign整合
流程图
我们可以看到,客户端通过调用一定的Feign协议就可以实现请求和响应。接下来让我们来手动操作一下,感受一下Feign的奇妙吧。
注册中心
注册中心不需要做任何改动,按照之前的文章搭建即可。
客户端
增加POM依赖
首先在pom.xml文件中,增加相关依赖
1 | <dependency> |
启动类
使用@EnableFeignClients开启fegin的支持
1 |
|
Fegin接口
Fegin修饰的接口不需要实现类,他会自动进行动态代理,通过RestTemplate完成调用。
1 |
|
注意:这里服务名不区分大小写,所以使用order-server和ORDER-SERVER都是可以的。另外,在Brixton.SR5版本中,原有的serviceId属性已经被废弃,若要写属性名,可以使用name或value。
访问接口
使用@Autowired直接注入上面定义的UserService实例,并在query函数中调用这个绑定了ORDER-SERVER服务接口的客户端来向该服务发起/query接口的调用。
1 |
|
服务端
服务端只要提供好对应的 http接口即可 不需要做任何变动
接口
1 |
|
实现类
1 |
|
注意:在这里接口和实现类不是同一个接口类,并且接口的名称不一致但是接口的调用地址一致
测试
通过postmn进行测试成功
可以看到,使用feign之后,我们调用eureka 注册的其他服务,在代码中就像各个service之间相互调用那么简单。
FeignClient注解的一些属性
属性名 | 默认值 | 作用 | 备注 |
---|---|---|---|
value | 空字符串 | 调用服务名称,和name属性相同 | |
serviceId | 空字符串 | 服务id,作用和name属性相同 | 已过期 |
name | 空字符串 | 调用服务名称,和value属性相同 | |
url | 空字符串 | 全路径地址或hostname,http或https可选 | |
decode404 | FALSE | 配置响应状态码为404时是否应该抛出FeignExceptions | |
configuration | {} | 自定义当前feign client的一些配置 | 参考FeignClientsConfiguration |
fallback | void.class | 熔断机制,调用失败时,走的一些回退方法,可以用来抛出异常或给出默认返回数据。 | 底层依赖hystrix,启动类要加上@EnableHystrix |
path | 空字符串 | 自动给所有方法的requestMapping前加上前缀,类似与controller类上的requestMapping | |
primary | TRUE |
客户端Hystrix整合
引入POM文件
在客户端引入Hystrix依赖
1 | <dependency> |
熔断器使用
在网络请求时,可能会出现异常请求,如果还想再异常情况下使系统可用,那么就需要容错处理,比如:网络请求超时时给用户提示“稍后重试”或使用本地快照数据等等。
Spring Cloud Feign就是通过Fallback
实现的,有两种方式:
@FeignClient.fallback = UserFeignFallback.class指定一个实现Feign接口的实现类。
@FeignClient.fallbackFactory = UserFeignFactory.class指定一个实现FallbackFactory
工厂接口类
注意:feign的注解@FeignClient:fallbackFactory与fallback方法不能同时使用,这个两个方法其实都类似于Hystrix的功能,当网络不通时返回默认的配置数据。
配置文件配置
在application.properties 启用hystrix
1 | feign.hystrix.enabled=true |
请务必注意,从Spring Cloud Dalston开始,Feign默认是不开启Hystrix的。
因此,如使用Dalston以及以上版本请务必额外设置属性:feign.hystrix.enabled=true,否则 断路器不会生效。
Spring Cloud Angel/Brixton/Camden中,Feign默认都是开启Hystrix的。
fallback 实现方式
创建
UserServiceFallBack
的回调实现,由spring创建使用@Component
(其他的注册也可以)注解
HystrixTargeter.targetWithFallback
方法实现了@FeignClient.fallback
处理逻辑,通过源码可以知道UserFeignFallback
回调类是从Spring容器中获取的,所以UserFeignFallback
由spring创建。
UserService 接口类
添加fallback注解的配置,配置我们自己的 UserServiceFallBack类
1 |
|
UserServiceFallBack类
1 |
|
测试
服务提供方异常
客户端控制台输出
1 | 查询订单异常....... |
服务提供方抛出的500错误代码,但是客户端程序还可以正常运行输出了UserServiceFallBack.query
方法返回的结果。
FallbackFactory工厂
上面的实现方式简单,但是获取不到HTTP请求错误状态码和信息 ,这时就可以使用工厂模式来实现
Fallback
同样工厂实现类也要交由spring管理,同时结合
UserFeignFallback
使用,这里需要注意的create
方法返回值类型一定要实现Feign接口,否则会报错。
UserServiceFallbackFactory
UserServiceFallbackFactory
创建可一个UserService
的匿名内部类,完成了异常的打印和返回
1 |
|
UserService 接口类
将fallback 替换为fallbackFactory方式
1 |
|
测试
熔断器监控
以上的配置只是让熔断器生效,但是是没有开启熔断器监控的,并且对应的 /actuator/hystrix.stream并没有开启。
注意:想要开启需要在启动类加入 @EnableCircuitBreaker 或者@EnableHystrix 注解。
1 |
|
自定义ErrorDecoder
ErrorDecoder
接口处理请求错误信息,默认实现ErrorDecoder.Default
抛出FeignException
异常
FeignException.status
方法返回HTTP状态码,FallbackFactory.create
默认情况下可以强制转换成FeignException
异常这样就可以获取到HTTP状态码了。
FeginErrorDecoder
自定义FeginErrorDecoder
1 | public class FeginErrorDecoder implements ErrorDecoder { |
配置类配置
1 |
|
流程
在Feign客户端发生http请求层面的错误时会调用decode方法。在decode方法中实现自定义的错误处理,当出现异常时首先会通过FeginErrorDecoder
进行异常的封装,然后会调用UserServiceFallbackFactory
进行异常的回调处理
测试
feign使用的一些问题
第一次请求失败
原因:由于spring的懒加载机制导致大量的类只有在真正使用的才会真正创建,由于默认的熔断超时时间(1秒)过短,导致第一次请求很容易失败,特别互相依赖复杂的时候。
1 | #设置hystrix超时时间 |
熔断高频出错问题
问题
在进行feign调用的是一直发生熔断 ,feign和hystrix的超时时间都调整了,就是一直不起作用
熔断产生的原因
openFeign默认支持Ribbon(软负载)和Hystrix(熔断)
关于Ribbon(软负载)
而Ribbon最重要的两个超时包含:
连接超时ConnectTimeout
读超时ReadTimeout
默认情况下,也就是没有任何配置下,Feign的超时时间会被Ribbon覆盖 ,两个超时时间都是1秒,如果调用的逻辑复杂,用时超过1秒,肯定会发生熔断现象(读超时ReadTimeout引起)
关于Hystrix(熔断)
如果Hystrix也开启(可以通过配置文件自定义)了超时,也需要将它的超时时间设置一下, 否则会和上面的Ribbon一样,会覆盖掉Fegin
解决方案一
在调用方的yml配置文件中添加配置
1 | # 配置超时时间 |
注意:上面
ReadTimeout:5000
和timeoutInMilliseconds: 5000
一定要大于业务逻辑处理时间,比如你业务处理为10秒,此处就应该大于10秒,否则还会出现熔断 如果上面的hystrix.command.execution.timeout.enabled
设置为false,那么timeoutInMilliseconds
的值就可以不用设置了
熔断解决方案二
1 | hystrix: |
注意:feign的配置会覆盖ribbon,也就是说Feign配置会优先于Ribbon配置。配置完之后也是同样的效果
默认情况下,也就是没有任何配置下,Ribbon会覆盖Feign,超时时间都是1秒
如果Hystrix+ribbon开启它也会覆盖Feign
Hystrix+feign的配置会覆盖ribbon,
Feign使用HttpClient
Feign在默认情况下使用的是JDK原生URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的persistence connection。我们可以用Apache的HTTP Client替换Feign原始的http client,从而获取连接池、超时时间等与性能息息相关的控制能力。Spring Cloud从Brixtion.SR5版本开始支持这种替换。
导入POM依赖
1 | <dependency> |
配置文件配置
1 | feign.httpclient.enabled=true |
主要:这个配置可加可不加,在该版本中默认为true,可以不加,在HttpClientFeignLoadBalancedConfiguration源码中有
1 |
的配置默认为true
验证
首先在工程配置文件中,将配置项 feign.httpclient.enabled 的值,设置为 false 。然后,在HttpClientFeignLoadBalancedConfiguration 的 feignClient(…)方法内的某行打上断点,重新启动项目,注意观察会发现,整个启动过程中,断点没有被命中。接下来,将配置项 feign.httpclient.enabled 的值设置为 true,再一次启动项目,断点被命中。由此,可以验证 HttpClientFeignLoadBalancedConfiguration 自动配置类被启动。
在feign请求之前操作
feign组件提供了请求操作接口RequestInterceptor,实现之后对apply函数进行重写就能对request进行修改,包括header和body操作。
1 |
|
请求压缩
Spring Cloud Feign支持对请求和响应进行GZIP压缩,以减少通信过程中的性能损耗。我们只需通过下面两个参数设置,就能开启请求与响应的压缩功能:
1 | feign.compression.request.enabled=true |
同时,我们还能对请求压缩做一些更细致的设置,比如下面的配置内容指定了压缩的请求数据类型,并设置了压缩的大小下限,只有超过这个大小的请求才会对其进行压缩。
1 | feign.compression.request.enabled=true |
上述配置的feign.compression.request.nime-types和feign.compression.requestmin-request-size均为默认值。
日志配置
Spring Cloud Feign在构建被@FeignClient注解修饰的服务客户端时,会为每一个客户端都创建一个feign的请求细节。可以在application.properties文件中使用logging.level.<FeignClient>的参数配置格式来开启指定Feign客户端的DEBUG日志,其中<FeignClient>为Feign客户端定义捷克队完整路径,比如针对本博文中我们实现的UserService可以如下配置开启:
1 | logging.level.com.springcloud.user.service.UserService=DEBUG |
但是,只是添加了如上配置,还无法实现对DEBUG日志的输出。这时由于Feign客户端默认对Logger.Level对象定义为NONE级别,该界别不会记录任何Feign调用过程中对信息,所以我们需要调整它对级别,针对全局对日志级别,可以在应用主类中直接假如Logger.Level的Bean创建,具体如下:
1 |
|
在调整日志级别为FULL之后,我们调用接口测试,查看日志
fegin日志级别
对于Feign的Logger级别主要有下面4类,可根据实际需要进行调整使用。
NONE:不记录任何信息。
BASIC:仅记录请求方法、URL以及响应状态码和执行时间。
HEADERS:出了记录BASIC级别的信息之外,还会记录请求和响应的头信息。
FULL:记录所有请求与响应的细节,包括头信息、请求体、元数据等。