SpringCloud——Feign实例及原理
一、实例
1、配置feign
添加依赖
在maven的pom中添加feign
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-feign</artifactId></dependency>
配置启用
在Application启动类中添加@EnableFeignClients注解。
@EnableEurekaClient
@EnableFeignClients
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}}
2、新建feign
@FeignClient(name = "SERVICE-NAME", url = "${***}")
public interface TestFeign {
}
其中:
name:微服务的名称,一定要以eureka后台配置的保持一致。
url:可以手动指定feign的调用地址
fallback:标记容错后执行的类
在feign中定义接口的方式与正常接口并无差异,需注意参数名称等保持一致。如:
@RequestMapping(value = "/user/message", method = RequestMethod.POST)JSONObject sendMessage(@RequestParam("userId") String userId, @RequestParam("content") String content);
3、调用feign
在ServiceImpl中注入feign接口,正常使用即可。
@AutowiredTestFeign testFeign;
二、原理
如果不使用springcloud的相关组件,调用服务需要要走http,配置请求head、body,然后才能发起请求。获得响应体后,还需解析等操作,十分繁琐。
那为什么feign的使用如此简单轻量呢?
1、如何启用
启动配置上检查是否有@EnableFeignClients注解,如果有该注解,则开启包扫描,扫描被@FeignClient注解接口。扫描出该注解后,通过beanDefinition注入到IOC容器中,方便后续被调用使用。
在FeignClientsRegistrar中,registerFeignClients()完成了注册feign的操作。
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {ClassPathScanningCandidateComponentProvider scanner = this.getScanner();scanner.setResourceLoader(this.resourceLoader);Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());......//遍历该项目所需调用的服务
Iterator var17 = ((Set)basePackages).iterator();while(var17.hasNext()) {String basePackage = (String)var17.next();Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);Iterator var21 = candidateComponents.iterator();while(var21.hasNext()) {BeanDefinition candidateComponent = (BeanDefinition)var21.next();if (candidateComponent instanceof AnnotatedBeanDefinition) {AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition)candidateComponent;//获取feign中的详细信息AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(FeignClient.class.getCanonicalName());String name = this.getClientName(attributes);//注册配置信息this.registerClientConfiguration(registry, name, attributes.get("configuration"));//注册feign客户端this.registerFeignClient(registry, annotationMetadata, attributes);}}}}
注册feign客户端,包括使用注解时配置的所有信息。
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {String className = annotationMetadata.getClassName();BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);this.validate(attributes);definition.addPropertyValue("url", this.getUrl(attributes));definition.addPropertyValue("path", this.getPath(attributes));String name = this.getName(attributes);definition.addPropertyValue("name", name);definition.addPropertyValue("type", className);definition.addPropertyValue("decode404", attributes.get("decode404"));definition.addPropertyValue("fallback", attributes.get("fallback"));definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));definition.setAutowireMode(2);String alias = name + "FeignClient";AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();boolean primary = ((Boolean)attributes.get("primary")).booleanValue();beanDefinition.setPrimary(primary);String qualifier = this.getQualifier(attributes);if (StringUtils.hasText(qualifier)) {alias = qualifier;}BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[]{alias});BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);}
2、如何发起请求
ReflectiveFeign内部使用了jdk的动态代理为目标接口生成了一个动态代理类,这里会生成一个InvocationHandler(jdk动态代理原理)统一的方法拦截器,同时为接口的每个方法生成一个SynchronousMethodHandler拦截器,并解析方法上的 元数据,生成一个http请求模板。
在SynchronousMethodHandler类中生成RequestTemplate发起请求。
public Object invoke(Object[] argv) throws Throwable {RequestTemplate template = buildTemplateFromArgs.create(argv);Retryer retryer = this.retryer.clone();while (true) {try {return executeAndDecode(template);} catch (RetryableException e) {retryer.continueOrPropagate(e);if (logLevel != Logger.Level.NONE) {logger.logRetry(metadata.configKey(), logLevel);}continue;}}}
Object executeAndDecode(RequestTemplate template) throws Throwable {Request request = targetRequest(template);Response response;long start = System.nanoTime();try {response = client.execute(request, options);// ensure the request is set. TODO: remove in Feign 10response.toBuilder().request(request).build();} catch (IOException e) {if (logLevel != Logger.Level.NONE) {logger.logIOException(metadata.configKey(), logLevel, e, elapsedTime(start));}throw errorExecuting(request, e);}}
发送http请求
@Overridepublic Response execute(Request request, Options options) throws IOException {HttpURLConnection connection = convertAndSend(request, options);return convertResponse(connection).toBuilder().request(request).build();}HttpURLConnection convertAndSend(Request request, Options options) throws IOException {final HttpURLConnectionconnection =(HttpURLConnection) new URL(request.url()).openConnection();if (connection instanceof HttpsURLConnection) {HttpsURLConnection sslCon = (HttpsURLConnection) connection;if (sslContextFactory != null) {sslCon.setSSLSocketFactory(sslContextFactory);}if (hostnameVerifier != null) {sslCon.setHostnameVerifier(hostnameVerifier);}}connection.setConnectTimeout(options.connectTimeoutMillis());connection.setReadTimeout(options.readTimeoutMillis());connection.setAllowUserInteraction(false);connection.setInstanceFollowRedirects(true);connection.setRequestMethod(request.method());Collection<String> contentEncodingValues = request.headers().get(CONTENT_ENCODING);booleangzipEncodedRequest =contentEncodingValues != null && contentEncodingValues.contains(ENCODING_GZIP);booleandeflateEncodedRequest =contentEncodingValues != null && contentEncodingValues.contains(ENCODING_DEFLATE);boolean hasAcceptHeader = false;Integer contentLength = null;for (String field : request.headers().keySet()) {if (field.equalsIgnoreCase("Accept")) {hasAcceptHeader = true;}for (String value : request.headers().get(field)) {if (field.equals(CONTENT_LENGTH)) {if (!gzipEncodedRequest && !deflateEncodedRequest) {contentLength = Integer.valueOf(value);connection.addRequestProperty(field, value);}} else {connection.addRequestProperty(field, value);}}}// Some servers choke on the default accept string.if (!hasAcceptHeader) {connection.addRequestProperty("Accept", "*/*");}if (request.body() != null) {if (contentLength != null) {connection.setFixedLengthStreamingMode(contentLength);} else {connection.setChunkedStreamingMode(8196);}connection.setDoOutput(true);OutputStream out = connection.getOutputStream();if (gzipEncodedRequest) {out = new GZIPOutputStream(out);} else if (deflateEncodedRequest) {out = new DeflaterOutputStream(out);}try {out.write(request.body());} finally {try {out.close();} catch (IOException suppressed) { // NOPMD}}}return connection;}
三、小结
1、约定大于配置
2、新瓶装旧酒
SpringCloud——Feign实例及原理相关推荐
- springCloud Feign的实现原理
一.Feign介绍 Feign是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求. Spring Cloud Feign是基于Netflix feign实现,整合了Spr ...
- SpringCloud Feign声明式服务调用
SpringCloud Feign声明式服务调用 1. 加入pom依赖 2. Application.java上声明@EnableFeignClients 3. @FeignClient声明接口调用服 ...
- Feign的调用原理及其源码分析
Feign的调用原理及其源码分析 目录 概述 架构特性 设计思路 实现思路分析 Feign是如何进行服务调用的 拓展实现 相关工具如下: 实验效果:(解决思路) 分析: 小结: 参考资料和推荐阅读 L ...
- feign扫描_feign原理+源码解析
1 架构图. 2 feign的初始化扫包原理. (1)feign使用,是main上的@EnableFeignClients 和 api的@FeignClient(value = "servi ...
- SpringCloud feign 的三种超时时间配置
1.负载均衡 Feign调用服务的默认时长是1秒钟,也就是如果超过1秒没连接上或者超过1秒没响应,就会相应的报错.Feign 的负载均衡底层用的是 Ribbon,其配置如下: ribbon:ReadT ...
- [深度学习] Attention机制,一文搞懂从实例到原理
一 人类的视觉注意力 扩展阅读: Attention and Augmented Recurrent Neural Networks[译文] 不用看数学公式!图解谷歌神经机器翻译核心部分:注意力机制 ...
- EntityFramework Core上下文实例池原理
[导读]无论是在我个人博客还是著作中,对于上下文实例池都只是通过大量文字描述来讲解其基本原理,而且也是浅尝辄止,导致我们对其认识仍是一知半解,本文我们摆源码,从源头开始分析 希望通过本文从源码的分析, ...
- springcloud Feign断路器实战和问题总结
springcloud Feign断路器实战和问题总结 断路由是防止该服务调用其他外服务时,外服务宕机或者出差时,影响到本服务的宕机,引起大面积的瘫痪,所以才有了断路由的由来. springcloud ...
- SpringCloud Feign 传参问题及传输Date类型参数的时差
1.多参数表单类型传输 @PostMapping("/service/system/advertiser/save")Response<Boolean> saveAdv ...
最新文章
- android adb命令,向开发手机添加文件
- 4、 LIMIT:限制查询结果的条数
- svn教程----示例二:测试人员拥有读权限
- UVA11212Editing aBook 编辑书稿
- SHD0新建屏幕变式
- 坚实原则:依赖倒置原则
- 推销自己的海盗猫王运营商
- JConsole连接远程linux服务器配置
- 交通灯程序设计C语言,基于MCS-51的交通灯程序设计(c语言控制直行左转,包含程序)...
- 29 WM配置-策略-出库策略2-定义“紧急FIFO”策略(Stringent FIFO)
- SQL Server学习笔记
- 成为Linux内核高手的四个方法
- 传说中的WCF(4):发送和接收SOAP头
- php 嵌套函数公式解析,Pyparsing,使用嵌套解析器解析php函数注释块的内容
- 英文单词和数字断行不折叠
- python绘制多边形的程序_Python – 绘制多边形
- 2020年阴历二月二十九 投资理财之一入雪球就被坑
- 对于大数据的一些处理方法
- C语言初阶-C语言中static的用法
- openwrt的UCI 网络配置与Luci在线安装