GO学习之 微框架(Gin)
GO系列
1、GO学习之Hello World
2、GO学习之入门语法
3、GO学习之切片操作
4、GO学习之 Map 操作
5、GO学习之 结构体 操作
6、GO学习之 通道(Channel)
7、GO学习之 多线程(goroutine)
8、GO学习之 函数(Function)
9、GO学习之 接口(Interface)
10、GO学习之 网络通信(Net/Http)
11、GO学习之 微框架(Gin)
文章目录
- GO系列
- 前言
- 一、Gin 简介
- 二、Gin 框架搭建
- 2.1 安装 Gin
- 2.2 简单示例
- 三、路由和处理函数
- 3.1 什么是路由?
- 3.2 Gin 框架中的路由及案例
- 3.3 实际开发中的应用及优缺点
- 四、请求和响应处理
- 4.1 接受参数
- 4.2 处理请求
- 4.3 生成响应
- 五、中间件
- 5.1 什么是中间件(Middleware)?
- 5.2 中间件的作用及案例
- 5.2.1 全局中间件
- 5.2.2 局部中间件
- 5.3 中间件的优缺点
- 六、错误处理
- 6.1 上下文存储异常信息
- 6.2 获取异常信息
- 七、总结
前言
按照公司目前的任务,go 学习是必经之路了,虽然行业卷,不过技多不压身,依旧努力!!!
说起微服务框架,首当其冲的就是 JAVA 技术栈的 Spring Cloud 全家桶,吃过的人都说好,比较 Spring Cloud 已经可以说是完全能满足互联网后端开发需求,用 Spring Cloud 来搭建一个三高(高并发、高可用、高性能)的后端架构并非难事。
Spring Cloud:Zuul(路由)、Gateway(路由)、Eureka(注册中心)、Hystrix(熔断限流)、Config(配置中心)、Bus(事件,消息总线)、Sleuth(日志收集,链路追踪)、Ribbon(负载均衡)、OpenFeign(API 调用) 等。
Spring Cloud Alibaba:Nacos(注册配置中心)、Sentinel(限流降级)、Seata(分布式事务)、Stream(分布式消息)等。
那 Go 语言自然也会包容微服务的思想,Gin 就是 Go 语言中的一个微服务框架,是一个轻量级的、高性能的 Web 框架,为专门构建快速的 Web 应用和 API 而设计。
一、Gin 简介
- Gin 是一个轻量级的、高性能的 Web 框架,专门为构建快速的 Web 应用和 API 而设计。它是基于 Go 语言的标准库,提供了简单易用的 API 和许多有用的功能,开发者可以快速地构建和部署 Web 服务。
- 强大的路由和中间件支持,Gin 提供了灵活的路由定义和中间件机制,可以方便的处理各种负责的逻辑。
- 适用于构建 RESTful API,Gin 提供了 JSON 构建和解析响应功能,非常适合构建 RESTful 风格 API。
当然也有不足之处:
- 功能相对简化,相比一些成熟的 Web 框架,Gin 在一些方面较为简化,如模板渲染等。
- 社区相对较小,虽然 Gin 在国内拥有一定的用户群体,但是相对 Spring 等其他 Web 框架,社区还是相对较小的。
二、Gin 框架搭建
在使用 Gin 框架之前,首先需要安装 Gin 包,就类似 JAVA 中如果要用到 web 模块就需要映入 starter-web 模块。
2.1 安装 Gin
通过如下命令来安装:
go get github.com/gin-gonic/gin
2.2 简单示例
下面是一个基于 Gin 写的一个 简单的 API 示例。
示例中,首先导入 “github.com/gin-gonic/gin” 包,并使用 gin.Default() 来创建一个默认的 Gin 引擎 router。然后使用 router 来定义了一个 路由,使用匿名函数来处理响应,返回了一个 Hello Gin 的相应字符串。最后使用 router.Run() 启动这个 HTTP 服务。
package mainimport ("net/http""github.com/gin-gonic/gin"
)func main() {// 创建一个 Gin 引擎router := gin.Default()// 定义路由和处理函数,这里使用匿名函数router.GET("/", func(ctx *gin.Context) {ctx.String(http.StatusOK, "Hello Gin!!!")})// 启动 HTTP 服务器router.Run("127.0.0.1:8080")
}
我们使用 go run hello.go 来运行,浏览器访问测试:
三、路由和处理函数
3.1 什么是路由?
路由是指根据客户端请求的 URL 路径,将请求映射到相应的处理函数上,这里对比 SpringMVC 框架的 DispatcherServlet 控制中心。在 Web 应用中,当用户访问不同的 URL 路径时,应用程序就需要调用相对于的处理函数来处理用户的请求,每个请求的处理函数都有不同的业务逻辑,
路由就会帮你将不同的请求分配到正确的处理函数上
。
在 Gin 框架中,你可以使用不同的 HTTP 方法(GET, POST, PUT, DELETE等)来定义不同的路由,并且为每一个路由指定一个处理函数。
3.2 Gin 框架中的路由及案例
Gin 框架中采用的路由是基于 httprouter 做的。
下面来看一个小小的示例:
下面示例中,通过 Gin 框架定义了 /hello GET 接口和 /add POST 接口,并且从请求体中获取 name 参数。
我来来看,一个包中,我们可以定义不同路由已完成不同的请求操作。
package mainimport ("fmt""net/http""github.com/gin-gonic/gin"
)func main() {// 创建一个 Gin 引擎router := gin.Default()// 定义路由和处理函数,这里使用匿名函数router.GET("/hello", func(ctx *gin.Context) {// 从请求体中查询 name 参数name := ctx.Query("name")fmt.Printf("接收到参数:%s", name)ctx.String(http.StatusOK, "Hello Gin! %s", name)})router.POST("/add", func(ctx *gin.Context) {var requestData struct {Name string "json:name binding:required"}// 使用 ShouldBindJSON 方法将请求体中的 JSON 数据绑定到结构体中if err := ctx.ShouldBindJSON(&requestData); err != nil {ctx.JSON(http.StatusBadRequest, gin.H{"error:": err.Error()})return}// 从结构体中获取参数name := requestData.Nameif len(name) == 0 {fmt.Println("获取name参数失败!")ctx.String(http.StatusBadRequest, "add fail!")} else {fmt.Printf("添加操作:%+v 完成\n", name)ctx.String(http.StatusOK, "%s add successful!", name)}})// 启动 HTTP 服务器router.Run("127.0.0.1:8080")
}
我们 Postman 来请求测试:
/hello GET 接口:
/add POST 请求:
3.3 实际开发中的应用及优缺点
在实际开发中,路由和函数处理运用非常广泛,以下是一些例子:
- 页面访问:给页面提供不同的 API 接口以供页面获取数据渲染,当用户访问不同的页面或资源时,通过 URL 统一资源定位符 来调用不同的路由获取资源。
- API 路由:除了前端页面,其他客户端比如:小程序、APP、给第三方系统提供接口等。
优点:
- 组织结构清晰:路由可以将不同的请求和处理函数组织起来,以便代码更加清晰。
- 模块化开发:可以实现模块化开发,每个处理函数对应每个路由处理不同的业务请求。
- 灵活性:路由可以通过不同的 URL 路径调用处理不同的处理函数,实现灵活处理请求。
缺点:
- 路由管理:如果规模庞大,路由管理可能变的复杂,需要合理地组织维护。
- 路由冲突:在定义路由时,如果定义了相同的路由,可能会导致路由冲突。
- 可读性:如果路由过多或者命名不清晰,可能会降低代码的可读性。
四、请求和响应处理
在 Gin 框架中,请求和响应处理是开发 Web 应用的核心部分,也是程序猿日常做的最多的事情。
每开发一个API,逃不过这几步骤:
4.1 接受参数
- 获取 Request Header 参数
token := ctx.GetHeader("token")
- 获取 Request Path 参数
id := ctx.Param("id")
- 获取 Request Param 参数
name := ctx.Query("name")
- 获取 Request Body 参数
在获取 RequestBody 参数是,我们需要封装结构体,然后绑定请求体到结构体中。
var requestData struct {Name string "json:name binding:required"
}
// 使用 ShouldBindJSON 方法将请求体中的 JSON 数据绑定到结构体中
if err := ctx.ShouldBindJSON(&requestData); err != nil {ctx.JSON(http.StatusBadRequest, gin.H{"error:": err.Error()})return
}
// 从结构体中获取参数
name := requestData.Name
4.2 处理请求
在路由处理函数中,你可以执行你想要的处理逻辑。例如,你可以查询数据库、调用其他函数、处理数据等等。处理请求的逻辑在路由处理函数中实现,声明处理函数或者使用匿名函数来处理。
4.3 生成响应
目前大多数的 Web 应用开发或者是系统之间相互调用、传参、接受数据等大多都是 JSON 格式数据,那在 SpringMVC 框架中有做封装,那在 Gin 框架中也是如此。
下面的案例中,我们通过
ctx.JSON()
方法向前端以 json 格式把数据返回,你可以将响应的数据封装成一个gin.H(类似 map)
对象,然后使用ctx.JSON
方法将其作为响应的主体返回给客户端。
注意
: 这里碰到一个小坑,忘记了 Go 语法中,小写是没有导出权限的,所以导致 postman 访问,data 中总是 [] 的,询问 大佬(chatGPT)才知道其中之奥妙,需大写。
package mainimport ("net/http""github.com/gin-gonic/gin"
)func main() {// 创建一个 Gin 引擎router := gin.Default()// 创建一个 Get 请求router.GET("/test/:id", func(ctx *gin.Context) {id := ctx.Param("id")name := ctx.Query("name")data := struct {// 注意,这里定义字段一定要大写,要不然则不能导出,返回空Id interface{} `json:"id"`Name string `json:"name"`}{Id: id,Name: name,}ctx.JSON(http.StatusOK, gin.H{"code": 200, "message": "请求成功", "data": data})})// 启动服务router.Run(":8080")
}
在 postma 中测试
调用:http://127.0.01:8080/test/10?name=phen
响应:
{"code": 200,"data": {"id": "10","name": "phen"},"message": "请求成功"
}
当然,如果需要返回其他类型的响应,比如 HTML页面、纯文本等,可以使用
ctx.HTML
、ctx.String
等方法,根据需要,设置适当的状态码和响应头。
五、中间件
5.1 什么是中间件(Middleware)?
- 中间件(
Middleware
)是一种常见的软件设计模式,在 Web 开发中用于请求和响应之间添加自定义逻辑。 - 中间件可以在请求到达处理函数之前或者在响应客户端之前,对请求和响应做处理。
- 在 Gin 框架中,中间件是一种非常重要的概念,用于处理一些通用的操作。
- 如果知道
SpringMVC
框架的话,就会知道Filter 过滤器
,Filter 有 Pre-proces 和 Post-process 操作,Gin 框架的中间件和 Filter 差不多。
5.2 中间件的作用及案例
中间件的作用对于请求和响应的进行预处理、后处理和记录,以便实现如下功能(不限于):
- 身份验证和授权:中间件中进行用户身份验证和授权,确保用户能够访问特定资源。
- 日志记录:可以在中间件中记录 PV、用户操作记录等便于监控和排错。
- 请求参数验证:在中间件中可以进行参数验证、XSS 攻击过滤等,确保用户提交的订单数据合法。
- 缓存和性能优化:可以在中间件中进行热数据的缓存,减少后端数据库的压力。
- 防爬机制:可以在中间件中对请求参数等进行校验,以防别人通过接口爬数据。
- 请求耗时统计:中间件中记录请求耗时,以便性能分析。
如何使用中间件呢:
5.2.1 全局中间件
在上面的案例中加入了中间件的使用,在下面的案例中,定义了一个
Logger()
函数来记录请求日志,在 Logger() 函数中,首先记录请求请求进入时间,调用Next()
函数 继续处理请求 最后记录结束记录时间和耗时。
package mainimport ("fmt""log""net/http""time""github.com/gin-gonic/gin"
)// 定义中间件处理函数,用户记录日志
func Logger() gin.HandlerFunc {// 使用自定义函数来处理return func(ctx *gin.Context) {fmt.Println("Middleware Logger Handler...")start := time.Now()// 调用 Next 继续处理请求ctx.Next()end := time.Now()latency := end.Sub(start)log.Printf("[%s] 请求方法:%s 请求路径:%s 耗时:%v", end.Format("2006-01-02 15:04:05"), ctx.Request.Method, ctx.Request.URL.Path, latency)}
}func main() {// 创建一个 Gin 引擎router := gin.Default()// 添加自定义的 Logger 中间件router.Use(Logger())// 定义路由和处理函数,这里使用匿名函数router.GET("/hello", func(ctx *gin.Context) {// 从请求体中查询 name 参数name := ctx.Query("name")fmt.Printf("请求处理...接收到参数:%s \n", name)ctx.String(http.StatusOK, "Hello Gin! %s", name)})router.POST("/add", func(ctx *gin.Context) {var requestData struct {Name string "json:name binding:required"}// 使用 ShouldBindJSON 方法将请求体中的 JSON 数据绑定到结构体中if err := ctx.ShouldBindJSON(&requestData); err != nil {ctx.JSON(http.StatusBadRequest, gin.H{"error:": err.Error()})return}// 从结构体中获取参数name := requestData.Nameif len(name) == 0 {fmt.Println("获取name参数失败!")ctx.String(http.StatusBadRequest, "add fail!")} else {fmt.Printf("添加操作:%+v 完成\n", name)ctx.String(http.StatusOK, "%s add successful!", name)}})// 启动 HTTP 服务器router.Run("127.0.0.1:8080")
}
通过 Postman 请求测试:
从上面的截图中可以看处,每次请求都调用到了中间件 Logger()。
5.2.2 局部中间件
上面是全局中间件,所有请求都会进入中间件,但是有时候并非所有请求都需要中间件处理,这时候就需要某几个特殊的 API 接口实现中间件即可。
package mainimport ("errors""fmt""log""net/http""github.com/gin-gonic/gin"
)// 定义局部中间件函数,检查用户权限
func checkAuth() gin.HandlerFunc {return func(ctx *gin.Context) {token := ctx.Query("token")if len(token) == 0 {ctx.Error(errors.New("无权访问"))ctx.JSON(http.StatusForbidden, gin.H{"code": 403, "message": "无权访问"})} else {log.Printf("请求方法:%s 请求路径:%s 正常访问", ctx.Request.Method, ctx.Request.URL.Path)}}
}func main() {// 创建一个 Gin 引擎router := gin.Default()router.GET("/list", checkAuth(), func(ctx *gin.Context) {errors := ctx.Errorsif errors != nil {ctx.Errors = nilreturn}data := struct {// 注意,这里定义字段一定要大写,要不然则不能导出,返回空Id interface{} `json:"id"`Name string `json:"name"`}{Id: 1,Name: "phen",}fmt.Println("data: ", data)ctx.JSON(http.StatusOK, gin.H{"code": 200, "message": "请求成功", "data": data})})// 启动 HTTP 服务器router.Run("127.0.0.1:8080")
}
通过浏览器访问测试:
访问成功:
无权访问:
从两张截图中可以看出,有 token 则访问成功,没 token 则人访问失败!
注意、注意、注意:
这里遇到一个问题,第一次测试是我的代码中没有 ctx.Errors = nil
这句代码,就发现如下情况:
- 第一次访问不带 token 参数,自然无权访问
- 第二次访问带上 token, 没有无权访问,但是也没有访问成功结果
- 为啥呢?chatGPT 告诉我
因为 Gin 框架上下文在一个请求中是共享的,所以在同一个请求的不同处理函数中,上下文中的数据和异常信息是可以共享的。
这种设计有助于在请求的不同处理阶段中传递数据和异常信息,但也需要小心处理,以确保不会导致意外的结果。如果你想要在每次请求中使用一个干净的上下文,可以在每个请求处理函数的开头重新初始化上下文,或者在处理函数之间隔离上下文。
如果你不希望在不同请求之间共享异常信息,你可以在每次请求开始时,将上下文清空或初始化,以确保上下文不会受到之前请求的影响。
所以我加入了这句代码 ctx.Errors = nil
,每次访问过之后清楚调异常信息,则每次访问的时候,带 token 则访问成功,不带则无权访问,这样的处理对于新手的我来说应该不是很合理,后面找大佬咨询,或者哪位大佬看到了,评论区指导一下
。
5.3 中间件的优缺点
有点:
- 代码复用:中间件可以通过将公共的代码逻辑提取出来,进行封装,实现代码的复用,避免相似逻辑和重复代码。
- 逻辑解耦:中间件可以将请求和处理逻辑解耦,是的代码更加清晰、模块化和易于维护。
- 灵活性:中间件可以在不同的路由器上使用,在上面的案例中,/hello GET 和 /add POST 路由都使用了 Logger() 中间件。
缺点:
- 复杂性:通过中间件处理,可能会增加业务的复杂性,不当使用会导致业务代码难以理解。
- 性能影响:每次请求都需要经过中间件,这样中间件中逻辑如果过于复杂,可能影响请求API性能。
六、错误处理
Gin 框架中提供了一种简便的方式来处理函数中发生的异常,确保错误能够捕获并且正确的返回给前端。
6.1 上下文存储异常信息
在下面的案例中,定义了一个 failFunc() 函数,此函数的作用就是返回一个异常,当在 Get() 方法中获取到的 err 时,则用
ctx.Error(err)
将错误信息存储在 Gin 上下文中。这样,Gin 框架会在请求处理完毕后,检查是否有存储的异常信息,如果有则把错误信息返回给前端。
package mainimport ("errors""net/http""github.com/gin-gonic/gin"
)func failFunc() error {return errors.New("运行异常")
}func main() {// 创建一个 Gin 引擎router := gin.Default()// 创建一个 Get 请求router.GET("/test/:id", func(ctx *gin.Context) {err := failFunc()if err != nil {// 将错误信息存储在 Gin 上下文中ctx.Error(err)ctx.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "服务器异常"})return}ctx.JSON(http.StatusOK, gin.H{"code": 200, "message": "请求成功"})})// 启动服务router.Run("127.0.0.1:8080")
}
通过 postman 调用测试:
请求:http://127.0.01:8080/test/10?name=phen
响应:
{"code": 500,"message": "服务器异常"
}
不过我发现,如果在出现异常是,也就是 err 不为空是,里面的 return
不要,则后面所有的 ctx.JSON、ctx.String等,都会成功放回,所以请求结果会如下:
请求:http://127.0.01:8080/test/10?name=phen
响应:
{"code": 500,"message": "服务器异常"
}{"code": 200,"message": "请求成功"
}请求结束
这一点不会像 Spring框架中,response 过之后,如果再 response 则会报错。
总之,Gin 框架通过在 Gin 上下文中存储错误信息的方式,提供了方法处理错误的机制,使得处理函数中的错误被捕获并且能够返回给前端。
这样有助于用户体验和调试信息。
6.2 获取异常信息
而也可以通过
ctx.Errors
获取到所有运行中的异常信息
fmt.Println("运行时的所有异常:", ctx.Errors)
只要在 return 前通过 Errors() 方法获取存储在 Gin 中的异常信息,就可以拿到所有的运行过程中的异常,以便业务逻辑的判断和处理。
七、总结
什么是微框架(Gin),Gin 框架是一个轻量级的,高性能的 Web 框架,专门为构架快速的 Web 应用和 API 而设计。它是基于 Go 语言的标准库开发,提供简单易用的 API 和其他更多的功能,开发者可以快速的构建出 Web 应用并且部署。
通过 go get github.com/gin-gonic/gin 来安装 Gin 框架到开发者环境,通过 框架提供的 路由和处理函数来快速开发 API,接受参数通过函数处理业务逻辑,然后通过 Gin 框架提供的 JSON、String、HTML等方法返回给前端不同类型数据。
通过中间件来处理公共的业务逻辑,比如认证授权、日志记录等。
也有异常处理机制,Gin 通过上下文存储异常信息,然后把异常信息返回给前端来实现异常的处理机制,提高用户体验和调试信息。
最后,Gin 框架总算是有些入门了,后面继续学习框架其他内容,然后博客分享。
现阶段还是对 Go 语言的学习阶段,想必有一些地方考虑的不全面,本文示例全部是亲自手敲代码并且执行通过。
如有问题,还请指教。
评论去告诉我哦!!!一起学习一起进步!!!
相关文章:

