Spring Cloud(Finchley.SR1)之feign(四)

1.Feign简介

目录

1.Feign简介

2.使用Feign

2.1 pom.xml中添加

2.2 定义接口

2.3 启动类

2.4 application.yml文件

2.5 覆盖Feign默认值,自定义FeignClient

2.6 .Feign 相关配置

3. Feign的Encoder、Decoder和ErrorDecoder

Feign的HTTP Client

总结


Feign是一个声明式的Web服务客户端。这使得Web服务客户端的写入更加方便 要使用Feign创建一个界面并对其进行注释。它具有可插入注释支持,包括Feign注释和JAX-RS注释。Feign还支持可插拔编码器和解码器。Spring Cloud增加了对Spring MVC注释的支持,并使用Spring Web中默认使用的HttpMessageConverters。Spring Cloud集成Ribbon和Eureka以在使用Feign时提供负载均衡的http客户端。

在Spring Cloud Netflix栈中,各个微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端。我们可以使用JDK原生的URLConnection、Apache的Http Client、Netty的异步HTTP Client, Spring的RestTemplate。但是,用起来最方便、最优雅的还是要属Feign了。

Feign是一种声明式、模板化的HTTP客户端。在Spring Cloud中使用Feign, 我们可以做到使用HTTP请求远程服务时能与调用本地方法一样的编码体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。

现在为止所进行的 有Rest 服务调用实际上都会出现一个非常尴尬的局面,所有的数据调用和转换都必须由户自己来完成,而我们本身不擅 所有的数据调用和转换都必须由户自己来完成,我们习惯的编程模式是:通过接口来实现业务 长这些,我们习惯的编程模式是:通过接口来实现业务 ,这可以通过feign来实现,Feign = RestTempate + HttpHeader Ribbon Eureka综合体 综合体 = 业务接口的自动实例化。

 

2.使用Feign

2.1 pom.xml中添加

老版本用spring-cloud-starter-feign,Finchley.SR1版本的用spring-cloud-starter-openfeign

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-cloud</artifactId>
        <groupId>com.alen</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
    <artifactId>feign-service</artifactId>
    <dependencies>
        <!--eureka client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
        </dependency>
        <!--feign支持-->
          <!--feign支持-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

 

2.2 定义接口

为了让Feign知道在调用方法时应该向哪个地址发请求以及请求需要带哪些参数,我们需要定义一个接口:

package com.alen.service;

import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * 定义一个feign接口,通过@ FeignClient(“服务名”),来指定调用哪个服务
 **/
@FeignClient(value ="eureka-client")
public interface HelloService {

    @RequestMapping("/hello")
     //必须显示的指定age,不显示还不行
      String hello(@RequestParam("age") Integer age);
}

A: @FeignClient用于通知Feign组件对该接口进行代理(不需要编写接口实现),使用者可直接通过@Autowired注入。

B: @RequestMapping表示在调用该方法时需要向/group/{groupId}发送GET请求。

C: @PathVariableSpringMVC中对应注解含义相同。

Spring Cloud应用在启动时,Feign会扫描标有@FeignClient注解的接口,生成代理,并注册到Spring容器中。生成代理时Feign会为每个接口方法创建一个RequetTemplate对象,该对象封装了HTTP请求需要的全部信息,请求参数名、请求方法等信息都是在这个过程中确定的,Feign的模板化就体现在这里。

在本例中,我们将Feign与Eureka和Ribbon组合使用,@FeignClient(name = "ea")意为通知Feign在调用该接口方法时要向Eureka中查询名为ea的服务,从而得到服务URL。

controller中调用这个接口

@RestController
public class HelloController {
    @Autowired
    private HelloService  helloService;
    /**
     * 通过Controller调用远程服务
     * @param age
     * @return
     */
    @RequestMapping("/hello")
    public String home(@RequestParam Integer age) {
        return helloService.hello(age);
    }
}

 

2.3 启动类

@SpringBootApplication
//通过注解@EnableEurekaClient 表明自己是一个eurekaclient.
@EnableEurekaClient
//开启Feign的功能
@EnableFeignClients
public class App {
   public static void main(String[] args) {
      SpringApplication.run(App.class, args);
   }
}

 

