我是靠谱客的博主 快乐睫毛膏,这篇文章主要介绍SpringCloud之Feign使用和拓展,现在分享给大家,希望可以做个参考。

SpringCloud之Feign使用和拓展

在使用Spring Cloud开发微服务应用时,各个服务提供者都是以HTTP接口的形式对外提供服务,因此在服务消费者调用服务提供者时,底层通过HTTP Client的方式访问。当然我们可以使用JDK原生的URLConnection、Apache的HTTP Client、Netty的异步HTTP Client, Spring的RestTemplate去实现服务间的调用。但是最方便、最优雅的方式是通过Spring Cloud Open Feign进行服务间的调用。Spring Cloud对Feign进行了增强,使Feign支持Spring MVC的注解,并整合了Ribbon等,从而让Feign的使用更加方便。本篇文章将从原理、实战分别对Feign扩展增强的角度进行剖析,帮助开发者快速掌握生产级别的Feign实战技巧。

一、什么是Feign

Feign是一个声明式的Web Service客户端。它的出现使开发Web Service客户端变得很简单。使用Feign只需要创建一个接口加上对应的注解,比如:FeignClient注解。Feign有可插拔的注解,包括Feign注解和JAX-RS注解。Feign也支持编码器和解码器,Spring Cloud Open Feign对Feign进行增强支持Spring MVC注解,可以像Spring Web一样使用HttpMessageConverters等。
Feign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用Feign,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。接下来介绍一下Feign的特性,具体如下:

❑ 可插拔的注解支持,包括Feign注解和JAX-RS注解。
❑ 支持可插拔的HTTP编码器和解码器。
❑ 支持Hystrix和它的Fallback。
❑ 支持Ribbon的负载均衡。
❑ 支持HTTP请求和响应的压缩。Feign是一个声明式的Web Service客户端,它的目的就是让Web Service调用更加简单。它整合了Ribbon和Hystrix,从而不需要开发者针对Feign对其进行整合。Feign还提供了HTTP请求的模板,通过编写简单的接口和注解,就可以定义好HTTP请求的参数、格式、地址等信息。Feign会完全代理HTTP的请求,在使用过程中我们只需要依赖注入Bean,然后调用对应的方法传递参数即可。Open Feign地址:http://github.com/OpenFeign/feign Spring Cloud Open Feign地址:http://github.com/spring-cloud/spring-cloud-openfeign

二、入门案例

2.1 案例演示

略。参考:

  • 实战代码演示

2.2 工作原理

通过上面的入门案例可以快速掌握Feign的基本使用,下面介绍一下Feign的工作原理:

  1. 在开发微服务应用时,我们会在主程序入口添加@EnableFeignClients注解开启对Feign Client扫描加载处理。根据Feign Client的开发规范,定义接口并加@FeignClients注解。

  2. 当程序启动时,会进行包扫描,扫描所有@ FeignClients的注解的类,并将这些信息注入Spring IOC容器中。当定义的Feign接口中的方法被调用时,通过JDK的代理的方式,来生成具体的RequestTemplate。当生成代理时,Feign会为每个接口方法创建一个RequetTemplate对象,该对象封装了HTTP请求需要的全部信息,如请求参数名、请求方法等信息都是在这个过程中确定的。

  3. 然后由RequestTemplate生成Request,然后把Request交给Client去处理,这里指的Client可以是JDK原生的URLConnection、Apache的Http Client,也可以是Okhttp。最后Client被封装到LoadBalanceClient类,这个类结合Ribbon负载均衡发起服务之间的调用。

三、Feign的常见功能

3.1 Feign的常见注解说明

FeignClient注解被@Target(ElementType.TYPE)修饰,表示FeignClient注解的作用目标在接口上。当打开org.springframework.cloud.openfeign.FeignClient这个注解定义类的时候,可以看到FeignClient注解对应的属性。
FeignClient注解的常用属性归纳如下:

❑ name:指定FeignClient的名称,如果项目使用了Ribbon, name属性会作为微服务的名称,用于服务发现。
❑ url:url一般用于调试,可以手动指定@FeignClient调用的地址。
❑ decode404:当发生404错误时,如果该字段为true,会调用decoder进行解码,否则抛出FeignException。
❑ configuration:Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract。
❑ fallback:定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口。
❑ fallbackFactory:工厂类,用于生成fallback类示例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码。
❑ path:定义当前FeignClient的统一前缀。

