一、原理

Feign 是一个 Java 到 HTTP 的客户端绑定器,灵感来自于 Retrofit 和 JAXRS-2.0 以及 WebSocket。Feign 的第一个目标是降低将 Denominator 无变化的绑定到 HTTP APIs 的复杂性,而不考虑 ReSTfulness

Feign 使用 Jersey 和 CXF 等工具为 ReST 或 SOAP 服务编写 java 客户端。此外,Feign 允许您在 Apache HC 等http 库之上编写自己的代码。Feign 以最小的开销将代码连接到 http APIs,并通过可定制的解码器和错误处理(可以写入任何基于文本的 http APIs)将代码连接到 http APIs。

Feign 通过将注解处理为模板化请求来工作。参数在输出之前直接应用于这些模板。尽管 Feign 仅限于支持基于文本的 APIs,但它极大地简化了系统方面,例如重放请求。此外,Feign 使得对转换进行单元测试变得简单。

Feign 10.x 及以上版本是在 Java 8上构建的,应该在 Java 9、10 和 11上工作。对于需要 JDK 6兼容性的用户,请使用 Feign 9.x

二、处理过程图

三、Http Client 依赖

feign 在默认情况下使用 JDK 原生的 URLConnection 发送HTTP请求。(没有连接池,保持长连接) 。

可以通过修改 client 依赖换用底层的 client,不同的 http client 对请求的支持可能有差异。具体使用示例如下:

feign: httpclient:enable: falseokhttp:enable: true

AND

<!-- Support PATCH Method-->
<dependency>    <groupId>org.apache.httpcomponents</groupId>    <artifactId>httpclient</artifactId>
</dependency><!-- Do not support PATCH Method -->
<dependency><groupId>io.github.openfeign</groupId><artifactId>feign-okhttp</artifactId>
</dependency>

四、Http Client 配置

  • okhttp 配置源码
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
public class OkHttpFeignConfiguration {private okhttp3.OkHttpClient okHttpClient;@Bean@ConditionalOnMissingBean(ConnectionPool.class)public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,OkHttpClientConnectionPoolFactory connectionPoolFactory) {Integer maxTotalConnections = httpClientProperties.getMaxConnections();Long timeToLive = httpClientProperties.getTimeToLive();TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);}@Beanpublic okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,ConnectionPool connectionPool,FeignHttpClientProperties httpClientProperties) {Boolean followRedirects = httpClientProperties.isFollowRedirects();Integer connectTimeout = httpClientProperties.getConnectionTimeout();this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation()).connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects).connectionPool(connectionPool).build();return this.okHttpClient;}@PreDestroypublic void destroy() {if (this.okHttpClient != null) {this.okHttpClient.dispatcher().executorService().shutdown();this.okHttpClient.connectionPool().evictAll();}}
}
  • HttpClient 配置源码
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(CloseableHttpClient.class)
public class HttpClientFeignConfiguration {private final Timer connectionManagerTimer = new Timer("FeignApacheHttpClientConfiguration.connectionManagerTimer", true);private CloseableHttpClient httpClient;@Autowired(required = false)private RegistryBuilder registryBuilder;@Bean@ConditionalOnMissingBean(HttpClientConnectionManager.class)public HttpClientConnectionManager connectionManager(ApacheHttpClientConnectionManagerFactory connectionManagerFactory,FeignHttpClientProperties httpClientProperties) {final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(httpClientProperties.isDisableSslValidation(),httpClientProperties.getMaxConnections(),httpClientProperties.getMaxConnectionsPerRoute(),httpClientProperties.getTimeToLive(),httpClientProperties.getTimeToLiveUnit(), this.registryBuilder);this.connectionManagerTimer.schedule(new TimerTask() {@Overridepublic void run() {connectionManager.closeExpiredConnections();}}, 30000, httpClientProperties.getConnectionTimerRepeat());return connectionManager;}@Bean@ConditionalOnProperty(value = "feign.compression.response.enabled",havingValue = "true")public CloseableHttpClient customHttpClient(HttpClientConnectionManager httpClientConnectionManager,FeignHttpClientProperties httpClientProperties) {HttpClientBuilder builder = HttpClientBuilder.create().disableCookieManagement().useSystemProperties();this.httpClient = createClient(builder, httpClientConnectionManager,httpClientProperties);return this.httpClient;}@Bean@ConditionalOnProperty(value = "feign.compression.response.enabled",havingValue = "false", matchIfMissing = true)public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,HttpClientConnectionManager httpClientConnectionManager,FeignHttpClientProperties httpClientProperties) {this.httpClient = createClient(httpClientFactory.createBuilder(),httpClientConnectionManager, httpClientProperties);return this.httpClient;}private CloseableHttpClient createClient(HttpClientBuilder builder,HttpClientConnectionManager httpClientConnectionManager,FeignHttpClientProperties httpClientProperties) {RequestConfig defaultRequestConfig = RequestConfig.custom().setConnectTimeout(httpClientProperties.getConnectionTimeout()).setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build();CloseableHttpClient httpClient = builder.setDefaultRequestConfig(defaultRequestConfig).setConnectionManager(httpClientConnectionManager).build();return httpClient;}@PreDestroypublic void destroy() throws Exception {this.connectionManagerTimer.cancel();if (this.httpClient != null) {this.httpClient.close();}}
}
  • HttpClient 配置属性
