十、软件设计架构-微服务-服务调用Feign

文章目录

  • 前言
  • 一、Feign介绍
    • 1. 什么是Feign
    • 2. 什么是Http客户端
    • 3. Feign 和 OpenFeign 的区别
  • 二、Feign底层原理
  • 三、Feign工作原理详解
    • 1. 动态代理机制
    • 2. 动态代理的创建过程
    • 3. 创建详细流程
    • 4. @FeignClient属性
  • 四、Feign使用
    • 1. 常规调用
    • 2.日志打印
    • 3. 添加Header


前言

服务调用方案--Feign

  声明式的Web服务客户端


一、Feign介绍

1. 什么是Feign

  Feign是声明式的Web服务客户端,让编写Web服务客户端变得非常容易,只需创建一个接口并在接口上添加注解即可。

  Feign 不做任何请求处理,通过处理注解相关信息生成 Request,并对调用返回的数据进行解码,从而实现 简化 HTTP API 的开发

Feign基本流程
如果要使用 Feign,需要创建一个接口并对其添加 Feign 相关注解,另外 Feign 还支持可插拔编码器和解码器,致力于打造一个轻量级 HTTP 客户端。

2. 什么是Http客户端

  HTTP(超文本传输协议)是一种应用层协议,用于客户端和服务端进行通信,按照标准格式如JSON、XML等进行网络数据的传输,通常也作为应用程序之间以REST API形式进行通信的常用协议。

在Java应用中需要调用其他应用提供的HTTP服务API时,通常需要使用一些HTTP客户端组件,对HTTP协议进行封装,将网络传输的功能转化为方法,开发人员就可以直接调用。

主要介绍的HTTP客户端包括:

  • Spring Boot中的‌WebClient‌:Java标准库的一部分。基于Java SE平台提供的HttpURLConnection类构建的。用于取代较旧的RestTemplate,以便在使用Spring Boot框架构建的应用程序中进行REST API调用,它支持同步、异步和流式处理。

  • Java 11+版本中提供的‌HttpClient‌:Java标准库的一部分,取代了JDK更早期的HttpUrlConnection类。与WebClient相比,HttpClient具有更好的性能和更多的功能。它支持连接池、重试机制、代理设置等高级特性。此外,HttpClient还提供了对HTTP/2和WebSocket的支持。

  • Apache HttpComponents项目中的‌HttpClient‌:可用于HTTP协议的Java工具集,HTTP代理实现。

  • ‌OkHttpClient‌:开源的HTTP客户端库。与WebClient和HttpClient相比,OkHttp更加灵活,易于扩展和定制。

Spring Boot 项目中,底层涉及网络请求的组件有 RestTemplate、Feign 和 Zuul,它们分别有自己默认的 HTTP 请求客户端,很多时候为了获得更好的性能,我们需要替换底层默认的 HTTP 客户端。

Feign 默认使用的是 ‌JDK 原生的 HTTPURLConnection‌。可以使用 Apache HTTP Client 或者 Okhttp 来进行替换,替换的步骤分为两步,首先引入相关的依赖库,然后修改配置。

3. Feign 和 OpenFeign 的区别

  OpenFeign 组件的前身是 Netflix Feign 项目,它最早是作为 Netflix OSS 项目的一部分,由 Netflix 公司开发。后来 Feign 项目被贡献给了开源组织,于是才有了我们今天使用的 Spring Cloud OpenFeign 组件。

Spring Cloud 添加了对 Spring MVC 注解的支持,并支持使用 Spring Web 中默认使用的相同HttpMessageConverters。

另外,Spring Cloud 同时集成了 Ribbon 和 注册中心 (Eureka、Consul、Naocs等)以及 Spring Cloud LoadBalancer,以在使用 Feign 时提供负载均衡的 HTTP 客户端。

Spring Cloud官网Feign介绍

OpenFeign源码

二、Feign底层原理

‌核心点围绕在动态代理,如何发送及接收 HTTP 网络请求‌。

Feign底层调用链
Feign集成 Ribbon负载均衡

  1. 通过 @EnableFeignCleints 注解启动 Feign Starter 组件。

  2. Feign Starter 在项目启动过程中注册全局配置,扫描包下所有的 @FeignClient 接口类,并进行注册 IOC 容器。

  3. @FeignClient 接口类被注入时,通过 FactoryBean#getObject 返回动态代理类。创建动态代理类的方式和 Mybatis Mapper 处理方式是一致的,因为两者都没有实现类。