3.2 Feign开启GZIP压缩

Spring Cloud Feign支持对请求和响应进行GZIP压缩,以提高通信效率,这里介绍两种不同的配置方式,即application.yml和application.properties的配置方式。下面将对Feign开启GZIP压缩的配置进行说明。

  1. 具体的application.yml配置信息如下面代码所示。

application.yml

复制代码
1
2
3
4
5
6
7
8
9
feign: compression: request: enabled: true mime-types: text/xml, application/xml, application/json # 配置压缩支持的MIME TYPE min-request-size: 2048 # 配置压缩数据大小的下限 response: enabled: true # 配置响应GZIP压缩

等价的application.properties配置信息如下:

复制代码
1
2
3
4
5
6
7
8
9
# 配置请求GZIP压缩 feign.compression.request.enabled=true # 配置响应GZIP压缩 feign.compression.response.enabled=true # 配置压缩支持的MIME TYPE feign.compression.request.mime-types=text/xml, application/xml, application/json # 配置压缩数据大小的下限 feign.compression.request.min-request-size=2048
  1. 由于开启GZIP压缩之后,Feign之间的调用通过二进制协议进行传输,返回值需要修改为ResponseEntity<byte[]>才可以正常显示,否则会导致服务之间的调用结果乱码,对应的Feign的Client处理如下面代码所示。
复制代码
1
2
3
@RequestMapping(value = "/test", method = RequestMethod.GET) ResponseEntity<byte[]> test(@RequestParam("q") String queryStr);

提示

Spring Cloud的Finchley.RC2版Feign开启GZIP压缩时会出现乱码,但官方已经给出解决方案,扩展阅读可以参考:http://github.com/spring-cloud/spring-cloud-openfeign/issues/33。

  1. 启动主应用程序SpringCloudFeignApplication.java,打开浏览器访问如下URL:http://localhost:8011/test?str=spring-cloud-dubbo,返回结果正常。

3.3 Feign支持属性文件配置

  1. 对单个指定特定名称的Feign进行配置
    @FeignClient的配置信息可以通过application.properties或application.yml来配置,application.yml示例配置信息如下:
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
feign: client: config: feignName: #需要配置的FeignName connectTimeout: 5000 #连接超时时间 readTimeout: 5000 #读超时时间设置 loggerLevel: full #配置Feign的日志级别 errorDecoder: com.example.SimpleErrorDecoder #Feign的错误解码器 retryer: com.example.SimpleRetryer #配置重试 requestInterceptors: #配置拦截器 - com.example.FooRequestInterceptor - com.example.BarRequestInterceptor decode404: false encoder: com.example.SimpleEncoder #Feign的编码器 decoder: com.example.SimpleDecoder #Feign的解码器 contract: com.example.SimpleContract #Feign的Contract配置
  1. 作用于所有Feign的配置方式

@EnableFeignClients注解上有个defaultConfiguration属性,我们可以将默认配置写成一个类,比如这个配置类叫DefaultFeignConfiguration.java,在主程序的启动入口用defaultConfiguration来引用配置,示例配置如下所示:

复制代码
1
2
3
4
5
6
7
8
@SpringBootApplication @EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration.class ) public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); } }

如果想使用application.yml或application.properties来作用于所有Feign也是可以的,示例在application.yml中的配置如下所示:

复制代码
1
2
3
4
5
6
7
8
feign: client: config: default: connectTimeout: 5000 readTimeout: 5000 loggerLevel: basic

注意,如果通过Java代码的方式配置过Feign,然后又通过属性文件的方式配置Feign,属性文件中Feign的配置会覆盖Java代码的配置。但是可以配置feign.client.default-to-properties=false来改变Feign配置生效的优先级。

3.4 Feign Client开启日志

Feign为每一个FeignClient都提供了一个feign.Logger实例,可以在配置中开启日志,开启方式比较简单,分为两步。

第一步:在application.yml或者application.properties中配置日志输出。
在application.yml中设置日志输出级别,如

复制代码
1
2
3
4
logging: level: cn.springcloud.book.feign.service.HelloFeignService: debug

第二步:通过Java代码的方式在主程序入口类中配置日志Bean,代码如下所示:

复制代码
1
2
3
4
5
@Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; }

也可以通过创建带有@Configuration注解的类,去配置日志bean,如下代码所示:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration public class HelloFeignServiceConfig { /** *Logger.Level的具体级别如下: NONE:不记录任何信息 BASIC:仅记录请求方法、URL以及响应状态码和执行时间 HEADERS:除了记录BASIC级别的信息外,还会记录请求和响应的头信息 FULL:记录所有请求与响应的明细,包括头信息、请求体、元数据 */ Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }

3.5 Feign的超时设置

Feign的调用分两层,即Ribbon的调用和Hystrix的调用,高版本的Hystrix默认是关闭的。

复制代码
1
2
3
4
5
6
7
8
9
10
11
feign.RetryableException: Read timed out executing POST http://******* at feign.FeignException.errorExecuting(FeignException.java:67) at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandl er.java:104) at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:76) at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign. java:103) at com.sun.proxy.$Proxy113.getBaseRow(Unknown Source) Caused by: java.net.SocketTimeoutException: Read timed out

如果出现上面的报错信息,说明Ribbon处理超时,此时设置Ribbon的配置信息如下即可。

复制代码
1
2
3
4
5
#获取超时时间 ribbon.ReadTimeout: 120000 #请求连接的超时时间 ribbon.ConnectTimeout: 30000

如果开启Hystrix, Hystrix的超时报错信息如下所示。

复制代码
1
2
3
4
5
6
7
8
9
com.netflix.hystrix.exception.HystrixRuntimeException: FeignDemo#demo() timed- out and no fallback available. at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:819) at com.netflix.hystrix.AbstractCommand$22.call(AbstractCommand.java:804) at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$4.onError(Ope ratorOnErrorResumeNextViaFunction.java:140) at rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber. onError(OnSubscribeDoOnEach.java:87)

看到上面的报错信息,说明Hystrix超时报错,此时设置Hystrix的配置信息如下所示.

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
feign.hystrix.enabled: true # hystrix熔断机制 hystrix: shareSecurityContext: true command: default: circuitBreaker: sleepWindowInMilliseconds: 100000 forceClosed: true execution: isolation: thread: timeoutInMilliseconds: 600000

四、实战和常见问题

4.1 Feign默认Http发送的Client的替换

Feign在默认情况下使用的是JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的persistence connection。我们可以用Apache的HTTP Client替换Feign原始的HTTP Client,通过设置连接池、超时时间等对服务之间的调用调优。Spring Cloud从Brixtion.SR5版本开始支持这种替换,接下来介绍一下如何用HTTP Client和okhttp去替换Feign默认的Client。

  1. 使用HTTP Client替换Feign默认Client

使用HTTP Client替换Feign的默认Client非常简单。主要有以下步骤。

依赖引入

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <! -- Spring Cloud OpenFeign的Starter的依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <! -- 使用Apache HttpClient替换Feign原生httpclient --> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> <dependency> <groupId>com.netflix.feign</groupId> <artifactId>feign-httpclient</artifactId> <version>8.17.0</version> </dependency> </dependencies>

配置
在application.yml中配置让Feign启动时加载HTTP Client替换默认的Client,如下所示。

复制代码
1
2
3
4
5
6
7
8
9
server: port: 8010 spring: application: name: test-httpclient-demo feign: httpclient: enabled: true

启动测试

略。

  1. 使用okhttp替换Feign默认的Client
    HTTP是目前比较通用的网络请求方式,用来访问请求交换数据,有效地使用HTTP可以使应用访问速度变得更快,更节省带宽。okhttp是一个很棒的HTTP客户端,具有以下功能和特性。

❑ 支持SPDY,可以合并多个到同一个主机的请求。
❑ 使用连接池技术减少请求的延迟(如果SPDY是可用的话)。
❑ 使用GZIP压缩减少传输的数据量。
❑ 缓存响应避免重复的网络请求。

依赖管理

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <! -- Spring Cloud OpenFeign的Starter的依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-okhttp</artifactId> </dependency> </dependencies>

配置
开启okhttp为Feign默认的Client。在application.yml中进行如下配置。

复制代码
1
2
3
4
5
6
7
8
9
10
11
server: port: 8011 spring: application: name: test-okhttp-demo feign: httpclient: enabled: false okhttp: enabled: true