GO学习之 微框架(Gin)
GO系列 1、GO学习之Hello World 2、GO学习之入门语法 3、GO学习之切片操作 4、GO学习之 Map 操作 5、GO学习之 结构体 操作 6、GO学习之 通道(Channel) 7、GO学习之 多线程(goroutine) 8、GO学习之 函数(Function) 9、GO学习之 接口(Interface) 10、GO学习之 网络通信(Net/Htt…...

C语言 字符指针
1、介绍 概念: 字符指针,就是字符类型的指针,同整型指针,指针指向的元素表示整型一样,字符指针指向的元素表示的是字符。 假设: char ch a;char * pc &ch; pc 就是字符指针变量,字符指…...
Springboot所有的依赖
<properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><!-- 声明springboot的版本号 -->…...

Flutter BottomSheet 三段式拖拽
BottomSheetBehavior 追踪 BottomSheet系统默认实现效果准备要实现的功能点:定义三段式状态:BottomSheetBehavoir阀值定义1. 未达到滚动阀值,恢复状态2. 达到滚动阀值,更新状态 前面倒是有讲过Android原生的BottomSheetBehavior&a…...

php后端实现调用高德地图进行POI搜索
对于当前位置或者选定省市位置进行查询 接口实现 /*** 查询地址* ApiTitle (查询地址)* ApiSummary (查询地址)* ApiMethod (POST)* ApiRoute (/api/demo/address)* ApiParams (name"dart", type"integer", requiredtrue, description"省…...

uniapp 实现滑动视图切换 顶部滚动导航栏
无论小程序的时候一般有这个功能,在页面处于首页时候,滑动视图,切换视图顶部滚动导航也跟着切换 1.想要实现这个功能就需要实现顶部导航栏,首先实现顶部滚导航栏 点击高亮颜色显示 模板代码 <scroll-view scroll-x"true" class"scroll-content" > …...
ArcGIS API for JavaScript 调用自定义地图模板总结
ArcGIS API for JavaScript 调用自定义地图模板总结 3.9版本4.24版本 3.9版本 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>Hello World</title><link rel"stylesheet" href&qu…...

QGraphicsView实现简易地图5『经纬网格』
前文链接:QGraphicsView实现简易地图4『局部加载-地图漫游』 由于GCJ02 Web 墨卡托投影 纬度并不随像素等分,且两极跨度较大,因此本次演示采用的经纬网等分逻辑为等分像素。同等像素跨度之间,两级纬度变化较小,越靠近赤…...

RestTemplate 请求转发异常 ERR_CONTENT_DECODING_FAILED 200 (OK)
#1 问题描述 在基于Spring Boot的项目中实现了请求转发(使用 RestTemplate 的 exchange 方法)的功能,忽然在前端报net::ERR_CONTENT_DECODING_FAILED 200 (OK)的错误,后端及上游系统日志均显示请求已完成。 #2 原因探寻 上述错…...
用python实现一个异或计算器
有这样一条需求:计算某个文件中的数组每一行元素的最后一个参数,异或输出。 因为元素比较多,十几行,通过人工去计算异或值非常困难。 而在线异或的计算器,也需要人为输入这些数值,每次计算一个最终结果需…...
Sketch打不开AI文件?转换方法在这里
1、对比设计软件 Sketch 与 AI 软件功能 Sketch 与 Illustrator 都是行业内优秀的矢量图形设计软件,各有千秋。Sketch 从 2010 年面世,专注 APP 界面设计,深受初学者与专业人士喜爱。Illustrator 拥有更悠久的历史,是处理复杂图标…...

小游戏扫雷实现教学(详解)
目录 【前言】 一、模块化程序设计(多文件编程)介绍 1.概述 2.传统编程的方式 3.模块化程序设计的方法 二、扫雷代码设计思路 三、扫雷代码设计 1.创建菜单函数 2.实现9x9扫雷 3.初始化棋盘 4.打印棋盘 5.随机布置雷的位置 6.排查雷的信息 7.回…...

04 mysql innodb record
前言 最近看到了 何登成 大佬的 "深入MySQL源码 -- Step By Step" 的 pdf 呵呵 似乎是找到了一些 方向 之前对于 mysql 方面的东西, 更多的仅仅是简单的使用[业务中的各种增删改查], 以及一些面试题的背诵 这里会参照 MySQL Internals Manual 来大致的看一下 i…...
Centos7安装Docker
0.安装Docker Docker 分为 CE 和 EE 两大版本。CE 即社区版(免费,支持周期 7 个月),EE 即企业版,强调安全,付费使用,支持周期 24 个月。 Docker CE 分为 stable test 和 nightly 三个更新频道…...

Vue中如何更好地封装组件?
子组件接受父组件传递的事件 1.子组件使用事件名"$emit(父组件中传递的事件名,想给父组件传递的参数(可选))" click"$emit(click)" 2.子组件使用 v-on"$listeners" 父组件: <template><div id"app"><myCo…...

C语言的链表的相关操作
本变博客源于自己想复习一下C语言,所以便自己动手复习了一下链表的相关操作。做个人记录使用。 main.c #include <stdio.h> #include "list.h"int main() {student *a;printf("hello world\n") ;printf("----初始化列表----------\…...
Python3中typing模块
Python类型注解是Python 3.5版本之后引入的新特性,它可以让开发者在函数、变量等声明时为其指定类型。typing模型能够声明类型,防止运行时出现参数和返回值类型不符合的问题。 ### 1. 基本类型注解 def hello(name: str) -> str:return (Hello, na…...
C语言自动抓取淘宝商品详情网页数据,实现轻松高效爬虫
你是否曾经遇到过需要大量获取网页上的数据,但手动复制粘贴又太过费时费力?那么这篇文章就是为你而写。今天我们将会详细讨论如何使用C语言实现自动抓取网页上的数据。本文将会从以下8个方面进行逐步分析讨论。 1. HTTP协议的基本原理 在开始之前&…...

数据结构---跳表
目录标题 为什么会有跳表跳表的原理跳表的模拟实现准备工作find函数insert函数erase函数 测试效率比较 为什么会有跳表 在前面的学习过程中我们学习过链表这个容器,这个容器在头部和尾部插入数据的时间复杂度为O(1),但是该容器存在一个缺陷就是不管数据…...
为什么Tomcat的NIO在读取body时要模拟阻塞?
文章首发地址 Tomcat的NIO完全可以以非阻塞方式处理IO,为什么在读取body部分时要模拟阻塞呢?在Tomcat的NIO读取HTTP请求时,为了保证请求的正确性和可靠性,需要模拟阻塞模式,这是因为servlet规范里定义了ServletInputSt…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...

业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
32单片机——基本定时器
STM32F103有众多的定时器,其中包括2个基本定时器(TIM6和TIM7)、4个通用定时器(TIM2~TIM5)、2个高级控制定时器(TIM1和TIM8),这些定时器彼此完全独立,不共享任何资源 1、定…...

基于开源AI智能名片链动2 + 1模式S2B2C商城小程序的沉浸式体验营销研究
摘要:在消费市场竞争日益激烈的当下,传统体验营销方式存在诸多局限。本文聚焦开源AI智能名片链动2 1模式S2B2C商城小程序,探讨其在沉浸式体验营销中的应用。通过对比传统品鉴、工厂参观等初级体验方式,分析沉浸式体验的优势与价值…...

工厂方法模式和抽象工厂方法模式的battle
1.案例直接上手 在这个案例里面,我们会实现这个普通的工厂方法,并且对比这个普通工厂方法和我们直接创建对象的差别在哪里,为什么需要一个工厂: 下面的这个是我们的这个案例里面涉及到的接口和对应的实现类: 两个发…...