Spring Boot 进阶 | 01 使用Feign作为HTTP客户端调用远程HTTP服务

Feign 简介

Spring Cloud 的 Feign 支持的一个中心概念就是命名客户端。Feign客户端使用@FeignClient 注册组合成组件,按需调用远程服务器。使用 FeignClientsConfiguration 创建一个新的集合作为每个命名客户端的ApplicationContext,包含 feign.Decoderfeign.Encoderfeign.Contract

可以使用 JerseyCXF 这些来写一个 Rest 或 SOAP 服务的java客服端,也可以直接使用 Apache HttpClient 来实现。但是 Feign 的目的是尽量的减少资源和代码来实现和 HTTP API 的连接。通过自定义的编码解码器以及错误处理,你可以编写任何基于文本的 HTTP API。

Feign 通过注解注入一个模板化请求进行工作。只需在发送之前关闭它,参数就可以被直接的运用到模板中。然而这也限制了 Feign,只支持文本形式的API,它在响应请求等方面极大的简化了系统。同时,它也是十分容易进行单元测试的。

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

maven依赖

<dependencies>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>

Feign客户端接口(消费者)

使用 @FeignClient 请发起请求:

@FeignClient(value = "user", url = "${addr.url}")
public interface UserClient {
        // Feign 独有的注解方式 
        @RequestLine("GET /user/index")
        String index();

        @RequestMapping(value = "/get0/{id}", method = RequestMethod.GET)
        User findById(@PathVariable("id") Long id);

        @RequestMapping(value = "/get1", method = RequestMethod.GET)
        User get1(@RequestParam("id") Long id, @RequestParam("name") String name);

        @RequestMapping(value = "/get2", method = RequestMethod.GET)
        User get2(@RequestParam Map<String, Object> map);

        @RequestMapping(value = "/hello2", method=RequestMethod.GET)
        User hello2(@RequestHeader("name") String name, @RequestHeader("age") Integer age);
        
        @RequestMapping(value = "/hello3", method=RequestMethod.POST)
        String hello3(@RequestBody User user);
}

当前工程中有和 Feign Client 中一样的 Endpoint 时,Feign Client 的类上不能用 @RequestMapping 注解,否则当前工程该 endpoint http 请求且使用 accpet 时会报404。但是如果不包含 Accept header 时,请求是可以的。
不在 Feign Client 上使用 @RequestMapping 注解,无论是否包含Accept,请求都是可以的:

@FeignClient(name = "card", url = "http://localhost:7913", 
	fallback = CardFeignClientFallback.class,
	configuration = FeignClientConfiguration.class)
public interface CardFeignClient {
    @RequestMapping(value = "/v1/card/balance", method = RequestMethod.POST, 
                produces = MediaType.APPLICATION_JSON_VALUE)
    Info info();
}

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

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

配置文件application.yml

#feign
feign:
  hystrix:
    enabled: true
  httpclient:
    enabled: true
addr:
  url: http://10.164.13.166:8080/msg-center/v1/sms/send

配置类Configuration

使用了配置 @Configuration 参数,自己定义 Configuration 类来自定义 FeignClientsConfiguration,并且 Configuration 类的类路径不能在启动类 Application 的扫描路径下,否则会覆盖该项目所有的 Feign 接口的默认配置

package com.spring.feigin.config;

import feign.auth.BasicAuthRequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class EurekaConfiguration {
    // 配置只允许使用Feign自己的注解url方式:@RequestLine
    @Bean
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor("zhihao.miao", "123456");
    }
    // 配置eureka的登录名和密码
    @Bean
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
        return new BasicAuthRequestInterceptor("zhihao.miao", "123456");
    }
}

定义主体启动类

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {
    public static void main(String[] args) {
       SpringApplication.run(OrderApplication.class,args);
    }
}

feign使用Hystrix

添加依赖
<!-- 整合hystrix,其实feign中自带了hystrix,引入该依赖主要是为了使用其中的hystrix-metrics-event-stream,用于dashboard -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
feign接口注解中,增加fallback指定类
@FeignClient (value = "${name}",  url = "${addr.url}", fallback = UserFallBack.class)
指定类中处理熔断的后续逻辑
@Slf4j
@Component
public class PosMemberClientFallBack implements PosMemberClient {
    @Override
    public String addMember(MemberDTO memberDTO) {
        log.warn("调用会员服务失败");
        return ("调用服务失败,熔断”);
    }
}
配置文件
#hystrix
hystrix:
  command:
    default:
      execution:
         isolation:
           strategy: THREAD
           thread:
             timeoutInMilliseconds: 30000
   threadpool:
     default:
       coreSize: 500 #缺省为10
修改启动类

在启动类上添加@EnableHystrix 注解

@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
@EnableHystrix
public class StartApplication {
 public static void main(String[] args) {
 SpringApplication.run(StartApplication.class, args);
 }
}

配置的fallback class也必须在FeignClient Configuration中实例化,否则会报
java.lang.IllegalStateException: No fallback instance of type class异常。

 @Configuration
    public class FooConfiguration {
        @Bean
        @Scope("prototype")
        public Feign.Builder feignBuilder() {
            return Feign.builder();
        }
        //实例化fallback
        @Bean
        public HystrixClientFallback fb(){
            return new HystrixClientFallback();
        }
    }

在 Spring Cloud 中,Feign 和Ribbon 在整合了 Hystrix 后,可能会出现首次调用失败的问题,要如何解决该问题呢?
Hystrix 默认的超时时间是1秒,如果超过这个时间尚未响应,将会进入 fallback 代码。
而首次请求往往会比较慢(因为 Spring 的懒加载机制,要实例化一些类),这个响应时间可能就大于1秒了。
解决方案 :
方法一
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
该配置是让Hystrix的超时时间改为5秒
方法二
hystrix.command.default.execution.timeout.enabled: false
该配置,用于禁用Hystrix的超时时间
方法三
feign.hystrix.enabled: fals
该配置,用于索性禁用feign的hystrix。该做法除非一些特殊场景,不推荐使用。

Feign的扩展配置

#Hystrix支持,如果为true,hystrix库必须在classpath中
feign:
	hystrix:
		enabled: false
#请求和响应GZIP压缩支持
	compression:
		request:
			enabled: true
		response:
			enabled: true
#支持压缩的mime types
	compression:
		request:
			enabled: true
		mime-types: text/xml,application/xml,application/json
		min-request-size: 2048

# 日志支持
logging:
	level:
		project:
			user:
				UserClient: DEBUG
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值