2.4 application.yml文件

server:
  port: 8014
eureka:
  instance:
    hostname: localhost
    # 发呆时间,即服务失效时间(缺省为90s),就是超过15秒没有续约就会从注册表中剔除
    lease-expiration-duration-in-seconds: 15
    # 心跳时间,即服务续约间隔时间(缺省为30s)
    lease-renewal-interval-in-seconds: 5
  client:
   #设置eureka服务器所在的地址,查询服务和注册服务都需要依赖这个地址。高可用的注册中心时,
    #可以配置多个注册中心,通过逗号隔开
    service-url:
     defaultZone: http://localhost:8010/eureka/ 

 

Feign的源码实现的过程如下:

  • 首先通过@EnableFeignCleints注解开启FeignCleint
  • 根据Feign的规则实现接口,并加@FeignCleint注解
  • 程序启动后,会进行包扫描,扫描所有的@ FeignCleint的注解的类,并将这些信息注入到ioc容器中。
  • 当接口的方法被调用,通过jdk的代理,来生成具体的RequesTemplate
  • RequesTemplate在生成Request
  • Request交给Client去处理,其中Client可以是HttpUrlConnection、HttpClient也可以是Okhttp
  • 最后Client被封装到LoadBalanceClient类,这个类结合类Ribbon做到了负载均衡。

2.5 覆盖Feign默认值,自定义FeignClient

默认的配置类为FeignClientsConfiguration,这个类在spring-cloud-netflix-core的jar包下,打开这个类,可以发现它是一个配置类,注入了很多的相关配置的bean,包括feignRetryer、FeignLoggerFactory、FormattingConversionService等,其中还包括了Decoder、Encoder、Contract,如果这三个bean在没有注入的情况下,会自动注入默认的配置。

  • Decoder feignDecoder: ResponseEntityDecoder(这是对SpringDecoder的封装)
  • Encoder feignEncoder: SpringEncoder
  • Logger feignLogger: Slf4jLogger
  • Contract feignContract: SpringMvcContract
  • Feign.Builder feignBuilder: HystrixFeign.Builder
@Configuration
public class FeignClientsConfiguration {

...//省略代码

@Bean
    @ConditionalOnMissingBean
    public Decoder feignDecoder() {
        return new ResponseEntityDecoder(new SpringDecoder(this.messageConverters));
    }

    @Bean
    @ConditionalOnMissingBean
    public Encoder feignEncoder() {
        return new SpringEncoder(this.messageConverters);
    }

    @Bean
    @ConditionalOnMissingBean
    public Contract feignContract(ConversionService feignConversionService) {
        return new SpringMvcContract(this.parameterProcessors, feignConversionService);
    }

...//省略代码
}

重写配置:

你可以重写FeignClientsConfiguration中的bean,从而达到自定义配置的目的,比如FeignClientsConfiguration的默认重试次数为Retryer.NEVER_RETRY,即不重试,那么希望做到重写,写个配置文件,注入feignRetryer的bean,代码如下:

@Configuration
public class FeignClientConfig{

    @Bean
    public Retryer feignRetryer() {
        return new Retryer.Default(100, SECONDS.toMillis(1), 5);
    }

}

在上述代码更改了该FeignClient的重试次数,重试间隔为100ms,最大重试时间为1s,重试次数为5次。

在这种情况下,客户端由FeignClientsConfiguration中的组件与FeignClientConfig中的任何组件组成(后者将覆盖前者)。

需要注意的是: FeignClientConfig类不能包含在主应用程序上下文的@ComponentScan中,否则该配置会被所有的@FeignClient共享。Ribbon的自定义配置中也需要注意这个问题。可以通过将其放置在任何@ComponentScan或@SpringBootApplication的单独的不重叠的包中,或者可以在@ComponentScan中明确排除。

2.6 .Feign 相关配置