根据 newInstance 方法按照行为大致划分,共做了四件事:

  • 处理 @FeignCLient 注解(SpringMvc 注解等)封装为 MethodHandler 包装类。
  • 遍历接口中所有方法,过滤 Object 方法,并将默认方法以及 FeignClient 方法分类。
  • 创建动态代理对应的 InvocationHandler 并创建 Proxy 实例。
  • 接口内 default 方法 绑定动态代理类。
  1. 接口被调用时被动态代理类逻辑拦截,将 @FeignClient 请求信息通过编码器生成 HTTP Request。Feign 发送请求以及接收响应等都是由 Client 完成,该类默认 Client.Default,另外支持 HttpClient、OkHttp 等客户端。

  2. 交由 Ribbon 进行负载均衡,挑选出一个健康的 Server 实例。通过 Ribbon 获取服务列表,并对服务列表进行负载均衡调用(服务名转换为 ip+port)。

  3. 继而通过 Client 携带 Request 调用远端服务返回请求响应。

  4. 通过解码器生成 HTTP Response 返回客户端,将信息流解析成为接口返回数据。

三、Feign工作原理详解

  OpenFeign 使用了动态代理技术来封装远程服务调用的过程,远程服务调用的信息被写在了被 @FeignClient 修饰的接口中。服务的名称、接口类型、访问路径已经通过注解做了声明。OpenFeign 通过解析这些注解标签生成一个“动态代理类”,这个代理类会将接口调用转化为一个远程服务调用的 Request,并发送给目标服务

1. 动态代理机制

Feign动态代理
上图中的步骤 1 到步骤 3 是在项目启动阶段加载完成的,只有第 4 步“调用远程服务”是发生在项目的运行阶段。

  1. 在项目启动阶段,OpenFeign 框架会发起一个主动的扫包流程,从指定的目录下扫描并加载所有被 @FeignClient 注解修饰的接口。

  2. OpenFeign 会针对每一个 FeignClient 接口生成一个动态代理对象,即图中的FeignProxyService,这个代理对象在继承关系上属于 FeignClient 注解所修饰的接口的实例。

  3. 这个动态代理对象会被添加到 Spring 上下文中,并注入到对应的服务里,也就是图中的 LocalService 服务。

  4. LocalService 会发起底层方法调用。实际上这个方法调用会被 OpenFeign 生成的代理对象接管,由代理对象发起一个远程服务调用,并将调用的结果返回给LocalService。

总之,就是通过 Java 动态代理生成了一个“代理类”,这个代理类将接口调用转化成为了一个远程服务调用。

2. 动态代理的创建过程

如何通过动态代理技术创建代理对象的?

@EnableFeignClients,将修饰了 @FeignClient 的接口注册为 IOC Bean。

Feign注册IOC Bean

  1. 项目加载:在项目的启动阶段,EnableFeignClients 注解扮演了“启动开关”的角色,它使用 Spring 框架的 Import 注解导入了 FeignClientsRegistrar 类,开始了OpenFeign 组件的加载过程。

  2. 扫包FeignClientsRegistrar 负责 FeignClient 接口的加载,它会在指定的包路径下扫描所有的 FeignClients 类,并构造 FeignClientFactoryBean 对象来解析FeignClient 接口。

  3. 解析 FeignClient 注解FeignClientFactoryBean 有两个重要的功能,一个是解析FeignClient 接口中的请求路径和降级函数的配置信息;另一个是触发动态代理的构造过程。其中,动态代理构造是由更下一层的 ReflectiveFeign 完成的。

  4. 构建动态代理对象ReflectiveFeign 包含了 OpenFeign 动态代理的核心逻辑,它主要负责创建出 FeignClient 接口的动态代理对象

ReflectiveFeign 在这个过程中有两个重要任务:

  • 解析 FeignClient 接口上各个方法级别的注解,将其中的远程接口URL、接口类型(GET、POST 等)、各个请求参数等封装成元数据,并为每一个方法生成一个对应的 MethodHandler 类作为方法级别的代理;

  • 将这些MethodHandler 方法代理做进一步封装,通过 Java 标准的动态代理协议,构建一个实现了 InvocationHandler 接口的动态代理对象,并将这个动态代理对象绑定到FeignClient 接口上。这样一来,所有发生在 FeignClient 接口上的调用,最终都会由它背后的动态代理对象来承接。