@ConfigurationProperties(prefix = "feign.httpclient")
public class FeignHttpClientProperties {/*** Default value for disabling SSL validation.*/public static final boolean DEFAULT_DISABLE_SSL_VALIDATION = false;/*** Default value for max number od connections.*/public static final int DEFAULT_MAX_CONNECTIONS = 200;/*** Default value for max number od connections per route.*/public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 50;/*** Default value for time to live.*/public static final long DEFAULT_TIME_TO_LIVE = 900L;/*** Default time to live unit.*/public static final TimeUnit DEFAULT_TIME_TO_LIVE_UNIT = TimeUnit.SECONDS;/*** Default value for following redirects.*/public static final boolean DEFAULT_FOLLOW_REDIRECTS = true;/*** Default value for connection timeout.*/public static final int DEFAULT_CONNECTION_TIMEOUT = 2000;/*** Default value for connection timer repeat.*/public static final int DEFAULT_CONNECTION_TIMER_REPEAT = 3000;private boolean disableSslValidation = DEFAULT_DISABLE_SSL_VALIDATION;private int maxConnections = DEFAULT_MAX_CONNECTIONS;private int maxConnectionsPerRoute = DEFAULT_MAX_CONNECTIONS_PER_ROUTE;private long timeToLive = DEFAULT_TIME_TO_LIVE;private TimeUnit timeToLiveUnit = DEFAULT_TIME_TO_LIVE_UNIT;private boolean followRedirects = DEFAULT_FOLLOW_REDIRECTS;private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;private int connectionTimerRepeat = DEFAULT_CONNECTION_TIMER_REPEAT;//省略 setter 和 getter 方法
}

五、部分注解

  • FeignClient 注解源码
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignClient {// 忽略了过时的属性/*** The name of the service with optional protocol prefix. Synonym for {@link #name()* name}. A name must be specified for all clients, whether or not a url is provided.* Can be specified as property key, eg: ${propertyKey}.* @return the name of the service with optional protocol prefix*/@AliasFor("name")String value() default "";/*** This will be used as the bean name instead of name if present, but will not be used* as a service id.* @return bean name instead of name if present*/String contextId() default "";/*** @return The service id with optional protocol prefix. Synonym for {@link #value()* value}.*/@AliasFor("value")String name() default "";/*** @return the <code>@Qualifier</code> value for the feign client.*/String qualifier() default "";/*** @return an absolute URL or resolvable hostname (the protocol is optional).*/String url() default "";/*** @return whether 404s should be decoded instead of throwing FeignExceptions*/boolean decode404() default false;/*** A custom configuration class for the feign client. Can contain override* <code>@Bean</code> definition for the pieces that make up the client, for instance* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.** @see FeignClientsConfiguration for the defaults* @return list of configurations for feign client*/Class<?>[] configuration() default {};/*** Fallback class for the specified Feign client interface. The fallback class must* implement the interface annotated by this annotation and be a valid spring bean.* @return fallback class for the specified Feign client interface*/Class<?> fallback() default void.class;/*** Define a fallback factory for the specified Feign client interface. The fallback* factory must produce instances of fallback classes that implement the interface* annotated by {@link FeignClient}. The fallback factory must be a valid spring bean.** @see feign.hystrix.FallbackFactory for details.* @return fallback factory for the specified Feign client interface*/Class<?> fallbackFactory() default void.class;/*** @return path prefix to be used by all method-level mappings. Can be used with or* without <code>@RibbonClient</code>.*/String path() default "";/*** @return whether to mark the feign proxy as a primary bean. Defaults to true.*/boolean primary() default true;
}

