十、软件设计架构-微服务-服务调用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 {

	@Autowired
	private OrderService orderService;
	
	@Autowired
	private 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 {
    @Bean
    Logger.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 {
    @Override
    public void apply(RequestTemplate temp) {
        temp.header(HttpHeaders.AUTHORIZATION, "XXXXX");
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Dolphinscheduler DAG核心源码剖析

背景描述 注意 : 在 Dolphinscheduler 中&#xff0c;离线任务是有完整的声明周期的&#xff0c;比如说停止、暂停、暂停恢复、重跑等等&#xff0c;都是以DAG(有向无环图的形式进行任务组织)T1离线任务的。 Dolphinscheduler DAG实现 org.apache.dolphinscheduler.common.gr…

渤海证券基于互联网环境的漏洞主动防护方案探索与实践

来源&#xff1a;中国金融电脑 作者&#xff1a;渤海证券股份有限公司信息技术总部 刘洋 伴随互联网业务的蓬勃发展&#xff0c;证券行业成为黑客进行网络攻击的重要目标之一&#xff0c;网络攻击的形式也变得愈发多样且复杂。网络攻击如同悬于行业之上的达摩克利斯之剑&…

AI与低代码技术融合:如何加速企业智能化应用开发?

引言 随着全球数字化转型的步伐加快&#xff0c;企业在智能化应用开发方面面临着前所未有的挑战和机遇。传统的软件开发方式往往需要大量的技术人员、时间和资源&#xff0c;而在瞬息万变的市场环境中&#xff0c;这种模式显得效率低下且难以满足企业快速迭代和创新的需求。 与…

基于 FFmpeg/Scrcpy 框架构建的一款高性能的安卓设备投屏管理工具-供大家学习研究参考

支持的投屏方式有:USB,WIFIADB,OTG,投屏之前需要开启开发者选项里面的USB调试。 主要功能有: 1.支持单个或多个设备投屏。 2.支持键鼠操控。 3.支持文字输入。 4.支持共享剪切板(可复制粘贴电脑端文字到手机端,也可导出手机剪切板到电脑端)。 5.支持视频图片上传,可单…

springboot整合mybatis-plus【详细版】

目录 一&#xff0c;简介 1. 什么是mybatis-plus2.mybatis-plus特点 二&#xff0c;搭建基本环境 1. 导入基本依赖&#xff1a;2. 编写配置文件3. 创建实体类4. 编写controller层5. 编写service接口6. 编写service层7. 编写mapper层 三&#xff0c;基本知识介绍 1. 基本注解 T…

MAUI APP开发蓝牙协议的经验分享:与跳绳设备对接

在开发MAUI应用程序时&#xff0c;蓝牙协议的应用是一个重要的环节&#xff0c;尤其是在需要与外部设备如智能跳绳进行数据交换的场景中。以下是我在开发过程中的一些经验和心得&#xff0c;希望能为你的项目提供帮助。 1. 蓝牙协议基础 蓝牙协议是无线通信的一种标准&#x…

快速构建NLP理论知识体系

NLP理论知识体系 一句话解释NLPNLP模型及原理简述1、Rag 一句话解释NLP 如果我们要实现机器翻译、情感分析、问答系统、文本摘要、聊天机器人、构造智能化的辅助文件填写模板&#xff0c;NLP可以通过现成的模型对输入的语音、文字、图片进行处理&#xff08;分词、标词性、去停…

试题转excel;pdf转excel;试卷转Excel,word试题转excel

一、问题描述 一名教师朋友&#xff0c;偶尔会需要整理一些高质量的题目到excel中 以往都是手动复制搬运&#xff0c;几百道题几乎需要一个下午的时间 关键这些事&#xff0c;枯燥无聊费眼睛&#xff0c;实在是看起来就很蠢的工作 就想着做一个工具&#xff0c;可以自动处理…

Android Gradle 相关

JDK环境配置&#xff1a; 1、Gradle运行时的JDK&#xff0c;即Gradle需要用到的JDK&#xff0c;配置如下&#xff1a; 如需修改现有项目的 Gradle JDK 配置&#xff0c;请依次点击 File&#xff08;或者 macOS 上的 Android Studio&#xff09;> Settings > Build, Exe…

LeetCode - #151 颠倒字符串中的单词

文章目录 前言1. 描述2. 示例3. 答案关于我们 前言 我们社区陆续会将顾毅&#xff08;Netflix 增长黑客&#xff0c;《iOS 面试之道》作者&#xff0c;ACE 职业健身教练。&#xff09;的 Swift 算法题题解整理为文字版以方便大家学习与阅读。 LeetCode 算法到目前我们已经更新…

图数据库 | 11、图数据库架构设计——高性能图存储架构(下)

在上篇内容中&#xff0c;老夫着重讲了高性能图存储系统的特点&#xff0c;咱们继续往下讲重点——高性能存储架构的设计思路&#xff01;&#xff01; 2.高性能存储架构设计思路 首先呢&#xff0c;存储架构以及核心数据结构的设计思路通常围绕如下4个维度来进行&#xff1a…

如何配置Github并在本地提交代码

前提: 可以流畅访问github, 需要一些上网技巧, 这就自行处理了 申请一个github账号 Github官网地址 首先就是邮箱注册啦, github没有对邮箱的限制, 只要是能收邮件的就ok, qq邮箱, 163等都可以使用. 然后和普通注册账号一样, 一路填写需要的信息, 验证邮箱即可. 如何新增代…

小型支付商城系统-MVC工程架构开发

第1-1节 DDD 架构概念 1.DDD 是什么 那 DDD 是什么呢&#xff1f;来自于维基百科的一段定义&#xff1a;"Domain-driven design (DDD) is a major software design approach. "&#xff0c;DDD 是一种软件设计方法。也就是说 DDD 是指导我们做软件工程设计的一种手…

【计算机网络】实验11:边界网关协议BGP

实验11 边界网关协议BGP 一、实验目的 本次实验旨在验证边界网关协议&#xff08;BGP&#xff09;的实际作用&#xff0c;并深入学习在路由器上配置和使用BGP协议的方法。通过实验&#xff0c;我将探索BGP在不同自治系统之间的路由选择和信息交换的功能&#xff0c;理解其在互…

ai即可一键生成ppt解决烦恼

在快节奏的职场环境中&#xff0c;制作PPT已经成为许多人日常工作的一部分。尽管PPT看似简单&#xff0c;却常常让人耗费大量时间。好在技术的进步为我们带来了全新的解决方案&#xff0c;比如智能生成PPT&#xff0c;让那些深夜加班的人看到了曙光。 从“手动排版”到“一键生…

利用docker-compose来搭建flink集群

1.前期准备 &#xff08;1&#xff09;把docker&#xff0c;docker-compose&#xff0c;kafka集群安装配置好 参考文章&#xff1a; 利用docker搭建kafka集群并且进行相应的实践-CSDN博客 这篇文章里面有另外两篇文章的链接&#xff0c;点进去就能够看到 &#xff08;2&…

网络命令配置

随笔记录 目录 1. 背景介绍 2. 配置网络命令空间 3 验证 3.1 未网络命令空间外网卡配置IP 3.2 验证配置 3.2.1 在网络命令空间外接口启动iperf3 3.2.2 网络命令空间内启动iperf3 client 1. 背景介绍 2. 配置网络命令空间 1. 配置前[rootlocalhost SDK-V1.10.1.7]# ip…

沃丰科技智能客服在电商独立站中有哪些核心功能?

在数字化飞速发展的今天&#xff0c;电商独立站作为企业与消费者沟通的重要桥梁&#xff0c;其客户服务的质量和效率直接关系到企业的竞争力和市场地位。沃丰科技智能客服以其全面的功能&#xff0c;成为了电商独立站接入的首选&#xff0c;为电商企业提供了全新的解决方案。 …

【深度学习】四大图像分类网络之VGGNet

2014年&#xff0c;牛津大学计算机视觉组&#xff08;Visual Geometry Group&#xff09;和Google DeepMind公司一起研发了新的卷积神经网络&#xff0c;并命名为VGGNet。VGGNet是比AlexNet更深的深度卷积神经网络&#xff0c;该模型获得了2014年ILSVRC竞赛的第二名&#xff0c…

C/C++当中的内存对齐

一&#xff1a;为什么要存在内存对齐 对与计算机而言&#xff0c;一次性可以取出处理的单元大小为字&#xff0c;在32位系统下&#xff0c;一次性可以取出4个字节&#xff0c;而在64位系统下&#xff0c;一次性可以取出8个字节&#xff0c;而一个地址对应一个内存单元&#xff…