当前位置: 首页 > news >正文

Go语言 Gin框架 使用指南

Gin 是一个用 Go (Golang) 编写的 Web 框架。 它具有类似 martini 的 API,性能要好得多,多亏了 httprouter,速度提高了 40 倍。 如果您需要性能和良好的生产力,您一定会喜欢 Gin。Gin 相比于 Iris 和 Beego 而言,更倾向于轻量化的框架,只负责 Web 部分,追求极致的路由性能,功能或许没那么全,胜在轻量易拓展,这也是它的优点。因此,在所有的 Web 框架中,Gin 是最容易上手和学习的。

特点

  • 快速:基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。
  • 支持中间件:传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB。
  • Crash 处理:Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。
  • JSON 验证:Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。
  • 路由组:更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能。
  • 错误管理:Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。
  • 内置渲染:Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API。
  • 可扩展性:新建一个中间件非常简单。

安装

$ go get -u github.com/gin-gonic/gin

示例:

package mainimport "github.com/gin-gonic/gin"func main() {// 创建 Gin 引擎实例r := gin.Default()// 定义路由r.GET("/", func(c *gin.Context) {c.String(200, "Hello, Gin!")})// 启动服务r.Run(":8080") // 默认监听 :8080
}

将上面的代码保存并编译执行,然后使用浏览器打开127.0.0.1:8080/hello就能看到"Hello, Gin!"字符串。

RESTful API

REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”或“表现层状态转化”。

简单来说,REST的含义就是客户端与Web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作。

  • GET用来获取资源
  • POST用来新建资源
  • PUT用来更新资源
  • DELETE用来删除资源。

只要API程序遵循了REST风格,那就可以称其为RESTful API。目前在前后端分离的架构中,前后端基本都是通过RESTful API来进行交互。

例如,我们现在要编写一个管理书籍的系统,我们可以查询对一本书进行查询、创建、更新和删除等操作,我们在编写程序的时候就要设计客户端浏览器与我们Web服务端交互的方式和路径。按照经验我们通常会设计成如下模式:

请求方法URL含义
GET/book查询书籍信息
POST/create_book创建书籍记录
POST/update_book更新书籍信息
POST/delete_book删除书籍信息

同样的需求我们按照RESTful API设计如下:

请求方法URL含义
GET/book查询书籍信息
POST/book创建书籍记录
PUT/book更新书籍信息
DELETE/book删除书籍信息

Gin框架支持开发RESTful API的开发。

func main() {r := gin.Default()r.GET("/book", func(c *gin.Context) {c.JSON(200, gin.H{"message": "GET",})})r.POST("/book", func(c *gin.Context) {c.JSON(200, gin.H{"message": "POST",})})r.PUT("/book", func(c *gin.Context) {c.JSON(200, gin.H{"message": "PUT",})})r.DELETE("/book", func(c *gin.Context) {c.JSON(200, gin.H{"message": "DELETE",})})
}

开发RESTful API的时候我们通常使用Postman来作为客户端的测试工具。

获取参数

获取querystring参数

querystring指的是URL中?后面携带的参数,例如:/user/search?username=彭于晏&address=深圳。 获取请求的querystring参数的方法如下:

r := gin.Default()// 处理 /search?name=彭于晏&location=深圳
r.GET("/search", func(c *gin.Context) {// 获取必填参数,若不存在则返回400错误name := c.Query("name")// 获取可选参数,提供默认值location := c.DefaultQuery("location", "unknown")c.JSON(200, gin.H{"name":     name,"location": location,})
})

获取form参数

当前端请求的数据通过form表单提交时,例如向/register发送一个POST请求,获取请求数据的方式如下:

r.POST("/register", func(c *gin.Context) {// 获取必填表单字段username := c.PostForm("username")// 获取可选表单字段email := c.DefaultPostForm("email", "default@example.com")c.JSON(200, gin.H{"username": username,"email":    email,})
})

获取JSON参数

当前端请求的数据通过JSON提交时,例如向/users发送一个JSON格式的POST请求,则获取请求参数的方式如下:

type User struct {Username string `json:"username" binding:"required"`Email    string `json:"email"`
}r.POST("/users", func(c *gin.Context) {var user User// 自动解析JSON请求体if err := c.ShouldBindJSON(&user); err != nil {c.JSON(400, gin.H{"error": err.Error()})return}c.JSON(201, gin.H{"status": "created","user":   user,})
})

获取path参数

请求的参数通过URL路径传递,例如:/user/123。 获取请求URL路径中的参数的方式如下。

// 处理 /users/123
r.GET("/users/:id", func(c *gin.Context) {id := c.Param("id")// 处理 /files/*path 通配符路径path := c.Param("path")c.JSON(200, gin.H{"id":   id,"path": path,})
})

参数绑定

为了能够更方便的获取请求相关参数,提高开发效率,我们可以基于请求的Content-Type识别请求数据类型并利用反射机制自动提取请求中QueryStringform表单JSONXML等参数到结构体中。 下面的示例代码演示了.ShouldBind()强大的功能,它能够基于请求自动提取JSONform表单QueryString类型的数据,并把值绑定到指定的结构体对象。

// Binding from JSON
type Login struct {User     string `form:"user" json:"user" binding:"required"`Password string `form:"password" json:"password" binding:"required"`
}func main() {router := gin.Default()// 绑定JSON的示例 ({"user": "tian", "password": "123456"})router.POST("/loginJSON", func(c *gin.Context) {var login Loginif err := c.ShouldBind(&login); err == nil {fmt.Printf("login info:%#v\n", login)c.JSON(http.StatusOK, gin.H{"user":     login.User,"password": login.Password,})} else {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})}})// 绑定form表单示例 (user=tian&password=123456)router.POST("/loginForm", func(c *gin.Context) {var login Login// ShouldBind()会根据请求的Content-Type自行选择绑定器if err := c.ShouldBind(&login); err == nil {c.JSON(http.StatusOK, gin.H{"user":     login.User,"password": login.Password,})} else {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})}})// 绑定QueryString示例 (/loginQuery?user=q1mi&password=123456)router.GET("/loginForm", func(c *gin.Context) {var login Login// ShouldBind()会根据请求的Content-Type自行选择绑定器if err := c.ShouldBind(&login); err == nil {c.JSON(http.StatusOK, gin.H{"user":     login.User,"password": login.Password,})} else {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})}})// Listen and serve on 0.0.0.0:8080router.Run(":8080")
}

ShouldBind会按照下面的顺序解析请求中的数据完成绑定:

  1. 如果是 GET 请求,只使用 Form 绑定引擎(query)。
  2. 如果是 POST 请求,首先检查 content-type 是否为 JSONXML,然后再使用 Formform-data)。

文件上传

单个文件上传

// 配置上传限制(默认32MB)
router.MaxMultipartMemory = 8 << 20 // 8MBrouter.POST("/upload", func(c *gin.Context) {file, err := c.FormFile("file")if err != nil {c.JSON(500, gin.H{"error": err.Error()})return}// 安全保存文件dst := filepath.Join("/uploads", file.Filename)if err := c.SaveUploadedFile(file, dst); err != nil {c.JSON(500, gin.H{"error": "保存失败"})return}c.JSON(200, gin.H{"status":   "上传成功","filename": file.Filename,"size":     file.Size,})
})

多个文件上传

router.POST("/multi-upload", func(c *gin.Context) {form, _ := c.MultipartForm()files := form.File["files"]var results []gin.Hfor _, file := range files {dst := filepath.Join("/uploads", file.Filename)if err := c.SaveUploadedFile(file, dst); err == nil {results = append(results, gin.H{"filename": file.Filename,"status":   "success",})}}c.JSON(200, gin.H{"total":   len(files),"success": len(results),"results": results,})
})

重定向

HTTP重定向

HTTP 重定向很容易。 内部、外部重定向均支持。

// 永久重定向
router.GET("/old", func(c *gin.Context) {c.Redirect(301, "/new")
})// 临时重定向
router.GET("/temp", func(c *gin.Context) {c.Redirect(302, "https://example.com")
})

路由重定向

路由重定向,使用HandleContext

router.GET("/route1", func(c *gin.Context) {c.Request.URL.Path = "/route2"router.HandleContext(c)
})router.GET("/route2", func(c *gin.Context) {c.String(200, "内部路由转发成功")
})

