11 | 给 Gin 服务器添加中间件
提示:
- 所有体系课见专栏:Go 项目开发极速入门实战课;
- 欢迎加入 云原生 AI 实战 星球,12+ 高质量体系课、20+ 高质量实战项目助你在 AI 时代建立技术竞争力(聚焦于 Go、云原生、AI Infra);
- 本节课最终源码位于 fastgo 项目的 feature/s08 分支;
更详细的课程版本见:Go 项目开发中级实战课:23 | Web 服务核心功能:如何添加 Gin 中间件?
在开发 Web 服务器时,经常会对所有的请求进行通用的处理,例如:打印请求的输入、输出,多所有请求进行认证鉴权等。这时候,不可能在每一个 API 接口中都实现相同的逻辑,不优雅,也很难维护。
这种情况下,业界通用的做法是,通过 Web 中间件来实现。本节课,就来看下如何基于 Gin 框架实现一个 Web 中间件。
Web 中间件介绍
中间件(Middleware)是位于应用程序请求-响应处理循环中的一个特殊函数。它可以在请求到达业务逻辑处理之前修改/处理请求,或是在响应返回给客户端之前修改/处理响应。中间件根据使用方又可分为客户端中间件和服务端中间件,两者在实现原理和使用方式上是一致的。
中间件的核心作用是对请求或响应进行预处理、后处理或监控。它允许在请求和响应被发送或接收之前或之后插入自定义逻辑,从而实现多种功能,例如认证、授权、日志记录、性能监控、错误处理、请求验证、跨域支持、限流等。以下是核心使用场景的详细说明:
- 认证和授权: 使用中间件可以实现认证和授权逻辑。在中间件中,可以验证请求者的身份、权限等信息,并根据情况决定是否允许请求继续进行;
- 日志记录: 中间件可以用于记录请求和响应的详细信息,从而实现日志记录和监控。可以记录请求的内容、调用的方法、响应的结果等,以便于调试和分析;
- 错误处理: 在中间件中可以捕获和处理 gRPC 调用过程中可能发生的错误,以提供更友好的错误信息或进行恢复操作;
- 性能监视: 使用中间件可以监视 Web 调用的性能指标,如调用时间、响应时间等,从而实现性能监控和优化。
Web 中间件工作原理如下图所示。
上图中,有两个中间件:中间件 A 和中间件 B。一个 Web 请求从开始到结束时的执行流程为:中间件 A->中间件 B->处理器函数->中间件 B->中间件 A,其执行顺序类似于栈结构。
Web 中间件的作用实际上是实现对请求的前置拦截和对响应的后置拦截功能:
- 请求前置拦截: 在 Web 请求到达定义的处理器函数之前,对请求进行拦截并执行相应的处理;
- 请求后置拦截: 在完成请求的处理并响应客户端后,拦截响应并进行相应的处理。
需要注意的是,中间件会附加到每个请求的链路上,因此如果中间件性能较差或不稳定,将会影响所有 API 接口。因此,在开发中间件时,应确保其稳定性和性能,同时建议仅添加必要的中间件。
Gin 中间件介绍
Gin 框架也支持 Web 中间件,在 Gin 框架中,Web 中间件就叫中间件。本节课就来详细介绍如何实现并添加 Gin 中间件。
Gin 支持三种中间件使用方式:
- 全局中间件: 全局中间件会作用于所有的路由。它们通常用于处理通用功能,比如请求日志记录、跨域设置、错误恢复;
- 路由组中间件: 路由组中间件仅对指定的路由组生效,适用于将某些逻辑限定在同一组相关的路由中。例如,所有/api 路径下的路由可能都需要一套特定的身份验证中间件;
- 单个路由中间件: 单个路由中间件仅对一个路由起作用。有时某个路由需要执行独立的中间件逻辑,这种情况下,可以将中间件绑定到单个路由上。
不同路由组的中间件设置方式不同。下述代码展示了 Gin 中间件的开发和设置方法。
package mainimport ("log""net/http""github.com/gin-gonic/gin"
)// 定义一个通用中间件:打印请求路径
func LogMiddleware() gin.HandlerFunc {return func(c *gin.Context) {log.Printf("Request path: %s\n", c.Request.URL.Path)// 继续处理后续的中间件或路由c.Next()}
}func main() {r := gin.Default()// 使用全局中间件:所有路由都会经过该中间件// r.Use(gin.Logger(), gin.Recovery()) 同时设置多个 Gin 中间件r.Use(LogMiddleware())// 定义普通路由r.GET("/", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "Home"})})// 定义一个路由组,并为组添加中间件apiGroup := r.Group("/api", LogMiddleware()){apiGroup.GET("/hello", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "Hello, API"})})apiGroup.GET("/world", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "World, API"})})}// 为单个路由添加中间件r.GET("/secure", LogMiddleware(), func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "This is a secure route"})})// 启动HTTP服务r.Run(":8080") // 监听在8080端口
}
上述代码中,通过 r.Use()
、r.Group()
、r.Get()
方法分别设置了多个 Gin 中间件。在设置 Gin 中间件时,可以根据需要同时设置一个或者多个,例如 r.Use(gin.Logger(), gin.Recovery())
,同时设置了两个 Gin 中间件。
在 LogMiddleware
中间件中,c.Next()
方法之前的代码将在请求到达处理器函数之前执行,而 c.Next()
方法之后的代码将在请求经过处理器函数处理之后执行。另外,在开发 Gin 中间件时,c.Abort()
方法也经常被开发者使用,该方法会直接终止请求的执行。
fastgo 添加中间件
给 fastgo 添加 Gin 中间件包括以下 2 步:
- 开发 Gin 中间件;
- 加载 Gin 中间件。
开发 Gin 中间件
这里,我们先开发 3 个常用的 Gin 中间件:
- NoCache: 通过设置一些 Header,禁止客户端缓存 HTTP 请求的返回结果;
- Cors: 用来设置 options 请求的返回头,然后退出中间件链,并结束请求(浏览器跨域设置);
- RequestID: 用来在每一个 HTTP 请求的 context, response 中注入
x-request-id
键值对。
Gin 中间件其实就是一个 func(c *gin.Context)
类型的函数。在函数中可以从 c
中解析请求,执行通用的处理逻辑、设置返回参数等。
NoCache 及 Cors 中间件代码如下(位于 internal/pkg/middleware/header.go 文件中):
// NoCache 是一个 Gin 中间件,用来禁止客户端缓存 HTTP 请求的返回结果.
func NoCache(c *gin.Context) {c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))c.Next()
}// Cors 是一个 Gin 中间件,用来设置 options 请求的返回头,然后退出中间件链,并结束请求(浏览器跨域设置).
func Cors(c *gin.Context) {if c.Request.Method != "OPTIONS" {c.Next()} else {c.Header("Access-Control-Allow-Origin", "*")c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")c.Header("Content-Type", "application/json")c.AbortWithStatus(200)}
}
上述代码,通过设置返回头来实现相关的中间件功能。
RequestID 中间件位于 internal/pkg/middleware/requestid.go 文件中,代码如下:
// RequestID 是一个 Gin 中间件,用来在每一个 HTTP 请求的 context, response 中注入 `x-request-id` 键值对.
func RequestID() gin.HandlerFunc {return func(c *gin.Context) {// 从请求头中获取 `x-request-id`,如果不存在则生成新的 UUIDrequestID := c.Request.Header.Get(known.XRequestID)if requestID == "" {requestID = uuid.New().String()}// 将 RequestID 保存到 context.Context 中,以便后续程序使用ctx := contextx.WithRequestID(c.Request.Context(), requestID)c.Request = c.Request.WithContext(ctx)// 将 RequestID 保存到 HTTP 返回头中,Header 的键为 `x-request-id`c.Writer.Header().Set(known.XRequestID, requestID)// 继续处理请求c.Next()}
}
RequestID 中间件尝试从 c
中获取 x-request-id
请求头,如果获取不到则生成一个新的请求 ID 并设置为请求头 x-request-id
的值。为了能够在代码中方便的获取请求 ID,例如:可以打印到日志中,方便串联整个请求日志。还通过 contextx.WithRequestID
调用,将请求 ID 保存在了自定义上下文 contextx 中。
中间件最后通过 c.Writer.Header().Set(known.XRequestID, requestID) 调用,将请求 ID 设置到了返回头中,这样可以将请求 ID 也头传给客户端。方便出问题时提供请求 ID 进行排查。
因为 x-request-id
会在多个地方被访问,为了更好的管理这种通用的常量,将 x-request-id
以常量的形式保存在 known 包中(位于 internal/pkg/known/known.go 文件中),包内容如下:
package knownconst (// XRequestID 用来定义上下文中的键,代表请求 ID.XRequestID = "x-request-id"
)
为了能够很方便的从 context.Context
中获取请求 ID,fastgo 项目引入了自定义上下文包 contextx。contextx 提供了便捷的函数,给 context.Context
添加键值对,并从 context.Conext
中根据键获取对应的值。contextx 包代码位于 internal/pkg/contextx/contextx.go 文件中,内容如下:
package contextximport ("context"
)// 定义用于上下文的键.
type (// requestIDKey 定义请求 ID 的上下文键.requestIDKey struct{}
)// WithRequestID 将请求 ID 存放到上下文中.
func WithRequestID(ctx context.Context, requestID string) context.Context {return context.WithValue(ctx, requestIDKey{}, requestID)
}// RequestID 从上下文中提取请求 ID.
func RequestID(ctx context.Context) string {requestID, _ := ctx.Value(requestIDKey{}).(string)return requestID
}
这里要注意,他通过定义一个新的类型 requestIDKey struct{}
,可以避免键名冲突。因为类型是唯一的。
加载 Gin 中间件
修改 internal/apiserver/server.go 文件,在文件中添加以下代码行,来加载 Gin 路由:
package apiserverimport (...mw "github.com/onexstack/fastgo/internal/pkg/middleware"...
)
...
// NewServer 根据配置创建服务器.
func (cfg *Config) NewServer() (*Server, error) {...// gin.Recovery() 中间件,用来捕获任何 panic,并恢复mws := []gin.HandlerFunc{gin.Recovery(), mw.NoCache, mw.Cors, mw.RequestID()}engine.Use(mws...)...
}
编译并运行
执行以下命令编译并运行 fg-apiserver:
$ ./build.sh
$ _output/fg-apiserver -c configs/fg-apiserver.yaml
打来一个新的 Linux 终端,并执行以下命令:
$ curl http://127.0.0.1:6666/healthz -v
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to 127.0.0.1 (127.0.0.1) port 6666 (#0)
> GET /healthz HTTP/1.1
> Host: 127.0.0.1:6666
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate, value
< Content-Type: application/json; charset=utf-8
< Expires: Thu, 01 Jan 1970 00:00:00 GMT
< Last-Modified: Sat, 08 Mar 2025 01:57:11 GMT
< X-Request-Id: 4e27e3ec-85c9-4ffc-8f0d-fa84c8053d19
< Date: Sat, 08 Mar 2025 01:57:11 GMT
< Content-Length: 15
<
* Connection #0 to host 127.0.0.1 left intact
{"status":"ok"}
可以看到,请求返回投中,成功返回了请求 ID:X-Request-Id
,及 NoCache 中间件设置的返回头。
相关文章:
11 | 给 Gin 服务器添加中间件
提示: 所有体系课见专栏:Go 项目开发极速入门实战课;欢迎加入 云原生 AI 实战 星球,12 高质量体系课、20 高质量实战项目助你在 AI 时代建立技术竞争力(聚焦于 Go、云原生、AI Infra);本节课最终…...
selenium等待
通常代码执行的速度⽐页⾯渲染的速度要快,如果避免因为渲染过慢出现的⾃动化误报的问题呢?可以使⽤selenium中提供的三种等待⽅法: 1. 隐式等待(Implicit Wait) 隐式等待适用于全局,它告诉 WebDriver 在查找元素时等待一定的时间,直到元素出现。 如果超时,WebDriver 不…...
为什么List、Set集合无法在遍历的时候修改内部元素
以常用集合ArrayList为例,ArrayList 在遍历过程中无法直接修改内部元素的结构(例如通过 remove() 或 add() 方法修改元素),是因为 遍历的过程中修改结构 可能会导致 不一致的行为、并发修改异常 或 逻辑错误。 注意:和…...
使用 Elasticsearch 构建多模式 RAG 系统:哥谭市的故事
作者:来自 Elastic Alex Salgado 学习如何构建一个多模态检索增强生成 (RAG) 系统,该系统集成文本、音频、视频和图像数据,以提供更丰富的、具有上下文的信息检索。 在这篇博客中,你将学习如何使用 Elasticsearch 构建一个多模态 …...
单一责任原则在Java设计模式中的深度解析
在软件开发中,设计模式提供了一种解决特定问题的思路。在众多的设计原则中,单一责任原则(Single Responsibility Principle,SRP)是一个非常重要的概念。它主要强调一个类应该只有一个责任,也就是说…...
设计模式学习记录
设计模式23种 创建型抽象工厂模式工厂模式生成器模式原型模式单例模式 结构型适配器模式桥接模式组合模式装饰模式外观模式享元模式代理模式 行为型责任链模式命令模式解释器模式迭代器模式中介者模式备忘录模式观察者模式状态模式策略模式模版方法模式访问者模式 创建型 与对…...
set_clock_groups
一、命令参数与工具处理逻辑 核心参数定义 参数定义工具行为工具兼容性-asynchronous完全异步时钟组,无任何相位或频率关系(如独立晶振、不同时钟树)工具完全禁用组间路径的时序分析,但需用户自行处理跨时钟域(CDC&a…...
QT创建项目(项目模板、构建系统、选择类、构建套件)
1. 项目模版 项目类型界面技术适用场景核心依赖模块开发语言Qt Widget ApplicationC Widgets传统桌面应用(复杂控件)Qt WidgetsCQt Console Application无 GUI命令行工具、服务Qt CoreCQt Quick ApplicationQML/Quick现代跨平台应用(动画/触…...
麒麟系统利用pycharm生成deb文件
在麒麟系统(Kylin OS)上使用 PyCharm 进行 Python 开发并生成 .deb 可安装软件包,可以按照以下步骤进行操作: 1. 准备工作 安装 PyCharm:确保已经在麒麟系统上安装了 PyCharm,可以使用官方提供的安装包进…...
超声重建,3D重建 超声三维重建,三维可视化平台 UR 3D Reconstruction
1. 超声波3D重建技术的实现方法与算法 技术概述 3D超声重建是一种基于2D超声图像生成3D体积数据的技术,广泛应用于医学影像领域。通过重建和可视化三维结构,3D超声能够显著提高诊断精度和效率,同时减少医生的脑力负担。本技术文档将详细阐述…...
Qt 信号与槽
目录 Qt信号和槽 connect函数 connect使用方法 自定义信号 与 自定义槽 Qt界面化工具自动生成的槽 自定义信号 带参数的信号和槽 信号与槽的断开 Qt信号和槽 谈到信号,设计3个要素 信号源:谁发出了信号 信号触发条件:哪个控件的哪个…...
卷积神经网络 - 卷积的变种、数学性质
本文我们来学习卷积的变种和相关的数学性质,为后面学习卷积神经网络做准备,有些概念可能不好理解,可以先了解其概念,然后慢慢理解、逐步深入。 在卷积的标准定义基础上,还可以引入卷积核的滑动步长和零填充来增加卷积…...
ubuntu 和 RV1126 交叉编译Mosqutiio-1.6.9
最近需要交叉编译mosquitto,遇到一些小问题记录一下。 1.众所周知使用它自带的Makefile编译的时候,只需要在编译前,指定它config.mk中的变量:CFLAGS头文件路径 和 LDFLAGS库文件路径就ok,例子如下: expor…...
从零开始学习机器人---如何高效学习机械原理
如何高效学习机械原理 1. 理解课程的核心概念2. 结合图形和模型学习3. 掌握公式和计算方法4. 理论与实践相结合5. 总结和复习6. 保持好奇心和探索精神 总结 机械原理是一门理论性和实践性都很强的课程,涉及到机械系统的运动、动力传递、机构设计等内容。快速学习机械…...
STM32 RS232通信开发全解析 | 零基础入门STM32第五十九步
主题内容教学目的/扩展视频RS232串口电路原理,跳线设置,驱动程序。与超级终端通信。了解电路原理和RS232协议。 师从洋桃电子,杜洋老师 📑文章目录 一、RS232通信系统架构二、RS232核心原理与硬件设计2.1 电气特性对比2.2 典型电路…...
文献分享: 对ColBERT段落多向量的剪枝——基于学习的方法
原论文 1. 导论 & \textbf{\&} &方法 1️⃣要干啥:在 ColBERT \text{ColBERT} ColBERT方法中,限制每个段落要保留的 Token \text{Token} Token的数量,或者说对段落 Token \text{Token} Token进行剪枝 2️⃣怎么干:注…...
(已解决)aws 上 部署Splunk 负载均衡unhealthy
在AWS 部署Splunk 服务,instance 是后端的EC2, 我把splunk 服务起好后,发现port : 8000 是listening: #netstat -an | grep 80 tcp 0 0 127.0.0.1:8065 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:8089 0.0.0.0:* …...
C# 异步编程
概述 同步:指必须等待前一个操作完成,后续操作才能继续。同步操作会阻塞线程直到任务完成。 异步:异步操作不会阻塞线程,允许程序在等待某个任务完成的同时,继续执行其他任务。 异步编程适用场景: 1、从…...
缓存之美:Guava Cache 相比于 Caffeine 差在哪里?
大家好,我是 方圆。本文将结合 Guava Cache 的源码来分析它的实现原理,并阐述它相比于 Caffeine Cache 在性能上的劣势。为了让大家对 Guava Cache 理解起来更容易,我们还是在开篇介绍它的原理: Guava Cache 通过分段(…...
Go string 字符串底层逻辑
在 Go 语言中,string 类型的底层结构是一个结构体,包含两个字段:一个指向字节数组的指针和该字节数组的长度。以下是其在 Go 源码中的大致定义:type stringStruct struct {str unsafe.Pointerlen int } str:这是一个指…...
高效集成聚水潭采购退货数据到MySQL的最佳实践
聚水潭数据集成到MySQL:采购退货单的高效对接方案 在企业的数据管理和分析过程中,数据的准确性和实时性至关重要。本文将分享一个具体的系统对接集成案例:如何通过轻易云数据集成平台,将聚水潭中的采购退货单数据高效地集成到MyS…...
STM32步进电机S型与T型加减速算法
目录 一、基本原理 二、常见类型 三、算法详解 四、应用场合 五、代码实现 1、main...
centos操作系统上传和下载百度网盘内容
探序基因 整理 进入百度网盘官网百度网盘 客户端下载 下载linux的rpm格式的安装包 在linux命令行中输入:rpm -ivh baidunetdisk_4.17.7_x86_64.rpm 出现报错: 错误:依赖检测失败: libXScrnSaver 被 baidunetdisk-4.17.7-1.x8…...
深入 Python 网络爬虫开发:从入门到实战
一、为什么需要爬虫? 在数据驱动的时代,网络爬虫是获取公开数据的重要工具。它可以帮助我们: 监控电商价格变化抓取学术文献构建数据分析样本自动化信息收集 二、基础环境搭建 1. 核心库安装 pip install requests beautifulsoup4 lxml …...
网络爬虫【简介】
我叫补三补四,很高兴见到大家,欢迎一起学习交流和进步 今天来讲一讲爬虫 一、网络爬虫的定义 网络爬虫(Web Crawler),又称为网络蜘蛛、网络机器人等,是一种按照一定规则自动抓取互联网信息的程序或脚本。它…...
Linux:Ubuntu server 24.02 上搭建 ollama + dify
一、安装Ubuntu 具体的安装过程可以参见此链接:链接:Ubuntu Server 20.04详细安装教程,这里主要记录一下过程中遇到的问题。 安装时subnet如何填写 在Ubuntu中subnet填写255.255.255.0是错误的,其格式为 xx.xx.xx.xx/yy &#…...
【生日蛋糕——DFS剪枝优化】
题目 分析 代码 #include <bits/stdc.h> using namespace std;const int N 24; const int inf 0x3f3f3f3f;int mins[N], minv[N]; int R[N], H[N]; int n, m, ans inf;void dfs(int u, int v, int s) {if(v minv[u] > n) return;if(s mins[u] > ans) return;…...
RabbitMq C++客户端的使用
1.RabbitMq介绍 RabbitMQ 是一款开源的消息队列中间件,基于 AMQP(高级消息队列协议)实现,支持多种编程语言和平台。以下是其核心特点和介绍: 核心特点 多语言支持 提供 Java、Python、C#、Go、JavaScript 等语言的客…...
入门基础项目-前端Vue_02
文章目录 1. 用户信息1.1 整体设计1.2 完整代码 User.vue1.2.1 数据加载1.2.2 表格 el-table1.2.2.1 多选1.2.2.2 自定义列的内容 Slot1.2.2.3 图片 el-image1.2.2.4 分页 el-pagination 1.2.3 编辑1.2.3.1 弹出框 el-dialog1.2.3.2 上传 el-upload 1.2.4 新增1.2.5 删除1.2.6 …...
C#中SerialPort 的使用
最近在学习C#的SerialPort ,关于SerialPort 的使用,做如下总结: 1.可以通过函数System.IO.Ports.SerialPort.GetPortNames() 将获得系统所有的串口名称。C#代码如下: string[] sPorts SerialPort.GetPortNames(); foreach(stri…...
使用py-ffmpeg批量合成视频的脚本
我有一个小米摄像头,用它录出来的视频全部都是3s一段3s一段的。其中有几个小时的视频我需要保存,当初直接把摄像头的卡文件导出来重命名掉了,那时候没有注意,之后想剪辑/发送给别人的时候发现疯了: 1.剪辑的话&#x…...
mac安装navicat及使用
0.删除旧的 sudo rm -Rf /Applications/Navicat\ Premium.app sudo rm -Rf /private/var/db/BootCaches/CB6F12B3-2C14-461E-B5A7-A8621B7FF130/app.com.prect.NavicatPremium.playlist sudo rm -Rf ~/Library/Caches/com.apple.helpd/SDMHelpData/Other/English/HelpSDMIndexF…...
织梦dedecmsV5.7提示信息提示框美化(带安装教程和效果展示)
一、效果展示 1、安装前效果 2、安装后效果 二、安装说明 1、安装测试版本:DedeCMS-V5.7.117-UTF8; 2、必须在修改代码之前请做好文件备份,以免误操无法恢复; 3、为了兼容其他版本,请在安装时,最好将替…...
【知识迁移的底层逻辑:从符号到语义的升维】
大语言模型(LLMs)能够通过有限语料库实现广泛知识迁移并回答多样化问题,其核心机制在于抽象模式学习、上下文推理能力及知识组合泛化,而非简单的数据记忆。以下是具体实现路径与技术原理: 一、知识迁移的底层逻辑&…...
Windows根据文件名批量在文件夹里查找文件并复制出来,用WPF实现的详细步骤
项目前言 在日常工作和生活中,我们常常会遇到需要从大量文件中根据文件名批量查找特定文件并复制到指定位置的情况。手动一个个查找和复制文件不仅效率低下,还容易出错。使用 Windows Presentation Foundation (WPF) 可以创建一个用户友好的图形界面应用…...
Certbot实现SSL免费证书自动续签(CentOS 7版 + Docker部署的nginx)
前置安装,可参考Certbot实现SSL免费证书自动续签(CentOS 7 nginx/apache) 如果是通过 Docker 运行 Nginx, certbot 无法直接检测到本地的 Nginx 配置。解决方案是 使用 standalone 模式 或 挂载 Webroot 方式获取 SSL 证书&…...
一周学会Flask3 Python Web开发-SQLAlchemy查询所有数据操作-班级模块
锋哥原创的Flask3 Python Web开发 Flask3视频教程: 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 我们来新建一个的蓝图模块-班级模块,后面可以和学生模块,实现一对多的数据库操作。 blueprint下新建g…...
工程实践:如何使用SU17无人机来实现室内巡检任务
阿木实验室最近发布了科研开发者版本的无人机SU17,该无人机上集成了四目视觉,三维激光雷达,云台吊舱,高算力的机载计算机,是一个非常合适的平台用于室内外巡检场景。同时阿木实验室维护了多个和无人机相关的开源项目。…...
14.使用各种读写包操作 Excel 文件:辅助模块
一 各种读写包 这些是 pandas 在底层使用的各种读写包。无须安装 pandas,直接使用这些读写包就能够读写 Excel 工作簿。可以尽可能地使用 pandas 来解决这类问题,只在 pandas 没有提供你所需要的功能时才用到读写包。 表中没有 xlwings ,因为…...
深入理解 Maven BOM 及其继承特性
深入理解 Maven BOM 及其继承特性 一、什么是 Maven BOM? Maven BOM(Bill Of Materials,物料清单)是一种特殊的 Maven 项目,用于集中管理依赖项的版本信息。BOM 项目本身并不包含实际的代码或资源,而仅仅…...
责任链模式如何减少模块之间的耦合
责任链模式如何减少模块之间的耦合 在复杂的软件系统中,模块之间的耦合是一个常见的问题。高耦合的代码不仅增加了维护成本,还会导致系统的扩展性和灵活性受限。当我们需要为不同的请求设计灵活的处理逻辑时,传统的硬编码方式会将请求的发送…...
Java面试:集合框架体系
一、ArrayList 1.数组(Array) 是一种用连续的内存空间存储相同数据类型数据的线性数据结构 数组如何获取其他元素的地址值? 寻址公式:a[i] baseAddress i * dataTypeSize baseAddress:数组的首地址dataTypeSize&am…...
【八股文】ArrayList和LinkedList的区别
先讲讲两者是如何实现的 ArrayList public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable {transient Object[] elementData; private int size; } 通过源码可以看出,ArrayLis…...
sentinel限流算法
限流算法:固定窗口算法、滑动时间窗口、令牌桶和漏桶这四种常见限流算法的原理: 限流算法原理 固定窗口: 固定窗口算法将时间划分为固定大小的窗口,并在每个窗口内限制请求的数量。在每个窗口开始时,计数器重置&#…...
Spring生态下的中台架构设计:如何构建可扩展业务系统?
一、中台战略的架构觉醒 在数字化转型的浪潮中,企业面临的核心矛盾日益凸显:前端业务的快速迭代需求与后端系统刚性架构之间的矛盾。中台架构的提出,本质上是对传统单体架构和过度微服务化的辩证扬弃。Spring生态以其模块化设计理念,恰好为中台建设提供了绝佳的技术土壤。…...
Project回调函数qsort②进阶应用
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h>//库函数strcmp头文件 //使用qsort排序结构体 struct Stu { char name[20]; int age; }; //按照年龄排序 int cmp_stu_by_age(const void* e1,const void* e2) { return ((struc…...
【推荐项目】052-用水监控管理系统
052-用水监控管理系统 介绍 用水监控管理系统 springboot java vuejs jdk1.8 当然,以下是一个简洁的用水监控管理系统的功能模块划分,基于Spring Boot(JDK 1.8)后端和Vue.js前端: 用水监控管理系统功能模块 后端&…...
Hive SQL 精进系列:PERCENTILE_APPROX 搞定分位数
目录 一、引言二、percentile_approx 函数基础2.1 基本语法参数解释返回值简单示例 三、应用场景3.1 数据分析与报告3.2 数据清洗与异常值检测3.3 性能监控与优化 四、使用注意事项4.1 数据类型要求4.2 精度与性能平衡4.3 空值处理 五、总结 一、引言 百分位数作为一种常用的统…...
使用Hbuilder发布小程序显示发布失败?
接受了一个新uniapp项目 但是在Hbuilder中发行报错 小程序发行失败 试了几次还是不行 写代码的人也走了,头疼。 不用Hbuilder小程序的主包体积又太大哎 开发工具无法上传~ 后来想看一下 这个发布失败到底有没有生成打包好的文件 如果生成了可以试一下 直接导入到微信…...
甲骨文找回二次验证的方法(超简单)
因为更换手机丢失了二次验证。 然后给客服沟通,获得了找到二次验证的办法,希望对你有用。 1、登录到账号登陆界面,查看地址栏当中自己的IDCE地址(yourIDCS_Stripe_here)部分,并复制。 https://idcs-yourID…...