okHttpClient是okhttp的核心功能的执行者,可以通过OkHttpClient client=new OkHttp Client();来创建默认的OkHttpClient对象,也可以使用如下代码来构建自定义的OkHttpClient对象,上面配置信息只给出了常用的设置项,其他设置项比较复杂,有兴趣的读者可以自己扩展阅读学习,具体okHttp的配置设置如下所示。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Configuration @ConditionalOnClass(Feign.class) @AutoConfigureBefore(FeignAutoConfiguration.class) public class FeignOkHttpConfig { @Bean public okhttp3.OkHttpClient okHttpClient(){ return new okhttp3.OkHttpClient.Builder() //设置连接超时 .connectTimeout(60, TimeUnit.SECONDS) //设置读超时 .readTimeout(60, TimeUnit.SECONDS) //设置写超时 .writeTimeout(60, TimeUnit.SECONDS) //是否自动重连 .retryOnConnectionFailure(true) .connectionPool(new ConnectionPool()) //构建OkHttpClient对象 .build(); } }

启动测试

略。

4.2 解决Feign首次请求失败问题

当Feign和Ribbon整合了Hystrix之后,可能会出现首次调用失败的问题,造成该问题出现的原因分析如下:

Hystrix默认的超时时间是1秒,如果超过这个时间尚未做出响应,将会进入fallback代码。由于Bean的装配以及懒加载机制等,Feign首次请求都会比较慢。如果这个响应时间大于1秒,就会出现请求失败的问题。下面以feign为例,介绍三种方法处理Feign首次请求失败的问题。

方法一 将Hystrix的超时时间改为5秒,配置如下所示:

复制代码
1
2
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000

方法二 禁用Hystrix的超时时间,配置如下所示:

复制代码
1
2
hystrix.command.default.execution.timeout.enabled: false

方法三 使用Feign的时候直接关闭Hystrix,该方式不推荐使用。

复制代码
1
2
feign.hystrix.enabled: false

说明
针对Feign首次请求失败的问题,可参考如下链接:https://github.com/spring-cloud/spring-cloud-netflix/issues/768

4.3 Feign返回图片流处理方式

通过Feign返回图片一般为字节数组,如下列代码所示。

复制代码
1
2
3
4
// 生成图片验证码 @RequestMapping(value = "createImageCode") public byte[] createImageCode(@RequestParam("imagekey") String imagekey);

在使用Feign的过程中可以将流转成字节数组传递,但是因为Controller层的返回不能直接返回byte,因此需要将Feign的返回值修改为response,示例代码如下。

复制代码
1
2
3
4
@RequestMapping(value = "createImageCode") public Response createImageCode(@RequestParam("imagekey") String imagekey);

4.4 Feign调用传递Token

在进行认证鉴权的时候,不管是jwt,还是security,当使用Feign时就会发现外部请求到A服务的时候,A服务是可以拿到Token的,然而当服务使用Feign调用B服务时,Token就会丢失,从而认证失败。解决方法相对比较简单,需要做的就是在Feign调用的时候,向请求头里面添加需要传递的Token。

我们只需要实现Feign提供的一个接口RequestInterceptor,假设我们在验证权限的时候放在请求头里面的key为oauthToken,先获取当前请求中的key为oauthToken的Token,然后放到Feign的请求Header上,如下代码所示。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Feign统一Token拦截器 @Component public class FeignTokenInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate requestTemplate) { if(null==getHttpServletRequest()){ return; } //将获取Token对应的值往下面传 requestTemplate.header("oauthToken", getHeaders(getHttpServletRequest() ).get("oauthToken")); } private HttpServletRequest getHttpServletRequest() { try { return ((ServletRequestAttributes) RequestContextHolder.getRequest- Attributes()).getRequest(); } catch (Exception e) { return null; } } }

具体使用案例可参考:Feign实战例子,喜欢的老铁给个start!!!

参考书籍:《重新定义Spring Cloud实战》

最后

以上就是快乐睫毛膏最近收集整理的关于SpringCloud之Feign使用和拓展的全部内容,更多相关SpringCloud之Feign使用和拓展内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(93)

评论列表共有 0 条评论

立即
投稿
返回
顶部