Feign之中最为核心的作用就是将 Rest 服务的信息转换为接口,但是在实际使用之中也需要 考虑到一些配置情况, 例如:数据压缩,Rest 的核心本质在于: JSON 数据传输( XML、文本),于是就必须思考一种情况,玩意 、文本),用户发送的数据很大呢? 所以这个时候可考虑修改 application.yml 配置文件对传输数据进行压缩; 配置文件对传输数据进行压缩;

feign:
   compression:
         request:
               mime-types: # 可以被压缩的类型
#                 - text/xml
#                 - application/xml
#                 - application/json
                min-request-size: 2048 # 超过2048的字节进行压缩

2、 如果有需要则可以在项目之中开启  feign的相关日志信息(默认不开启) :

为每个创建的Feign客户端创建一个记录器。默认情况下,记录器的名称是用于创建Feign客户端的接口的完整类名。Feign日志记录仅响应DEBUG级别
修改 application.yml配置文件,追加日志追踪:

logging:
     level:
#      feign接口包所在位置
        cn.mldn.service: DEBUG

修改 FeignClientConfig ,开启日志的输出:

您可以为每个客户端配置的Logger.Level对象告诉Feign记录多少。选择是:

  • NONE,无记录(DEFAULT)。

  • BASIC,只记录请求方法和URL以及响应状态代码和执行时间。

  • HEADERS,记录基本信息以及请求和响应标头。

  • FULL,记录请求和响应的头文件,正文和元数据。

例如,以下将Logger.Level设置为FULL

 
@Configuration
public class FeignClientConfig {
    @Bean
    public Logger.Level getFeignLoggerLevel() {
        return feign.Logger.Level.FULL ;
    }
   
}

 

3. Feign的Encoder、Decoder和ErrorDecoder

Feign将方法签名中方法参数对象序列化为请求参数放到HTTP请求中的过程,是由编码器(Encoder)完成的。同理,将HTTP响应数据反序列化为java对象是由解码器(Decoder)完成的。

默认情况下,Feign会将标有@RequestParam注解的参数转换成字符串添加到URL中,将没有注解的参数通过Jackson转换成json放到请求体中。注意,如果在@RequetMapping中的method将请求方式指定为POST,那么所有未标注解的参数将会被忽略,例如:

@RequestMapping(value = "/group/{groupId}", method = RequestMethod.GET)
void update(@PathVariable("groupId") Integer groupId, @RequestParam("groupName") String groupName, DataObject obj);

此时因为声明的是GET请求没有请求体,所以obj参数就会被忽略。

在Spring Cloud环境下,Feign的Encoder*只会用来编码没有添加注解的参数*。如果你自定义了Encoder, 那么只有在编码obj参数时才会调用你的Encoder。对于Decoder, 默认会委托给SpringMVC中的MappingJackson2HttpMessageConverter类进行解码。只有当状态码不在200 ~ 300之间时ErrorDecoder才会被调用。ErrorDecoder的作用是可以根据HTTP响应信息返回一个异常,该异常可以在调用Feign接口的地方被捕获到。我们目前就通过ErrorDecoder来使Feign接口抛出业务异常以供调用者处理。

Feign的HTTP Client

Feign在默认情况下使用的是JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的persistence connection 。我们可以用Apache的HTTP Client替换Feign原始的http client, 从而获取连接池、超时时间等与性能息息相关的控制能力。Spring Cloud从Brixtion.SR5版本开始支持这种替换,首先在项目中声明Apache HTTP Client和feign-httpclient依赖:

<!-- 使用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>${feign-httpclient}</version>
</dependency>

然后在application.properties中添加:

feign.httpclient.enabled=true

总结

通过Feign, 我们能把HTTP远程调用对开发者完全透明,得到与调用本地方法一致的编码体验。这一点与阿里Dubbo中暴露远程服务的方式类似,区别在于Dubbo是基于私有二进制协议,而Feign本质上还是个HTTP客户端。如果是在用Spring Cloud Netflix搭建微服务,那么Feign无疑是最佳选择。

参考:

http://blog.csdn.net/neosmith/article/details/52449921

https://springcloud.cc/spring-cloud-dalston.html#spring-cloud-feign

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值