(四)Spring Cloud Feign 服务远程调用

OpenFeign 简介

微服务提倡将一个原本独立的系统分成众多小型服务系统,这些小型服务系统都在独立的进程中运行,通过各个小型服务系统之间的协作来实现原本独立系统的所有业务功能。拆分后的微服务系统使用多种跨进程的方式进行通信协作,在使用 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 的使用更加方便。

什么是 OpenFeign

OpenFeign 是一个声明式 HTTP 网络请求客户端。 OpenFeign 提供了HTTP 请求的模板,通过编写简单的接口和插入注解,就可以定义好 HTTP 请求的参数、格式、地址等信息。Feign 会完全代理 HTTP 请求,我们只需要像调用方法一样调用它就可以完成服务请求及相关处理。Spring Cloud 对 Feign 进行了封装,使其支持 SpringMVC 标准注解和 HttpMessageConverters。OpenFeign 主要特性如下:

  • 可插拔的注解支持, 包括 Feign 注解和 JAX-RS 注解。
  • 支持可插拔的 HTTP 编码器和解码器。
  • 支持 Hystrix 和它的 Fallback。
  • 支持 Ribbon 的负载均衡。
  • 支持 HTTP 请求和响应的压缩 。

使用 OpenFeign

在 Spring Cloud 中集成 OpenFeign 的步骤很简单,首先还是先引入依赖,代码如下:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

在启动类上添加 @EnableFeignClients 注解,如果 Feign 接口定义和启动类不在同一个路径下,还需要通过 basePackages 来指定扫描的包名。@EnableFeignClients 就像是一个开关,只有使用了该注解,OpenFeign 相关组件和配置机制才会生效。

@EnableFeignClients(basePackages = "com.yuxuan")
@EnableDiscoveryClient
@SpringBootApplication
public class FeignApplication {

    public static void main(String[] args) {
        SpringApplication.run(FeignApplication.class,args);
    }

}

使用 Feign 调用接口

/**
 * 订单服务远程调用客户端
 **/
@FeignClient(value = "order-service")
public interface OrderClient {

    @GetMapping("/order/add")
    String addOrder();

}

@FeignClient 注解就是用来标记该接口是一个 Feign 远程调用客户端,value 属性对应的是服务名称,代表需要调用那个服务中的接口
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 的统一前缀。

自定义 Feign 的配置

日志配置

有时候遇到接口调用失败,参数没收到等问题,或者想看看调用性能。这个时候就需要配置 Feign 的日志级别,让 Feign 把请求信息输出到日志中。代码如下:

public class FeignConfig {

    /**
     * 日志级别
     * NONE: 不输出日志
     * BASIC: 只输出请求方法的 URL 和响应的状态码以及接口执行的时间
     * HEADERS:将 BASIC 信息和请求头信息输出
     * FULL:输出完整的请求信息
     * @return
     */
    @Bean
    Logger.Level feignLoggerLevel(){
        return Logger.Level.FULL;
    }

}

然后在 @FeignClient 注解中指定使用的配置类

@FeignClient(value = "order-service",configuration = FeignConfig.class)
public interface OrderClient {

    @GetMapping("/order/add")
    String addOrder();

}

在配置文件中指定 Client 类的日志级别,格式为"logging.level.client类路径"

logging:
  level:
    com:
      yuxuan:
        feign:
          OrderClient: debug

FeignConfig 类上如果添加 @Configuration 注解,则不需要在 Client 上再次指定配置类,默认会作用到所有的 FeignClien 上

契约配置

Spring Cloud 在 Feign 的基础上做了扩展,可以让 Feign 支持 Spring MVC 的注解来调用。如果想在 Spring Cloud 中使用原生的注解方式来定义客户端也是可以的,通过配置契约来改变这个配置,Spring Cloud 中默认的是 SpringMvcContract。

@Bean
public Contract feignContract(){
    return new feign.Contract.Default();
}

Basic 认证配置

通常调用的接口都是有权限控制的,很多时候可能认证的值是通过参数去传递的,还有就是通过请求头去传递认证信息,比如 Basic 认证方式。在 Feign 中我们可以直接配置 Basic 认证。

@Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor(){
    return new BasicAuthRequestInterceptor("user","password");
}

也可以自定义请求拦截器,通过实现 RequestInterceptor 接口来实现。

/**
 * feign 请求拦截器
 * @Author yuxuan
 **/
@Component
public class CustomerFeignInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        // 具体业务逻辑
    }
}

超时时间配置

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

image.png

如果出现上面报错信息,代表 Ribbon 处理超时,添加 Ribbon 配置即可:

ribbon:
  ConnectTimeout: 2000  #请求连接的超时时间
  ReadTimeout: 5000  #请求处理的超时时间
image.png

如果出现上面报错信息,则代表 Hystrix 超时报错,设置 Hystrix 配置即可:

feign
  hystrix:
    enabled: true
hystrix:
  shareSecurityContext: true
  command:
    default:
      circuitBreaker:
        sleepWindowInMilliseconds: 100000
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 60000

通过 Options 可以配置连接超时时间和读取超时时间,Options 的第一个参数是连接超时时间(ms),默认值是 10 × 1000 ; 第二个是取超时时间(ms),默认 60 × 1000

@Bean
public Request.Options options(){
    return new Request.Options(5000,10000);
}

客户端组件配置

Feign 中默认使用 JDK 原生的 URLConnection 发送 HTTP 请求,我们可以集成别的组件来替换掉 URLConnection,比如 Apache HttpClient, OkHttp。

配置 OkHttp 只需要加入 OkHttp 的依赖。

<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>

修改配置,将 Feign 的 HttpClient 禁用,启用 OkHttp

feign:
  httpclient:
    enabled: false
  okhttp:
    enabled: true

关于配置可参考源码 org.springframework.cloud.openfeign .FeignAutoConfiguration

添加配置之后就会创建默认的 OkHttpClient 对象,当然也可以构建自定义的 OkHttpClient,代码如下。

@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())
                .build();
    }
}

GZIP 压缩配置

开启压缩可以有效节约网络资源,提升接口性能,我们可以配置 GZIP 来压缩数据

feign:
  compression:
    request:
      ##开启请求数据压缩
      enabled: true
      ## 指定压缩类型
      mime-types: text/xml,application/xml,application/json
      ## 最小压缩值
      min-request-size: 2048
    response:
     ##开启响应数据压缩
      enabled: true

只有当 Feign 的 HttpClient 不是 okhttp3 的时候,压缩才会生效,配置源码在 org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration
核心代码就是 @ConditionalOnMissingBean(type = "okhttp3.OkHttpClient") 表示 Spring BeanFactory 中不包含指定的 bean 时条件匹配, 也就是没有启用 okhttp3 时才会进行压缩配置。

配置文件自定义 Feign 的配置

# 链接超时时间
feign.client.config.feignName.connectTimeout=5000
# 读取超时时间
feign.client.config.feignName.readTimeout=5000
# 日志等级
feign.client.config.feignName.loggerLevel=full
# 重试
feign.client.config.feignName.retryer=com.example.SimpleRetryer
# 拦截器
feign.client.config.feignName.requestInterceptors[0]=com.example.FooRequestInterceptor
feign.client.config.feignName.requestInterceptors[1]=com.example.BarRequestInterceptor
# 编码器
feign.client.config.feignName.encoder=com.example.SimpleEncoder
# 解码器
feign.client.config.feignName.decoder=com.example.SimpleDecoder
# 契约
feign.client.config.feignName.contract=com.example.SimpleContract

feignName 需要替换成指定的 feignclient 名称

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容