六、Feign Client 配置

  • FeignClient 配置源码
 /*** Feign client configuration.*/public static class FeignClientConfiguration {private Logger.Level loggerLevel;private Integer connectTimeout;private Integer readTimeout;private Class<Retryer> retryer;private Class<ErrorDecoder> errorDecoder;private List<Class<RequestInterceptor>> requestInterceptors;private Boolean decode404;private Class<Decoder> decoder;private Class<Encoder> encoder;private Class<Contract> contract;private ExceptionPropagationPolicy exceptionPropagationPolicy;//省略setter 和 getter}

七、Spring boot 服务下使用示例

  • pom.xml 中引入依赖,部分特性需要额外的依赖扩展(诸如表单提交等)
    <dependencies>
    <!-- spring-cloud-starter-openfeign 支持负载均衡、重试、断路器等 -->
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>2.2.2.RELEASE</version>
    </dependency>
    <!-- Required to use PATCH. feign-okhttp not support PATCH Method -->
    <dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
    <version>11.0</version>
    </dependency>
    </dependencies>
  • 开启支持-使用 EnableFeignClients 注解
    @SpringBootApplication
    @EnableFeignClients
    public class TyaleApplication {

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

    }

  • 接口注解-标记请求地址、请求header、请求方式、参数(是否必填)等
    //如果是微服务内部调用则 value 可以直接指定对方服务在服务发现中的服务名,不需要 url
    @FeignClient(value = "tyale", url = "${base.uri}")
    public interface TyaleFeignClient {

    @PostMapping(value = "/token", consumes ="application/x-www-form-urlencoded")
    Map<String, Object> obtainToken(Map<String, ?> queryParam);

    @GetMapping(value = Constants.STATION_URI)
    StationPage stations(@RequestHeader("Accept-Language") String acceptLanguage,
    @RequestParam(name = "country") String country,
    @RequestParam(name = "order") String order,
    @RequestParam(name = "page", required = false) Integer page,
    @RequestParam(name = "pageSize") Integer pageSize);

    @PostMapping(value = Constants.PAYMENT_URI)
    PaymentDTO payment(@RequestHeader("Accept-Language") String acceptLanguage,
    @RequestBody PaymentRQ paymentRq);
    }

  • FormEncoder 支持
    @Configuration
    public class FeignFormConfiguration {

    @Autowired
    private ObjectFactory<HttpMessageConverters> messageConverters;

    @Bean
    @Primary
    public Encoder feignFormEncoder() {
    return new FormEncoder(new SpringEncoder(this.messageConverters));
    }
    }

  • 拦截器-自动添加header 或者 token 等
    @Configuration
    public class FeignInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
    requestTemplate.header(Constants.TOKEN_STR, "Bearer xxx");
    }
    }

  • ErrorCode-可以自定义错误响应码的处理
    @Configuration
    public class TyaleErrorDecoder implements ErrorDecoder {

    @Override
    public Exception decode(String methodKey, Response response) {
    TyaleErrorException errorException = null;
    try {
    if (response.body() != null) {
    Charset utf8 = StandardCharsets.UTF_8;
    var body = Util.toString(response.body().asReader(utf8));
    errorException = GsonUtils.fromJson(body, TyaleErrorException.class);
    } else {
    errorException = new TyaleErrorException();
    }
    } catch (IOException ignored) {

    }
    return errorException;
    }
    }

  • TyaleErrorException 类示例-处理返回失败响应码时的数据,不同的服务端可能需要不同的处理
    @EqualsAndHashCode(callSuper = true)
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class TyaleErrorException extends Exception {

    /**
    * example: "./api/{service-name}/{problem-id}"
    */
    private String type;

    /**
    * example: {title}
    */
    private String title;

    /**
    * example: https://api/docs/index.html#error-handling
    */
    private String documentation;

    /**
    * example: {code}
    */
    private String status;
    }

  • FeignClient 使用示例
    @RestController
    @RequestMapping(value = "/rest/tyale")
    public class TyaleController {

    @Autowired
    private TyaleFeignClient feignClient;

    @GetMapping(value="/stations")
    public BaseResponseDTO<StationPage> stations() {
    try {
    String acceptLanguage = "en";
    String country = "DE";
    String order = "NAME";
    Integer page = 0;
    Integer pageSize = 20;
    StationPage stationPage = feignClient.stations(acceptLanguage,
    country, order, page, pageSize);
    return ResponseBuilder.buildSuccessRS(stationPage);
    } catch (TyaleErrorException tyaleError) {
    System.out.println(tyaleError);
    //todo 处理异常返回时的响应
    }
    return ResponseBuilder.buildSuccessRS();
    }
    }