其中,元数据的解析如何完成的呢?

依赖于 OpenFeign 组件中的Contract 协议解析功能。Contract 是 OpenFeign 组件中定义的顶层抽象接口,它有一系列的具体实现,其中和我们项目有关的是 SpringMvcContract 这个类。
SpringMvcContract 的继承结构是 SpringMvcContract->BaseContract->Contract。
详见OpenFeign 如何做到 “隔空取物”

3. 创建详细流程

Feign就是通过扫描添加了@FeignClient注解的接口,然后一步步生成代理对象,具体流程如下:

Feign请求流程后续在请求时,通过代理对象的FeignInvocationHandler进行拦截,并根据对应方法进行处理器的分发,完成后续的http请求操作。

4. @FeignClient属性

  • name
    定义当前客户端Client的名称。如果项目使用了Ribbon,name属性会作为微服务的名称,用于服务发现。

  • value
    等同于name属性。

  • url
    配置指定服务的地址。

  • path
    配置指定接口的请求路径。

  • configuration
    Feign配置类,可以自定义Feign的Encoder、Decoder、LogLevel、Contract等。

  • fallback
    定义容错的处理类,当调用远程接口失败或超时时,会调用对应接口的容错逻辑,fallback指定的类必须实现@FeignClient标记的接口。

  • fallbackFactory
    工厂类,用于生成fallback类实例,通过这个属性我们可以实现每个接口通用的容错逻辑,减少重复的代码。

  • decode404
    当发生http 404错误时,如果该字段为true,会调用decoder进行解码,否则抛出FeignException。

四、Feign使用

1. 常规调用

  1. 加入Fegin的依赖
<!--fegin组件-->
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  1. 在启动类需要添加@EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients//开启Fegin
public class OrderApplication {}
  1. 使用添加@FeignClient,定义远程服务的信息
@FeignClient("service-product")//声明调用的提供者的name服务名
public interface ProductService {//指定调用提供者的哪个方法//@FeignClient + @GetMapping 就是一个完整的请求路径 http://service-product/product/{pid}@GetMapping(value = "/product/{pid}")Product findByPid(@PathVariable("pid") Integer pid);
}
  1. 服务消费者-订单服务,修改Controller代码,并启动验证
@RestController
@Slf4j
public class OrderController {@Autowiredprivate OrderService orderService;@Autowiredprivate ProductService productService;//准备买1件商品@GetMapping("/order/prod/{pid}")public Order order(@PathVariable("pid") Integer pid) {log.info(">>客户下单,这时候要调用商品微服务查询商品信息");//通过fegin调用商品微服务Product product = productService.findByPid(pid);log.info(">>商品信息,查询结果:" + JSON.toJSONString(product));Order order = new Order();order.setUid(1);order.setUsername("测试用户");order.setPid(product.getPid());order.setPname(product.getPname());order.setPprice(product.getPprice());order.setNumber(1);orderService.save(order);return order;}
}
  1. 重启order微服务,查看效果

2.日志打印

Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节。
说白了就是对Feign接口的调用情况进行监控和输出。

配置日志Bean:

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class FeignConfig {@BeanLogger.Level feignLoggerLevel() {return Logger.Level.FULL;}
}

YML文件里需要开启日志的Feign客户端

logging:level:# feign日志以什么级别监控哪个接口com.gzl.cn.service.PaymentFeignService: debug

3. 添加Header

以下提供了四种方式:

1.在@RequestMapping中添加,如下:

@FeignClient(name="custorm",fallback=Hysitx.class)
public interface IRemoteCallService {@RequestMapping(value="/custorm/getTest",method = RequestMethod.POST,headers = {"Content-Type=application/json;charset=UTF-8"})List<String> test(@RequestParam("names") String[] names);
}

2:在方法参数前面添加@RequestHeader注解,如下:

@FeignClient(name="custorm",fallback=Hysitx.class)
public interface IRemoteCallService {@RequestMapping(value="/custorm/getTest",method = RequestMethod.POST,headers = {"Content-Type=application/json;charset=UTF-8"})List<String> test(@RequestParam("names")@RequestHeader("Authorization") String[] names);
}

设置多个属性时,可以使用Map,如下:

@FeignClient(name="custorm",fallback=Hysitx.class)
public interface IRemoteCallService {@RequestMapping(value="/custorm/getTest",method = RequestMethod.POST,headers = {"Content-Type=application/json;charset=UTF-8"})List<String> test(@RequestParam("names") String[] names, @RequestHeader MultiValueMap<String, String> headers);
}

3.使用@Header注解,如下:

@FeignClient(name="custorm",fallback=Hysitx.class)
public interface IRemoteCallService {@RequestMapping(value="/custorm/getTest",method = RequestMethod.POST)@Headers({"Content-Type: application/json;charset=UTF-8"})List<String> test(@RequestParam("names") String[] names);
}

4.实现RequestInterceptor接口(拦截器),如下:

@Configuration
public class FeignRequestInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate temp) {temp.header(HttpHeaders.AUTHORIZATION, "XXXXX");}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/888790.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在 LS-DYNA 中将应力转换为用户定义的坐标系

介绍 通常&#xff0c;使用 LS-DYNA 或 Ansys Mechanical 等仿真工具解决工程问题需要将张量结果与解析解进行比较。一个这样的例子是加压圆柱体&#xff0c;其中圆周应力或环状应力是感兴趣的主要应力度量。例如&#xff0c;如果对具有复杂端部处理的几何结构进行此类仿真&am…

树莓集团是如何链接政、产、企、校四个板块的?

树莓集团作为数字影像行业的积极探索者与推动者&#xff0c;我们通过多维度、深层次的战略举措&#xff0c;将政、产、企、校四个关键板块紧密链接在一起&#xff0c;实现了资源的高效整合与协同发展&#xff0c;共同为数字影像产业的繁荣贡献力量。 与政府的深度合作政府在产业…

工业异常检测-CVPR2024-新的3D异常数据合成办法和自监督网络IMRNet

论文&#xff1a;https://arxiv.org/pdf/2311.14897v3.pdf 项目&#xff1a;https://github.com/chopper-233/anomaly-shapenet 这篇论文主要关注的是3D异常检测和定位&#xff0c;这是一个在工业质量检查中至关重要的任务。作者们提出了一种新的方法来合成3D异常数据&#x…

debian ubuntu armbian部署asp.net core 项目 开机自启动

我本地的环境是 rk3399机器&#xff0c;安装armbian系统。 1.安装.net core 组件 sudo apt-get update && \sudo apt-get install -y dotnet-sdk-8.0或者安装运行库&#xff0c;但无法生成编译项目 sudo apt-get update && \sudo apt-get install -y aspnet…

qt QNetworkAccessManager详解

1、概述 QNetworkAccessManager是QtNetwork模块中的一个核心类&#xff0c;它允许应用程序发送网络请求并接收响应。该类是网络通信的基石&#xff0c;提供了一种方便的方式来处理常见的网络协议&#xff0c;如HTTP、HTTPS等。QNetworkAccessManager对象持有其发送的请求的通用…

21、结构体成员分布

结构体中的成员并不是紧挨着分布的&#xff0c;内存分布遵循字节对齐的原则。 按照成员定义的顺序&#xff0c;遵循字节对齐的原则存储。 字节对齐的原则&#xff1a; 找成员中占据字节数最大的成员&#xff0c;以它为单位进行空间空配 --- 遇到数组看元素的类型 每一个成员距离…

前端基础——CSS

美化文档 HTML内部添加样式 本节我们来学习如何在标签中引入CSS样式。 1 在标签中添加声明 声明的关键字是style后接等号&#xff08;&#xff09;再接引号&#xff08;""&#xff09;&#xff0c;即style"" 具体声明如下&#xff1a; <input typ…

WPS解决Word文件引入excel对象文件无法打开提示“不能启动此对象...”的问题

一、问题现象 接收到了一份 Word文件&#xff0c;里面引入了一个Excel对象文件&#xff0c;双击时候&#xff0c;wps出现卡顿&#xff0c;过一会之后弹出错误提示&#xff1a;不能启动此对象... 二、解决方法 1.点击WPS左上角图标&#xff0c;并打开右上角设置&#xff0c;萱蕚…

spring下的beanutils.copyProperties实现深拷贝