Gin路由

普通路由

r.GET("/index", func(c *gin.Context) {...})
r.GET("/login", func(c *gin.Context) {...})
r.POST("/login", func(c *gin.Context) {...})

此外,还有一个可以匹配所有请求方法的Any方法如下:

r.Any("/test", func(c *gin.Context) {...})

为没有配置处理函数的路由添加处理程序,默认情况下它返回404代码,下面的代码为没有匹配到路由的请求都返回views/404.html页面。

r.NoRoute(func(c *gin.Context) {c.HTML(http.StatusNotFound, "views/404.html", nil)})

路由组

我们可以将拥有共同URL前缀的路由划分为一个路由组。习惯性一对{}包裹同组的路由,这只是为了看着清晰,你用不用{}包裹功能上没什么区别。

func main() {r := gin.Default()userGroup := r.Group("/user"){userGroup.GET("/index", func(c *gin.Context) {...})userGroup.GET("/login", func(c *gin.Context) {...})userGroup.POST("/login", func(c *gin.Context) {...})}shopGroup := r.Group("/shop"){shopGroup.GET("/index", func(c *gin.Context) {...})shopGroup.GET("/cart", func(c *gin.Context) {...})shopGroup.POST("/checkout", func(c *gin.Context) {...})}r.Run()
}

路由组也是支持嵌套的,例如:

shopGroup := r.Group("/shop"){shopGroup.GET("/index", func(c *gin.Context) {...})shopGroup.GET("/cart", func(c *gin.Context) {...})shopGroup.POST("/checkout", func(c *gin.Context) {...})// 嵌套路由组xx := shopGroup.Group("xx")xx.GET("/oo", func(c *gin.Context) {...})}

通常我们将路由分组用在划分业务逻辑或划分API版本时。

Gin中间件

Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。

定义中间件

Gin中的中间件必须是一个gin.HandlerFunc类型。

记录接口耗时的中间件

例如我们像下面的代码一样定义一个统计请求耗时的中间件。

// 耗时统计中间件
func LatencyMiddleware() gin.HandlerFunc {return func(c *gin.Context) {start := time.Now()// 前置处理c.Set("traceId", uuid.NewString())// 执行后续处理器c.Next()// 后置处理latency := time.Since(start)log.Printf("[%s] %s cost %v", c.GetString("traceId"), c.Request.URL.Path, latency)}
}

记录响应体的中间件

我们有时候可能会想要记录下某些情况下返回给客户端的响应数据,这个时候就可以编写一个中间件来搞定。

type responseRecorder struct {gin.ResponseWriterbody *bytes.Buffer
}func (r *responseRecorder) Write(b []byte) (int, error) {r.body.Write(b)return r.ResponseWriter.Write(b)
}func ResponseLogger() gin.HandlerFunc {return func(c *gin.Context) {recorder := &responseRecorder{ResponseWriter: c.Writer,body:           bytes.NewBuffer(nil),}c.Writer = recorderc.Next()if c.Writer.Status() >= 400 {log.Printf("Error Response: %s", recorder.body.String())}}
}

注册中间件

在gin框架中,我们可以为每个路由添加任意数量的中间件。

为全局路由注册

r := gin.New()
r.Use(gin.Recovery(),    // panic恢复LatencyMiddleware(), // 耗时统计ResponseLogger(),   // 响应日志
)

为某个路由单独注册

// 给/metrics路由单独注册中间件(可注册多个)
r.GET("/metrics", BasicAuthMiddleware(),  // 基础认证PrometheusHandler(),    // 监控端点
)

为路由组注册中间件

为路由组注册中间件有以下两种写法。

写法1:

shopGroup := r.Group("/shop", StatCost())
{shopGroup.GET("/index", func(c *gin.Context) {...})...
}

写法2:

shopGroup := r.Group("/shop")
shopGroup.Use(StatCost())
{shopGroup.GET("/index", func(c *gin.Context) {...})...
}

中间件注意事项

gin默认中间件

gin.Default()默认使用了LoggerRecovery中间件,其中:

  • Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release
  • Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。

如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。

gin中间件中使用goroutine

当在中间件或handler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())。

运行多个服务

我们可以在多个端口启动服务,例如:

func main() {var g errgroup.Group// API服务g.Go(func() error {return startServer(":8080", apiRouter())})// 监控服务g.Go(func() error {return startServer(":8081", metricsRouter())})if err := g.Wait(); err != nil {log.Fatal(err)}
}func startServer(addr string, handler http.Handler) error {srv := &http.Server{Addr:         addr,Handler:      handler,ReadTimeout:  5 * time.Second,WriteTimeout: 10 * time.Second,}return srv.ListenAndServe()
}

参考资料:

Gin Web Framework

Golang 中文学习文档 - Web开发 - Gin

李文周的博客 - Gin框架介绍及使用

相关文章:

Go语言 Gin框架 使用指南

Gin 是一个用 Go (Golang) 编写的 Web 框架。 它具有类似 martini 的 API&#xff0c;性能要好得多&#xff0c;多亏了 httprouter&#xff0c;速度提高了 40 倍。 如果您需要性能和良好的生产力&#xff0c;您一定会喜欢 Gin。Gin 相比于 Iris 和 Beego 而言&#xff0c;更倾向…...

内容安全:使用开源框架Caffe实现上传图片进行敏感内容识别

上传图片进行敏感内容识别 预览效果 环境准备 Ubuntu 16.04python 2.7.12caffe 1.0.0 安装调试环境: sudo apt-get update sudo apt-get install -y --no-install-recommends build-essential cmake git wget libatlas-base-dev libboost-all-dev libgflags-dev sudo apt-g…...

缓慢变化维度(SCD)策略

缓慢变化维度&#xff08;SCD&#xff09;策略 缓慢变化维度&#xff08;SCD&#xff09;策略是数据仓库中处理维度属性随时间变化的核心技术&#xff0c;根据业务需求的不同&#xff0c;主要分为以下类型&#xff1a; 1. SCD Type 0&#xff08;固定维度&#xff09; 定义&a…...

【Mysql】详解InnoDB存储引擎以及binlog,redelog,undolog+MVCC

1.InnoDB存储引擎 在Mysql中&#xff0c;InnoDB存储引擎是默认的&#xff0c;也是我们最常用的一个存储引擎&#xff0c;其中分为内存结构和磁盘结构两大部分&#xff0c;整体架构图如下&#xff1a; 1.1Buffer Pool Buffer pool(缓存区)是Mysql内存的一个主要区域&#xff0…...

面向对象详解和JVM底层内存分析

神速熟悉面向对象 表格结构和类结构 我们在现实生活中&#xff0c;思考问题、发现问题、处理问题&#xff0c;往往都会用“表格”作为工具。实际上&#xff0c;“表格思维”就是一种典型的面向对象思维。 实际上&#xff0c;互联网上所有的数据本质上都是“表格”。我们在这里…...

C语言指针深入详解(一):内存和地址、指针变量和地址、指针变量类型的意义、指针运算

目录 一、内存和地址 &#xff08;一&#xff09;内存 &#xff08;二&#xff09;如何理解编址 二、指针变量和地址 &#xff08;一&#xff09;取地址操作符&#xff08;&&#xff09; &#xff08;二&#xff09;指针变量和解引用操作符&#xff08;*&#xff09;…...

MATLAB中进行深度学习网络训练的模型评估步骤

文章目录 前言环境配置一、基础性能评估二、高级评估指标三、模型解释与可视化四、交叉验证与模型选择五、部署前的优化 前言 在 MATLAB 中进行深度学习网络训练后的模型评估是确保模型性能和可靠性的关键环节。以下是详细的评估步骤和方法。 环境配置 MATLAB下载安装教程&…...

30、WebAssembly:古代魔法——React 19 性能优化

一、符文编译术&#xff08;编译优化&#xff09; 1. 语言选择与量子精简 // Rust编译优化 cargo build --target wasm32-wasi --release 魔法特性&#xff1a; • 选择低运行时开销语言&#xff08;如Rust/C&#xff09;&#xff0c;编译后文件比Swift小4倍 • --rel…...

Python集合运算:从基础到进阶全解析

Python基础&#xff1a;集合运算进阶 文章目录 Python基础&#xff1a;集合运算进阶一、知识点详解1.1 集合运算&#xff08;运算符 vs 方法&#xff09;1.2 集合运算符优先级1.3 集合关系判断方法1.4 方法对比 二、说明示例2.1 权限管理系统2.2 数据去重与差异分析2.3 数学运算…...

【开源Agent框架】Suna架构设计深度解析与应用实践

一、项目基本介绍 Suna是一款全栈开源的通用型AI代理系统,其名称源自日语"砂"的发音,寓意如流沙般渗透到各类数字任务中。项目采用Apache 2.0协议,由Kortix AI团队维护,核心开发者包括Adam Cohen Hillel等三位主要贡献者。 技术架构全景 系统由四大核心组件构…...

C++类与对象--2 对象的初始化和清理

C面向对象来源于生活&#xff0c;每个对象都有初始化设置和销毁前的清理数据的设置。 2.1 构造函数和析构函数 &#xff08;1&#xff09;构造函数 初始化对象的成员属性不提供构造函数时&#xff0c;编译器会提供不带参数的默认构造函数&#xff0c;函数实现是空的构造函数不…...

计网| 网际控制报文协议(ICMP)

目录 网际控制报文协议&#xff08;ICMP&#xff09; 一、ICMP 基础特性 二、ICMP 报文分类及作用 差错报告报文 询问报文 网际控制报文协议&#xff08;ICMP&#xff09; ICMP&#xff08;Internet Control Message Protocol&#xff0c;网际控制报文协议&#xff09;是 …...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...

printf耗时高的原因

背景&#xff1a;设备升级初始化失败。具体表现为&#xff1a;app在启动dsp后&#xff0c;需在15秒内与其建立连接以确认通信成功&#xff0c;但当前未能在此时间限制内完成连接。 排查过程&#xff1a;通过在初始化过程中添加耗时打印&#xff0c;发现各阶段耗时虽不高&#…...

20250517 我设想一个空间,无限大,空间不与其中物质进行任何作用,甚至这个空间能容纳可以伸缩的空间

1.我设想一个空间&#xff0c;无限大&#xff0c;空间不与其中物质进行任何作用&#xff0c;甚至这个空间能容纳可以伸缩的空间 您设想的这个空间具有一些有趣的特点&#xff1a; 无限大&#xff1a;空间本身没有边界或限制&#xff0c;理论上可以容纳无限多的物质或结构。非…...

GO语言学习(二)

GO语言学习&#xff08;二&#xff09; method&#xff08;方法&#xff09; 这一节我们介绍一下GO语言的面向对象&#xff0c;之前我们学习了struct结构体&#xff0c;现在我们来解释一下方法method主要是为了简化代码&#xff0c;在计算同类时&#xff0c;使用函数接收方法…...

神经网络与深度学习第六章--循环神经网络(理论)

#第六章-循环神经网络 前馈神经网络的缺点&#xff1a; ①信息的传递是单向的。前馈神经网络可以看作一个复杂的函数&#xff0c;每次的输入都是独立的&#xff0c;即网络的输出只依赖于当前的输入。前馈神经网络是一种静态网络&#xff0c;没有记忆能力&#xff0c;就无法模拟…...

第三十五节:特征检测与描述-ORB 特征

1. 引言:为什么需要ORB? 在计算机视觉领域,特征检测与描述是许多任务(如图像匹配、目标跟踪、三维重建等)的核心基础。传统的算法如SIFT(尺度不变特征变换)和SURF(加速稳健特征)因其优异的性能被广泛应用,但它们存在两个显著问题: 专利限制:SIFT和SURF受专利保护,…...

重庆 ICPC 比赛游记

2025.5.9 比赛前一天晚上&#xff0c;激动地睡不着觉&#xff0c;起来收拾了好多东西。&#xff08;其实就四本书&#xff0c;剩下的全是零食……关键在于这四本书基本没用。&#xff09; 2025.5.10 学校丧心病狂的让我们 6:20 到校门口集合坐车&#xff08;据说是怕赶不上比…...

二进制与十进制互转的方法

附言: 在计算机科学和数字系统中&#xff0c;二进制和十进制是最常见的两种数制。二进制是计算机内部数据存储和处理的基础&#xff0c;而十进制则是我们日常生活中最常用的数制。因此&#xff0c;掌握二进制与十进制之间的转换方法对于计算机学习者和相关领域的从业者来说至关…...

咖啡叶子病害检测数据集VOC+YOLO格式1468张4类别均为单叶子

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1468 标注数量(xml文件个数)&#xff1a;1468 标注数量(txt文件个数)&#xff1a;1468 …...

JDBC实现模糊、动态与分页查询的详解

文章目录 一. 模糊查询1. Mysql的写法2. JDBC的实现 二. 动态条件查询1. 创建生成动态条件查询sql的方法2. 完整的动态条件查询类以及测试类 三. 分页查询1. 什么是分页查询&#xff1f;2. 分页查询的分类3. MySQL的实现4. JDBC实现4.1. 创建page页4.2. 分页的实现 本章来讲一下…...

golang读、写、复制、创建目录、删除、重命名,文件方法总结

文章目录 一、只读文件二、写入文件三、复制文件四、创建目录五、删除目录/文件五、重命名文件 一、只读文件 file, err : os.Open("./main.go")defer file.Close() //打开文件一定要关闭关闭文件if err ! nil {fmt.Println("文件打开失败", err)}/*方案一…...

信贷域——互联网金融业务

摘要 本文深入探讨了信贷域全托与半托业务的定义、特点、适用场景及注意事项&#xff0c;并分析了互联网金融核心信息流的多个方面&#xff0c;包括资金流、信息流、风险流、合规流、物流、技术流和商流&#xff0c;还阐述了金融系统“断直连”业务的相关内容&#xff0c;以及…...

计算机操作系统概要

不谋万世者&#xff0c;不⾜谋⼀时。不谋全局者 &#xff0c;足谋⼀域 。 ——陈澹然《寤⾔》《迁都建藩议》 操作系统 一.对文件简单操作的常用基础指令 ls ls 选项 目录或⽂件名:罗列当前⽬录下的⽂件 -l&#xff1a;以长格式显示⽂件和⽬录的详细信息 -a 或 --all&…...

gRPC开发指南:Visual Studio 2022 + Vcpkg + Windows全流程配置

前言 gRPC作为Google开源的高性能RPC框架&#xff0c;在微服务架构中扮演着重要角色。本文将详细介绍在Windows平台下&#xff0c;使用Visual Studio 2022和Vcpkg进行gRPC开发的完整流程&#xff0c;包括环境配置、项目搭建、常见问题解决等实用内容。 环境准备 1. 安装必要组…...

MATLAB安装常见问题及解决办法

MATLAB安装失败 安装MATLAB时可能会遇到失败的情况,通常是由于系统环境不兼容或安装文件损坏。确保系统满足MATLAB的最低要求,并重新下载安装文件。如果问题仍然存在,可以尝试以管理员身份运行安装程序。 许可证激活问题 在激活MATLAB许可证时,可能会遇到激活失败或无法…...

英语学习5.17

attract &#x1f449; 前缀&#xff1a;at-&#xff08;朝向&#xff09; &#x1f449; 含义&#xff1a;吸引&#xff08;朝某处拉&#xff09; 例句&#xff1a;The flowers attract bees. &#xff08;花吸引蜜蜂。&#xff09; distract &#x1f449; 前缀&#xff…...

深入解析 React 的 useEffect:从入门到实战

文章目录 前言一、为什么需要 useEffect&#xff1f;核心作用&#xff1a; 二、useEffect 的基础用法1. 基本语法2. 依赖项数组的作用 三、依赖项数组演示1. 空数组 []&#xff1a;2.无依赖项&#xff08;空&#xff09;3.有依赖项 四、清理副作用函数实战案例演示1. 清除定时器…...

Scrapy进阶实践指南:从脚本运行到分布式爬取

Scrapy作为Python生态中最强大的爬虫框架之一&#xff0c;其官方文档的"Common Practices"章节总结了多个高频使用场景的解决方案。本文将深入解析如何通过脚本控制爬虫、多爬虫协同工作、分布式部署策略以及反反爬技巧&#xff0c;帮助开发者突破基础使用限制。 一…...

(面试)TCP、UDP协议

TCP&#xff08;传输控制协议&#xff09;和UDP&#xff08;用户数据报协议&#xff09;是互联网核心的传输层协议&#xff0c;负责应用程序之间的数据传输。它们在设计目标、特性和适用场景上有显著差异&#xff1a; TCP&#xff1a;面向连接&#xff0c;可靠的&#xff0c;速…...

数据库blog1_信息(数据)的处理与效率提升

&#x1f33f;信息的处理 &#x1f342;实际中离不开信息处理 ● 解决问题的建模 任何对问题的处理都可以看作数据的输入、处理、输出。 eg.一个项目中&#xff0c;用户点击信息由前端接收传递到后端处理后返回结果eg.面对一个问题&#xff0c;我们在搜集信息后做出处理与分析…...

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(23):受身形

日语学习-日语知识点小记-构建基础-JLPT-N4阶段(23):受身形 1、前言(1)情况说明(2)工程师的信仰2、知识点(1)うけみけい 受身形1、グループ2、グループ3、グループ(2) か ~かどうか1、か2、かどうか3、单词(1)日语(2)日语片假名单词4、相近词练习5、单词…...

kubernetes的Service与服务发现

kubernetes的Service与服务发现 1 Service1.1 Service概念1.2 Service类型1.2.1 ClusterIP1.2.2 NodePort1.2.3 LoadBalancer1.2.4 ExternalName1.2.5 Headless 2 CoreDNS2.1 CoreDNS概念2.2 CoreDNS插件架构2.3 CoreDNS在kubernetes下的工作原理2.4 Pod上的DNS解析策略 3 Ingr…...

python打卡day28

类的简单复习 知识点回顾&#xff1a; 类的定义pass占位语句类的初始化方法类的普通方法类的继承&#xff1a;属性的继承、方法的继承 类就是对属性和方法的封装&#xff0c;一个常见的类的定义包括了&#xff1a; 关键字class类名语法固定符号冒号(:)一个初始化函数__init__(…...

【学习心得】英伟达的诸多显卡性能对比

型号 CUDA核心 显存容量 算力&#xff08;FP32/TFLOPS&#xff09; A100 6912 HBM2e/80G 19.49 A800 6912 HBM2e/80G 19.49 H100 14592 HBM3/80G 51.22 H800 14592 HBM3/80G 51.22 T4 4352 GDDR6/16G 8.14 P40 3840 GDDR5/24G 11.76 L40 18176 G…...

使用Pinia持久化插件-persist解决刷新浏览器后数据丢失的问题

文章目录 一、现象二、原因三、解决&#xff1a;使用Pinia持久化插件-persist安装persistpinia中使用persist插件在创建定义状态时配置持久化 四、参考资料 一、现象 登录成功后&#xff0c;能正常看到文章分类的数据&#xff0c;但只要刷新浏览器就提示服务异常 二、原因 P…...

mysql中4种扫描方式和聚簇索引非聚簇索引【爽文一篇】

目录 一 mysql的聚簇索引&非聚簇索引 1.1 数据表 1.2 聚簇索引 1.3 非聚簇索引 1.4 覆盖索引 二 mysql的4种扫描查询 2.1 全表扫描 2.2 索引扫描 2.3 覆盖索引扫描 2.4 回表扫描 2.5 总结 三 mysql的回表查询详解 3.1 回表查询 一 mysql的聚簇索引&非聚簇…...

交流学习 | 江西同为科技有限公司赴海尔总部考察交流

2025年4月8日至9日&#xff0c;江西同为科技有限公司在江西省科技装备商会的带领下&#xff0c;以蔡文君经理为代表&#xff0c;一行人赴山东青岛海尔总部开展两天的考察交流活动。本次考察不仅深入剖析了海尔企业的前沿技术与管理理念&#xff0c;更促进了行业内科技创新、商业…...

AGI大模型(20):混合检索之rank_bm25库来实现词法搜索

1 混合检索简介 混合搜索结合了两种检索信息的方法 词法搜索 (BM25) :这种传统方法根据精确的关键字匹配来检索文档。例如,如果您搜索“cat on the mat”,它将找到包含这些确切单词的文档。 基于嵌入的搜索(密集检索) :这种较新的方法通过比较文档的语义来检索文档。查…...

QT调用Halcon查询所有摄像头名称

QT软件中的测试代码 //获取当前连接的所有设备信息实例HTuple hv_general, hv_ValueList;InfoFramegrabber("DirectShow", "device", &hv_general, &hv_ValueList);qDebug()<<QString::fromUtf8(hv_general.S().Text());//Value list for de…...

16 C 语言布尔类型与 sizeof 运算符详解:布尔类型的三种声明方式、执行时间、赋值规则

1 布尔类型 1.1 布尔类型概述 布尔类型用于表示逻辑上的真&#xff08;true&#xff09;和假&#xff08;false&#xff09;两种状态&#xff0c;是编程中条件判断和逻辑运算的基础。在 C 语言中&#xff0c;布尔值的表示方式随着标准的发展而不断完善。 1.2 布尔类型的三种声…...

配置ssh服务-ubuntu到Windows拷贝文件方法

背景&#xff1a; 在工作中&#xff0c;需要频繁从ubuntu到Windows拷贝文件&#xff0c;但有时间总是无法拷出&#xff0c;每次重启虚拟机又比较麻烦并且效率较低。可以使用scp服务进行拷贝&#xff0c;不仅稳定而且高效&#xff0c;现将配置过程进行梳理&#xff0c;以供大家参…...

使用ts-node搭建typescript运行环境

目录 首先安装好node.js 安装typescript 安装ts-node 创建一个typescript文件 使用ts-node运行typescript文件 首先安装好node.js 安装typescript npm install typescript4.7.4 -g 安装ts-node npm install ts-nodev10.8.1 -g 创建一个typescript文件 文件名为app.ts&a…...

如何深入学习MATLAB的高级应用?

文章目录 要深入学习 MATLAB 的高级应用&#xff0c;需要在掌握基础语法后&#xff0c;系统性地学习特定领域的工具箱和算法&#xff0c;并通过实战项目提升能力。以下是分阶段的学习路径和资源推荐&#xff1a; 一、深化核心技能 高级矩阵运算与线性代数 matlab % 稀疏矩阵处…...

英汉 “语言” 初印象:符号背后的文化底色​

英汉 “语言” 初印象&#xff1a;符号背后的文化底色​ ​ 原始尺寸更换图片 ​​ 在生活里&#xff0c;我们每天都会进行各式各样的交流&#xff0c;或许不曾留意&#xff0c;汉语和英语这两种极具代表性的语言&#xff0c;从最简单的问候语中就能展现出它们独特的文化内…...

C语言_编译全攻略_从原理到实战的深度解析

在 C 语言开发中,编译是连接源代码与可执行程序的关键桥梁。理解编译过程不仅能提升开发效率,更能帮助我们定位内存泄漏、性能瓶颈等深层次问题。本文将从编译原理出发,结合 GCC 工具链,带你掌握 C 语言编译的核心技术。 一、编译流程底层原理 1. 编译四阶段详解 预处理…...

AGI大模型(21):混合检索之混合搜索

为了执行混合搜索,我们结合了 BM25 和密集检索的结果。每种方法的分数均经过标准化和加权以获得最佳总体结果 1 代码 先编写 BM25搜索的代码,再编写密集检索的代码,最后进行混合。 from rank_bm25 import BM25Okapi from nltk.tokenize import word_tokenize import jieb…...

Vue3学习(组合式API——ref模版引用与defineExpose编译宏函数)

目录 一、ref模版引用。 &#xff08;1&#xff09;基本介绍。 &#xff08;2&#xff09;核心基本步骤。(以获取DOM、组件为例) &#xff08;3&#xff09;案例&#xff1a;获取dom对象演示。 <1>需求&#xff1a;点击按钮&#xff0c;让输入框聚焦。 &#xff08;4&…...

Zephyr OS 中的 FIFO 接口应用介绍

目录 概述 1 FIFO的接口函数 1.1 K_FIFO_DEFINE函数 1.2 k_fifo_init函数 1.3 k_fifo_put函数 1.4 k_fifo_get 函数 1.5 k_fifo_is_empty 函数 2 应用验证 2.1 UART中使用FIFO范例 2.2 生产-消费类型范例 3 注意事项 3.1 内存管理 3.2 线程安全边界 概述 Zephy…...