Feign Client 原理和使用相关推荐

  1. No fallback instance of type class found for feign client user-service(转)

    No fallback instance of type class found for feign client user-service(转) 1.错误日志 在 feign 开启熔断,配置 fal ...

  2. SpringCloud Feign工作原理基本理解

    Feign介绍 Feign是Netflix公司开源的轻量级rest客户端,使用Feign可以非常方便的实现Http 客户端.Spring Cloud引入Feign并且集成了Ribbon实现客户端负载均 ...

  3. Feign底层原理分析-自动装载动态代理

    本篇文章仅介绍Feign的核心机制,包括如何交由Spring容器托管.动态代理机制等内容,不会过分深入细节. 1.什么是Feign? 这里套用Feign官方Github上的介绍:"Feign ...

  4. SpringCloud 中 Feign 核心原理,简单易懂!

    目录 SpringCloud 中 Feign 核心原理 Feign远程调用的基本流程 Feign 远程调用的重要组件 Feigh 远程调用的执行流程 SpringCloud 中 Feign 核心原理 ...

  5. Feign Client的各种超时时间设置

    在Spring Cloud微服务架构中,大部分公司都是利用Open Feign进行服务间的调用,而比较简单的业务使用默认配置是不会有多大问题的,但是如果是业务比较复杂,服务要进行比较繁杂的业务计算,那 ...

  6. Spring Cloud Feign设计原理

    点击关注,快速进阶高级架构师 作者:亦山札记 什么是Feign? Feign 的英文表意为"假装,伪装,变形", 是一个http请求调用的轻量级框架,可以以Java接口注解的方式调 ...

  7. 谷粒商城基础篇爬坑笔记--No Feign Client for loadBalancing defined以及加载测试失败

    在远程调用章节写完代码运行member模块开启失败,报错信息为: No Feign Client for loadBalancing defined. Did you forget to includ ...

  8. No Feign Client for loadBalancing defined.错误

    SpringCloud OpenFeign报错 No Feign Client for loadBalancing defined. Did you forget to include spring- ...

  9. spring cloud整合feign和nacos报错:No Feign Client for loadBalancing defined. Did you forget to include

    Did you forget to include spring-cloud-starter-loadbalancer 问题描述 项目环境 解决方案 1.引入eureka依赖--无效 2.降低spri ...

最新文章

  1. Elasticsearch源码分析—线程池(十一) ——就是从队列里处理请求
  2. 数学之美 十四 谈谈数学模型的重要性
  3. 2015 2020 r4烧录卡 区别_谁跑赢了沪深300?聪明指数全收益排名 2020-04-10
  4. js 获取json数组里面数组的长度
  5. leetcode面试题 04.03. 特定深度节点链表(bfs)
  6. 玩玩机器学习3——TensorFlow基础之Session基本运算、占位符和变量的使用
  7. java 网络实验_java网络聊天室实验
  8. 服务器安装 accessdatabaseengine_.net IIS 服务器环境配置
  9. webclient对reactor-netty的封装
  10. ELK下es索引管理工具-curator
  11. 解决Request method 'GET' not supported问题
  12. java新建json 数组_Java创建JSON对象
  13. 最新金色版萝卜影视源码/原生视频影视系统APP源码
  14. java网上购物系统_Java Web 应用教程——网上购物系统的实现
  15. uni-app APP横屏和竖屏
  16. pano2vr无法输出html5,教大家Pano2VR怎么输出全景图的方法
  17. CTF MISC(杂项)知识点总结——图片类(一)
  18. python 彩票图表下载_python彩票
  19. pdf加密文件怎么解密?
  20. 三种交换技术及其比较

热门文章

  1. php自动加载类与路由,PHP实现路由和类自动加载
  2. 蓝桥杯 基础练习 高精度加法
  3. 蓝桥杯 基础练习 芯片测试
  4. 364 页 PyTorch 版《动手学深度学习》分享(全中文,支持 Jupyter 运行)
  5. 花书+吴恩达深度学习(十一)卷积神经网络 CNN 之池化层
  6. Linux无root权限安装cuda9.1和cudnn7.05以及编译框架时无lcuda.so的问题
  7. Vue.use()与Vue.prototype
  8. java---解析XML文件,通过反射动态将XML内容封装到一个类中
  9. java 内部类 单例_确保对象的唯一性——单例模式 (四):一种更好的单例实现方法(静态内部类)...
  10. Spring Boot + JPA + Oracle 自增长字段实现示例