spring下的beanutils.copyProperties方法是深拷贝还是浅拷贝&#xff1f;可以实现深拷贝吗&#xff1f; 答案&#xff1a;浅拷贝。 一、浅拷贝深拷贝的理解 简单说拷贝就是将一个类中的属性拷贝到另一个中&#xff0c;对于BeanUtils.copyProperties来说&#xff0c;你必须保…

3GPP R18 LTM(L1/L2 Triggered Mobility)是什么鬼?(三) RACH-less LTM cell switch

这篇看下RACH-less LTM cell switch。 相比于RACH-based LTM,RACH-less LTM在进行LTM cell switch之前就要先知道target cell的TA信息,进而才能进行RACH-less过程,这里一般可以通过UE自行测量或者通过RA过程获取,而这里的RA一般是通过PDCCH order过程触发。根据38.300中的描…

Ansible 运维工具

安装 apt install ansible /etc/ansible/hosts , 指定密码或密钥访问分组机器 [k8s_masters] master0.c0.k8s.sb[k8s_nodes] node0.c0.k8s.sb node1.c0.k8s.sb[k8s:children] k8s_masters k8s_nodes[k8s_masters:vars] ansible_ssh_usersbadmin ansible_ssh_pass"***&q…

三菱JET伺服CC-Link IE现场网络Basic链接软元件(RYn/RXn)(RWwn/RWrn)

链接软元件(RYn/RXn) 要点 在循环通信中对主站发送给伺服放大器的请求(RYn及RWwn)设定了范围外的值时&#xff0c;将无法反映设定内容。 循环通信的请求报文与响应报文的收发数据被换读为伺服放大器的对象数据(RYn、RXn)。 响应报文的设定值可进行变更。变更初始设定值时&…

WPF中的VisualState(视觉状态)

以前在设置控件样式或自定义控件时&#xff0c;都是使用触发器来进行样式更改。触发器可以在属性值发生更改时启动操作。 像这样&#xff1a; <Style TargetType"ListBoxItem"><Setter Property"Opacity" Value"0.5" /><Setter …

ARINC 标准全解析:航空电子领域多系列标准的核心内容、应用与重要意义

ARINC标准概述 ARINC标准是航空电子领域一系列重要的标准规范&#xff0c;由航空电子工程委员会&#xff08;AEEC&#xff09;编制&#xff0c;众多航空公司等参与支持。这些标准涵盖了从飞机设备安装、数据传输到航空电子设备功能等众多方面&#xff0c;确保航空电子系统的兼…

用golang简单做一个内网穿透小工具!

一个个人需求&#xff0c;需要简单的穿透一下 家里的电脑&#xff0c;也不需要多稳定&#xff0c;也没有并发。然后 通过http可以访问到内网的 一个网站&#xff0c; 因为服务端 是linux 系统 &#xff0c;内网是 windows10服务器 所以 就采用 用golang这个语言来实现&#xff…

微服务监控prometheus+Grafana

目录 Prometheus 概述 核心组件 特点 使用场景 Grafana 概述 功能特点 使用场景 PrometheusGrafana组合 部署和配置 一、准备工作 二、部署Prometheus 三、部署Grafana 四、创建监控仪表盘 五、验证和调优 总结 微服务监控是确保微服务架构稳定运行的关键环节…

(vue)el-cascader多选级联选择器,值取最后一级的数据

(vue)el-cascader多选级联选择器&#xff0c;取值取最后一级的数据 获取到&#xff1a;[“养殖区”,“鸡棚”,“E5001”] 期望&#xff1a;[“E5001”] 问题: 解决方法 增加change事件方法&#xff0c;处理选中的value值 1.单选 <el-cascaderv-model"tags2":o…

Git常用的命令【提交与回退】

git分布式版本控制系统 &#xff08;SVN集中式版本控制系统&#xff09;之间的对比 git有本地仓库和远程仓库&#xff0c;不同的开发人员可以分别提交自己的本地仓库并维护代码的版本控制。 然后多个人员在本地仓库协作的代码&#xff0c;可以提交到远程仓库中做整合。 git本…

AUTOSAR AP和CP的安全要求规范(Safety Req)详细解读

一、规范的编制的背景原因 编制该规范的原因 确保系统安全性和可靠性 随着汽车电子系统日益复杂&#xff0c;功能不断增加&#xff0c;对安全性和可靠性的要求也越来越高。该规范为AUTOSAR平台在安全执行、配置、更新、信息交换、数据处理等多方面制定了明确要求&#xff0c;…