一、实例

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实例及原理相关推荐

  1. springCloud Feign的实现原理

    一.Feign介绍 Feign是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求. Spring Cloud Feign是基于Netflix feign实现,整合了Spr ...

  2. SpringCloud Feign声明式服务调用

    SpringCloud Feign声明式服务调用 1. 加入pom依赖 2. Application.java上声明@EnableFeignClients 3. @FeignClient声明接口调用服 ...

  3. Feign的调用原理及其源码分析

    Feign的调用原理及其源码分析 目录 概述 架构特性 设计思路 实现思路分析 Feign是如何进行服务调用的 拓展实现 相关工具如下: 实验效果:(解决思路) 分析: 小结: 参考资料和推荐阅读 L ...

  4. feign扫描_feign原理+源码解析

    1 架构图. 2 feign的初始化扫包原理. (1)feign使用,是main上的@EnableFeignClients 和 api的@FeignClient(value = "servi ...

  5. SpringCloud feign 的三种超时时间配置

    1.负载均衡 Feign调用服务的默认时长是1秒钟,也就是如果超过1秒没连接上或者超过1秒没响应,就会相应的报错.Feign 的负载均衡底层用的是 Ribbon,其配置如下: ribbon:ReadT ...

  6. [深度学习] Attention机制,一文搞懂从实例到原理

    一 人类的视觉注意力 扩展阅读: Attention and Augmented Recurrent Neural Networks[译文] 不用看数学公式!图解谷歌神经机器翻译核心部分:注意力机制 ...

  7. EntityFramework Core上下文实例池原理

    [导读]无论是在我个人博客还是著作中,对于上下文实例池都只是通过大量文字描述来讲解其基本原理,而且也是浅尝辄止,导致我们对其认识仍是一知半解,本文我们摆源码,从源头开始分析 希望通过本文从源码的分析, ...

  8. springcloud Feign断路器实战和问题总结

    springcloud Feign断路器实战和问题总结 断路由是防止该服务调用其他外服务时,外服务宕机或者出差时,影响到本服务的宕机,引起大面积的瘫痪,所以才有了断路由的由来. springcloud ...

  9. SpringCloud Feign 传参问题及传输Date类型参数的时差

    1.多参数表单类型传输 @PostMapping("/service/system/advertiser/save")Response<Boolean> saveAdv ...

最新文章

  1. android adb命令,向开发手机添加文件
  2. 4、 LIMIT:限制查询结果的条数
  3. svn教程----示例二:测试人员拥有读权限
  4. UVA11212Editing aBook 编辑书稿
  5. SHD0新建屏幕变式
  6. 坚实原则:依赖倒置原则
  7. 推销自己的海盗猫王运营商
  8. JConsole连接远程linux服务器配置
  9. 交通灯程序设计C语言,基于MCS-51的交通灯程序设计(c语言控制直行左转,包含程序)...
  10. 29 WM配置-策略-出库策略2-定义“紧急FIFO”策略(Stringent FIFO)
  11. SQL Server学习笔记
  12. 成为Linux内核高手的四个方法
  13. 传说中的WCF(4):发送和接收SOAP头
  14. php 嵌套函数公式解析,Pyparsing,使用嵌套解析器解析php函数注释块的内容
  15. 英文单词和数字断行不折叠
  16. python绘制多边形的程序_Python – 绘制多边形
  17. 2020年阴历二月二十九 投资理财之一入雪球就被坑
  18. 对于大数据的一些处理方法
  19. C语言初阶-C语言中static的用法
  20. openwrt的UCI 网络配置与Luci在线安装

热门文章

  1. 计算机无法用u盘重装系统,电脑开不了机怎么办教你用u盘安装系统
  2. SWFupload上传插件案例及头像的截取
  3. 利用卫星数据绘制热脆弱性图—利用遥感监测城市热量
  4. 统一异常处理 GlobalExceptionHandler
  5. Android读取手机根目录文件即本地SD卡
  6. 苹果首款ARM Mac来了,浅谈ARM和Intel处理器!
  7. EventLoop 事件循环机制
  8. Pygame Zero(pgzrun)游戏库介绍
  9. 希尔排序、快速排序、KMP、链表的理解
  10. 深度学习面试题:怎么减少卷积层的参数数量?