Feign Client 原理和使用
一、原理
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 原理和使用相关推荐
- 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 ...
- SpringCloud Feign工作原理基本理解
Feign介绍 Feign是Netflix公司开源的轻量级rest客户端,使用Feign可以非常方便的实现Http 客户端.Spring Cloud引入Feign并且集成了Ribbon实现客户端负载均 ...
- Feign底层原理分析-自动装载动态代理
本篇文章仅介绍Feign的核心机制,包括如何交由Spring容器托管.动态代理机制等内容,不会过分深入细节. 1.什么是Feign? 这里套用Feign官方Github上的介绍:"Feign ...
- SpringCloud 中 Feign 核心原理,简单易懂!
目录 SpringCloud 中 Feign 核心原理 Feign远程调用的基本流程 Feign 远程调用的重要组件 Feigh 远程调用的执行流程 SpringCloud 中 Feign 核心原理 ...
- Feign Client的各种超时时间设置
在Spring Cloud微服务架构中,大部分公司都是利用Open Feign进行服务间的调用,而比较简单的业务使用默认配置是不会有多大问题的,但是如果是业务比较复杂,服务要进行比较繁杂的业务计算,那 ...
- Spring Cloud Feign设计原理
点击关注,快速进阶高级架构师 作者:亦山札记 什么是Feign? Feign 的英文表意为"假装,伪装,变形", 是一个http请求调用的轻量级框架,可以以Java接口注解的方式调 ...
- 谷粒商城基础篇爬坑笔记--No Feign Client for loadBalancing defined以及加载测试失败
在远程调用章节写完代码运行member模块开启失败,报错信息为: No Feign Client for loadBalancing defined. Did you forget to includ ...
- No Feign Client for loadBalancing defined.错误
SpringCloud OpenFeign报错 No Feign Client for loadBalancing defined. Did you forget to include spring- ...
- 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 ...
最新文章
- Elasticsearch源码分析—线程池(十一) ——就是从队列里处理请求
- 数学之美 十四 谈谈数学模型的重要性
- 2015 2020 r4烧录卡 区别_谁跑赢了沪深300?聪明指数全收益排名 2020-04-10
- js 获取json数组里面数组的长度
- leetcode面试题 04.03. 特定深度节点链表(bfs)
- 玩玩机器学习3——TensorFlow基础之Session基本运算、占位符和变量的使用
- java 网络实验_java网络聊天室实验
- 服务器安装 accessdatabaseengine_.net IIS 服务器环境配置
- webclient对reactor-netty的封装
- ELK下es索引管理工具-curator
- 解决Request method 'GET' not supported问题
- java新建json 数组_Java创建JSON对象
- 最新金色版萝卜影视源码/原生视频影视系统APP源码
- java网上购物系统_Java Web 应用教程——网上购物系统的实现
- uni-app APP横屏和竖屏
- pano2vr无法输出html5,教大家Pano2VR怎么输出全景图的方法
- CTF MISC(杂项)知识点总结——图片类(一)
- python 彩票图表下载_python彩票
- pdf加密文件怎么解密?
- 三种交换技术及其比较
热门文章
- php自动加载类与路由,PHP实现路由和类自动加载
- 蓝桥杯 基础练习 高精度加法
- 蓝桥杯 基础练习 芯片测试
- 364 页 PyTorch 版《动手学深度学习》分享(全中文,支持 Jupyter 运行)
- 花书+吴恩达深度学习(十一)卷积神经网络 CNN 之池化层
- Linux无root权限安装cuda9.1和cudnn7.05以及编译框架时无lcuda.so的问题
- Vue.use()与Vue.prototype
- java---解析XML文件,通过反射动态将XML内容封装到一个类中
- java 内部类 单例_确保对象的唯一性——单例模式 (四):一种更好的单例实现方法(静态内部类)...
- Spring Boot + JPA + Oracle 自增长字段实现示例