wordpress simple济a/网站外链优化方法
【Gin】深度解析:在Gin框架中优化应用程序流程的责任链设计模式(下)
大家好 我是寸铁👊
【Gin】深度解析:在Gin框架中优化应用程序流程的责任链设计模式(下)✨
喜欢的小伙伴可以点点关注 💝
前言
本次文章分为上下两部分,上部分为对理论的介绍,下部分为具体的底层代码深度剖析和编程实践,感兴趣的伙伴不要错过哦~
责任链设计模式作为一种经典的行为设计模式,在现代软件开发中扮演着重要角色。特别是在高效的Web应用开发中,如Gin框架这样的轻量级Go语言Web框架,合理地应用责任链模式可以显著提升代码的可扩展性和灵活性。本文将深入探讨在Gin框架中责任链模式的实现原理、优化策略以及实际应用场景。
责任链模式通过将请求的发送者和接收者解耦,将多个对象连成一条链,并在链上传递请求,直到有对象处理该请求为止。在Gin
框架中,利用责任链模式可以有效地处理请求的流程控制、中间件管理和异常处理,使得代码结构更加清晰和可维护。本文将探索如何在Gin框架中设计和优化责任链模式,以提升应用程序的性能和可维护性。
关键的类图和时序图
(1)类图
责任链模式包含以下几个主要角色:
Handler
(抽象处理者):
声明一个处理请求的接口,通常包含一个指向下一个处理者的引用。
MiddlerWare
(具体处理者):
Gin的中间件实现抽象处理者的处理方法,并根据自身的处理能力决定是否处理请求,如果不能处理则将请求传递给下一个处理者。
Client
(客户端):
创建一个具体处理者对象的链,并向链上的第一个处理者对象发送请求。
图 28 责任链模式类图
由类图28可得:先声明一个处理请求的接口
Handler
,通常聚合一个指向下一个处理者的引用successor
。声明处理请求的方法handlerRequest()
,由具体的中间件责任方MiddleWare
实现。接着Gin的中间件实现抽象处理者的处理方法,调用handlerRequest()
方法根据自身的处理能力决定是否处理请求,如果不能处理则将请求传递给下一个处理者nextHandler
。客户端创建一个具体处理者对象的链,并向链上的第一个处理者对象发送请求。
(2)时序图
图 29 责任链模式时序图
由上图29可得:客户端发送请求:客户端调用具体处理者链的第一个处理者的
handleRequest()
方法,将请求request
传递给链的起始点MiddleWare1
。
责任链中的处理者Midlleware
处理请求:每个具体中间件处理者Midlleware
收到请求后,根据自己的处理能力决定是否处理请求。
请求传递到合适的处理者:如果当前处理者能够处理请求,则处理完成;如果不能处理,则将请求传递给链中的下一个处理者Midlleware
。
链的末端处理:请求request
会沿着链依次传递,直到被处理或者到达链的末端。如果末端处理者能够处理请求processing Request,则处理完成后,沿着责任链返回处理完后的响应信息
response`。如果整个链上的处理者都不能处理请求,则请求最终未被处理。
主程序的流程
由下图30可知:一开始,
Client
客户端构建责任链后,开始发起请求,责任链的中间件MiddleWare1
开始接收请求,然后处理请求,处理完毕后,返回响应给上层调用者,如果客户端接收到响应后,则程序退出。
接着如果责任链的中间件MiddleWare1
无法处理请求,则将请求传递给下一层中间件MiddleWare2
,MiddleWare2
处理请求,处理完毕后,返回响应给上层调用者,再由上层调用者沿责任链继续将响应信息返回给上层调用者,直至Client
客户端。
如果责任链的中间件MiddleWare2
无法处理请求,则将请求传递给下一层中间件MiddleWare……
,MiddleWare……
处理请求,处理完毕后,返回响应给上层调用者,再由上层调用者沿责任链继续将响应信息返回给上层调用者,直至Client
客户端。
如果责任链的中间件都无法处理请求,则将请求传递给最后一层中间件FinalMiddleWare
处理请求,处理完毕后,返回响应给上层调用者,再由上层调用者沿责任链继续将响应信息返回给上层调用者,直至Client
客户端。如果最后一层中间件FinalMiddleWare
都无法处理请求,则责任链程序结束退出。
图 30 责任链模式主程序流程图
程序模块之间的调用关系
下图为程序各模块之间的调用关系:
图 31 责任链模式程序模块调用图
由上图31可知,责任链模式主要有如下3个角色:
(1) IRoutes
(处理者):
定义处理请求的接口,通常包含一个处理方法(handle
),其具体实现方(ConcreteHandler
)实现处理请求的方式。每个处理者对象中通常会包含一个指向下一个处理者的引用(后继者)。
(2) CombineHandlers
(具体处理者):
实现 IRoutes
接口,处理请求的具体逻辑。如果自己能够处理请求,则处理;否则将请求传递给下一个处理者。当然,也可以设置拦截请求,将请求只在本层进行处理,不传递给下一层责任方进行处理。
(3) Client
(客户端):
负责创建和提交请求对象。按照指定的顺序构建责任链,可以设置拦截器,用于拦截请求,最后将请求发送到链的起始点。
下面是对上图各层次调用关系的描述:
客户端调用
r.GET
进行按照指定的顺序构建责任链,GET
方法实现IRoutes
接口,GET
方法将构建路由组对象的请求转发给真正的构建路由组对象的handle方法,该handle
方法实现IRoutes
接口。handle方法进一步转发构建GET
中指定顺序的中间件责任链请求给具体处理者CombineHandlers
,具体处理者CombineHandlers
负责按照客户端指定的顺序构建中间件责任链。责任链中的每一个处理者存在Next()
和Abort()
方法,调用Next()
方法遍历责任链中的请求者对象,依次将请求传递给链中的具体处理者对象业务图见下图32。Abort()
方法设置索引为超出合法范围的值,使得不将请求转发给下一个处理者,实现拦截效果,业务图见下图33。构建完后返回给上层的调用者Handler
,Handler
将路由组对象组装好后,再将组装好的路由组对象返回给GET
方法,GET
方法返回嵌入责任处理链的路由组对象给客户端(责任链的起始点)进行调用,客户端拿到对象后,做下一步的处理。
图 32 责任链Next处理业务图
图 33 责任链Abort拦截业务图
在上图的基础上,下面对各个模块的代码进行深入剖析:
图 34 责任链客户端代码
gin.Default()
创建了一个默认的 Gin 路由引擎实例 r。
r.GET("", middleWare1, middleWare2, Home)
定义了一个 GET 请求的路由,其中middleWare1
和middleWare2
是中间件函数,用于在请求到达最终处理函数Home
之前执行预处理或者其他操作。
r.Run(":8080")
启动了 HTTP 服务器,监听在 8080 端口上。
图 35 客户端指定处理请求的中间件代码
函数签名和参数:
func middleWare1(c *gin.Context)
:这是一个函数签名,接收一个*gin.Context
类型的参数c
,用于处理 HTTP 请求和响应。
处理逻辑:fmt.Println("M1 请求部分")
:这行代码输出 “M1 请求部分”,表示这是中间件处理请求的部分,可以在这里执行一些预处理逻辑,如日志记录、权限检查等。
c.Next()
c.Next()
是 Gin 框架中用于将控制传递给链中的下一个处理程序的方法。在这里,它表示将控制权传递给下一个注册的中间件或路由处理函数。fmt.Println(“M1 响应部分”):这行代码输出 “M1 响应部分”,表示中间件处理请求后的响应部分,可以在这里执行一些后续处理逻辑,如记录响应时间、清理资源等。
注释的代码c.Abort()
:
//c.Abort():这是一个被注释掉的代码片段。在 Gin 框架中,如果调用 c.Abort(),它将会中止当前请求链的执行,不会再继续执行后续的中间件或路由处理函数。如果取消注释,将会导致请求处理过程被中止,不再执行后续的处理函数。
执行流程:
当有一个
HTTP
请求到达与该中间件关联的路由时,这段代码的执行流程如下:当请求进入时,中间件输出 “M1 请求部分”,执行一些请求前的逻辑。
c.Next()
调用将控制权传递给下一个中间件或路由处理函数。如果注释掉的c.Abort()
被取消注释,则请求的处理将在此中间件结束,不再继续向下执行。
如果没有调用c.Abort()
或者注释掉了,请求将继续执行下一个中间件或者最终的路由处理函数。当控制返回给该中间件时(表示后续处理完成或者中间件链中断),输出 “M1 响应部分”,执行一些请求后的逻辑。
图 36 IRoutes接口
代码位置:RouterGroup.go的33-51行
方法解析:
Use
Use(...HandlerFunc) IRoutes
作用:注册一个或多个中间件函数,这些中间件函数会在后续的路由处理中被调用。返回值:返回 IRoutes 接口本身,以支持链式调用。
HTTP 方法相关路由
这些方法 (Handle, Any, GET, POST, DELETE, PATCH, PUT, OPTIONS, HEAD
) 都接受路由路径作为第一个参数,后跟一个或多个 HandlerFunc,表示路由处理函数。每个方法都允许注册对应 HTTP 方法的路由处理器。
返回值:同样返回 IRoutes 接口,支持链式调用,以便在代码中可以连续调用多个路由注册方法。
Match
Match([]string, string, ...HandlerFunc) IRoutes
参数:第一个参数是 HTTP 方法的列表,第二个参数是路由路径模式,后跟一个或多个HandlerFunc
。
作用:注册一个支持多种 HTTP 方法的路由处理器。
返回值:返回IRoutes
接口。
静态文件服务相关方法:
StaticFile(string, string) IRoutes
:注册单个静态文件的路由处理器。
StaticFileFS(string, string, http.FileSystem) IRoutes
:注册单个静态文件的路由处理器,并指定文件系统。
Static(string, string) IRoutes
:注册指定路径下所有文件的静态文件服务。
StaticFS(string, http.FileSystem) IRoutes
:注册指定路径下所有文件的静态文件服务,并指定文件系统。
总结:
这个接口定义了一组方法,用于在一个路由处理器中注册路由和处理函数。通过IRoutes
接口,可以方便地添加中间件、处理各种HTTP
方法的请求,以及处理静态文件服务。这种设计使得路由的注册和处理能够保持清晰和模块化,符合常见的 Web 框架的路由管理模式。
图 37 GET方法代码
代码位置:routergroup.go的116-118行
函数签名:
GET(relativePath string, handlers ...HandlerFunc)
:这是一个方法定义,属于RouterGroup
类型的接收者group
。它接收一个相对路径relativePath
和一个或多个HandlerFunc
类型的处理函数作为参数。
IRoutes
是一个接口,用于表示路由集合或路由的操作。
功能说明:
GET
方法作为RouterGroup
的一个方法,是为了注册一个处理GET
请求的路由。
relativePath string
参数表示注册的路由的相对路径,如 “/”、“/users” 等。
handlers ...HandlerFunc
参数是一个变长参数,接收一个或多个 HandlerFunc 函数,用来处理请求。
方法调用:
roup.handle(http.MethodGet, relativePath, handlers)
:在 GET 方法内部,调用了group.handle
方法,将 HTTP 方法名 “GET”、相对路径relativePath
和handlers
函数传递给它。
http.MethodGet
是 Go 标准库中定义的常量,表示 HTTP GET 请求方法。
返回值:
return group.handle(...)
:GET 方法返回了group.handle(...)
的结果。通常情况下,这个方法会返回路由集合或者支持路由操作的接口,以便可以进一步链式调用其他路由相关的方法。
执行流程:
在 Gin 框架中,RouterGroup
类型的GET
方法是一个便捷方法,它内部调用了group.handle
方法来处理注册 GET 请求的路由。具体的处理流程如下:当调用 GET 方法注册路由时,实际上是通过 group.handle 方法进行注册。group.handle 方法会将 “GET”、relativePath
和handlers
作为参数传递给底层的路由处理器进行处理和注册。这样做的好处是可以通过不同的HTTP
方法(例如GET
,POST
,PUT
等)来注册不同的路由处理逻辑,同时保持了代码的简洁性和可读性。
图 38 具体处理者代码
代码位置:routergroup.go的86-91行
函数签名:
handle(httpMethod, relativePath string, handlers HandlersChain)
:这是一个方法定义,属于 RouterGroup 类型的接收者 group。它接收三个参数:
httpMethod
:表示 HTTP 请求方法,如"GET"
、"POST"
等。
relativePath
:表示路由的相对路径,例如 “/”、“/users” 等。
handlers HandlersChain
:是一个类型为 HandlersChain 的参数,表示一系列的处理函数链。
路径计算和处理器组合:
calculateAbsolutePath
方法确保生成正确的完整路径,考虑了路由组的前缀等因素。combineHandlers
方法可能用来将当前路由组的中间件与传入的处理函数链合并,确保请求能够按照正确的顺序执行。
路由注册:
addRoute
方法将最终确定的HTTP
方法、路径和处理函数链注册到Gin
框架的路由引擎中,以便后续能够根据请求的HTTP
方法和路径找到对应的处理函数。
返回值:
returnObj
方法返回当前路由组的某个接口或对象,用于可能的链式调用或其他路由相关操作。
这段代码展示了 Gin 框架中路由注册的核心逻辑。它负责计算路由的绝对路径,合并处理函数链,最终将路由信息注册到底层的路由引擎中。通过这种设计,框架能够支持灵活的路由定义和中间件处理,同时保证了性能和可扩展性。
图 39 具体处理者真正构建责任链代码
代码位置:routergroup.go的241-248行
函数签名:
combineHandlers(handlers HandlersChain) HandlersChain
:这是一个方法定义,属于RouterGroup
类型的接收者group
。它接收一个类型为HandlersChain
的参数 handlers,表示一系列的处理函数链。返回类型为HandlersChain
,即处理函数链。
参数解释:
handlers HandlersChain
:表示要合并到当前路由组 (group) 的处理函数链。HandlersChain
可能是一个类型为[]func(c *Context)
的切片,用于存储中间件和处理函数。
计算最终大小:
finalSize := len(group.Handlers) + len(handlers)
:计算合并后的处理函数链的总长度。group.Handlers
是当前路由组已有的处理函数链的长度,handlers
是传入的新的处理函数链的长度。
断言检查:
assert1(finalSize < int(abortIndex), "too many handlers")
:使用 assert1 函数来断言finalSize
必须小于abortIndex
,否则会输出 “too many handlers” 的错误信息。这是为了确保合并后的处理函数链不会超过某个预设的最大限制,避免潜在的内存溢出或其他问题。
创建合并后的处理函数链:
mergedHandlers := make(HandlersChain, finalSize)
:根据 finalSize 创建一个新的HandlersChain
,即mergedHandlers
,用于存储合并后的处理函数链。
复制处理函数:
copy(mergedHandlers, group.Handlers)
:将当前路由组 (group) 的已有处理函数链复制到mergedHandlers ``的开头部分。
copy(mergedHandlers[len(group.Handlers):], handlers):将传入的新处理函数链 handlers 复制到 mergedHandlers 的末尾部分,从 group.Handlers 的长度位置开始复制。
返回合并后的处理函数链:
return mergedHandlers
:返回合并后的 HandlersChain,即包含了当前路由组的处理函数链和传入的新处理函数链的完整链条。
处理函数链合并:
combineHandlers
方法用于将当前路由组的已有处理函数链与传入的新处理函数链进行合并,确保请求按照正确的顺序执行所有中间件和处理函数。
长度和断言检查:
在合并前,通过计算和断言来确保合并后的处理函数链不会过长,以保证系统的稳定性和性能。
返回值:
返回合并后的处理函数链,以便在路由注册时使用。
这段代码展示了 Gin 框架中如何处理路由组的处理函数链的合并逻辑。通过这种方式,框架能够支持在路由组中动态添加中间件和处理函数,保证了灵活性和可扩展性。
图 40 责任链的数据结构
代码位置:gin.go的54行
定义:定义了一个
HandlerFunc
请求处理者的切片,用于存储HandlerFunc
请求处理者,构建责任链。
图 41 请求处理者的定义
代码位置:gin.go的48行
定义:定义
HandlerFunc
类型,用于创建具体请求处理对象。
图 42 责任链访问下一个责任方Next()代码
代码位置:context.go的182-188行
方法说明:
c.index
是 Context 结构体中的一个字段,用于跟踪当前执行的中间件或处理函数的位置。
c.handlers
是一个存储HandlerFunc
的切片,这些函数是注册到当前路由处理器的中间件和处理函数。
c.index++
将 index 递增,以准备执行下一个中间件或处理函数。
for
循环用来遍历 handlers 中的函数,从 index 所指的位置开始执行,直到数组末尾或者中途某个函数决定中断执行。
执行流程:
调用Next()
方法会使 index 递增,从而将控制权交给下一个注册的中间件或处理函数。
每次循环,通过c.handlers[c.index](c)
调用 index 所指的函数,并将当前的Context
对象c
传递给它。
循环继续,直到index
超过了handlers
的长度或者某个中间件函数调用了Next()
以停止后续执行。
图43 定义拦截索引
代码位置:context.go的50-51行
abortIndex
是一个int8
类型的常量。
math.MaxInt8
是int8
类型能表示的最大整数,通常为127
。
>> 1
是位运算操作,表示将 math.MaxInt8 右移一位,即将其值除以 2,得到的结果约为 63(实际值取决于具体的整数大小和运算系统)。
这样设定的目的是使abortIndex
成为一个比较大的数值,足以确保c.index
大于等于abortIndex
后可以立即停止后续的处理函数调用。
图 44 请求处理者设置拦截器
代码位置:context.go的199-201行
代码解释:
c.index
是 Context
结构体中的一个字段,用于跟踪当前执行的中间件或处理函数的位置。
abortIndex
是一个常量或全局变量,用于表示中止处理流程的索引值。
Abort()
方法将 c.index
设置为 abortIndex
,这样在接下来调用 Next()
方法时,循环将直接结束,不再执行后续的处理函数或中间件。
图 45 判断请求处理者是否设置拦截
代码位置:context.go的191-193行
代码解释:
c.index
是Context
结构体中的一个字段,用于跟踪当前执行的中间件或处理函数的位置。
abortIndex
是一个常量或全局变量,用于表示中止(abort
)处理流程的索引值。
IsAborted()
方法通过比较c.index
是否大于或等于abortIndex
来判断当前处理流程是否已经被中止。
如果c.index
大于或等于abortIndex
,则返回true
,表示当前处理已被中止;否则返回false
,表示未被中止。
责任链模式案例及调试分析
责任链模式案例编写如下:
下面分析一下每个部分的功能和调用流程:
结构定义和接口:
图101 定义Handler接口
Handler
接口:
Handle(c *gin.Context)
方法用于处理请求。
SetNext(handler Handler)
方法用于设置下一个责任链节点。
图102 定义Middleware中间件
Middleware
结构体:实现了 Handler 接口。
图103 定义中间件1的Handle方法
Handle(c *gin.Context)
方法中,打印 “M1 接收请求”,然后调用下一个处理者(如果存在),最后打印 “M1 得到响应”。
图104 定义中间件2的Handle方法
Handle(c *gin.Context)
方法中,打印 “M2 接收请求”,然后调用下一个处理者(如果存在),最后打印 “M2 得到响应”。
图105 定义末端中间件的Handle方法
FinalHandler
结构体:作为最终的处理者,实现了 Handler 接口。
Handle(c *gin.Context)
方法中,打印 “FinalHandler 接收请求”,然后调用具体的业务逻辑函数 Home(c)
,最后打印 “FinalHandler 得到响应”。
图106 定义视图层代码
Home
视图层部分:
fmt.Println("Home Receiving……")
放在 Home 函数中的最开始,这样在请求到达时会立即打印 “Home Receiving……”。
c.String(200, "Home Receiving……")
在完成日志记录后立即向客户端发送 “Home Receiving……” 响应
责任链的构建和运行:
图107 客户端构建责任链
解读: 在
main
函数中:先创建了Gin
引擎实例r
。再实例化了Middleware1
、Middleware2
和FinalHandler
。之后使用SetNext
方法将它们串联起来,形成责任链:middleware1 -> middleware2 -> finalHandler。将middleware1.Handle
方法作为 Gin 路由处理函数注册到了根路径 “”,这意味着当收到 GET 请求时,责任链会依次处理该请求。最后通过r.Run(":8080")
启动 Gin 服务器,监听在 8080 端口上。
小结:责任链模式案例展示了如何使用 Go 和 Gin 框架构建一个简单的责任链,用于处理 HTTP 请求。每个中间件和最终处理者都负责一部分逻辑,并通过 SetNext 方法连接成链条,确保请求依次经过每个处理者,并且每个处理者都能在适当的时机打印日志和处理响应。
调试分析:
在执行时,假设收到一个 GET 请求:
Gin 路由会将该请求交给middleware1.Handle
处理。
middleware1.Handle
中会打印 “M1 接收请求”,然后调用 middleware2.Handle。
middleware2.Handle
中会打印 “M2 接收请求”,然后调用 finalHandler.Handle。
finalHandler.Handle
中会打印 “FinalHandler 接收请求”,然后调用Home(c)
处理实际的业务逻辑。
Home(c)
会在控制台打印 “Home Receiving……”,并向客户端返回 “Home Receiving……” 字符串。
控制流会逆序返回,最终 finalHandler.Handle 打印 “FinalHandler 得到响应”,然后依次是 middleware2.Handle 和 middleware1.Handle 的响应打印。
Gin引擎对象
启动成功,代码无报错,责任链构建成功,正在监听8080
端口,Demo启动成功!具体输出结果见测试结果部分。
图 108 成功启动责任链模式案例
责任链模式测试结果
APIfox测试工具监听向8080端口发送GET请求:显示Home Reciving……
,说明责任链构建成功,且将信息Home Reciving……
正确显示到客户端。
图132 Apifox发起GET测试请求
责任链模式测试结果进一步剖析如下:
图133 责任链模式测试结果剖析图
客户端构建责任链并发起监听8080端口请求,接下来分析控制台输出的顺序是否与调试分析的预测一致:
图134 Apifox发起GET请求
访问 http://localhost:8080/
,向服务端发出一条GET请求,可以看到以下控制台输出:
(1) 请求发出:
M1
接收请求:Middleware1 接收请求。与分析图的序号①对应
M2
接收请求:Middleware2 接收请求。与分析图的序号②对应
FinalHandler
接收请求:FinalHandler 接收请求。与分析图的序号③对应
Home
视图层 Home Receiving……:Home 函数处理请求,输出 “Home Receiving……”。与分析图的序号④对应
同时向客户端(APIfox发起的请求端)发送"Home Receiving……
(2) 接收响应:
FinalHandler
得到响应:FinalHandler 处理完请求,得到响应。与分析图的序号⑤对应
M2
得到响应:Middleware2 得到最终处理结果的响应。与分析图的序号⑥对应
M1
得到响应:Middleware1 得到 Middleware2 的响应,最终完成整个请求的处理。与分析图的序号⑦对应。
综上,无论是控制台输出的结果还是客户端显示的信息,都与整个调试分析的结果一致,责任链构建成功,测试通过!
图135服务端监听端口多次测试请求
持续发起多次请求,责任链也能够正常处理,不发生请求,则持续监听端口。
再测试一下拦截请求的效果,这里只需要将调用下一个处理者代码替换为c.Abort()
即可,这样就能将请求拦截在Middleware2
处理者,而
不会传给下一层的具体处理者。
图 136 进行请求的拦截
预期结果如下:
由于在Middleware2
这一层拦截掉,请求不会转发给下一层处理,即最后处理者和Home
业务函数的内容都不会输出,客户端也不会显示出Home Receiving……
输出结果预期如下:
M1
接收请求M2
接收请求 M2 得到响应 M1 得到响应
测试结果如下:
客户端发起请求,没有接收到Home发送的内容,测试结果与预期一致!
图137 客户端发起GET请求
再看一下控制台输出的内容:只输出M1和M2两个中间件有关的内容,无输出最后处理者的内容,测试结果与预期一致!
图138 服务端监听端口输出信息
结语
责任链模式作为一种优秀的设计模式,在Gin框架中展现了其强大的灵活性和可扩展性。通过本文的探讨,我们深入理解了责任链模式在处理请求流程、中间件管理和异常处理方面的应用。合理地利用责任链模式可以使代码更加模块化和可复用,从而提高了应用程序的设计质量和开发效率。希望本文能够为开发者提供实用的指导和启发,帮助他们在实际项目中充分发挥责任链模式的优势,构建更加健壮和高效的Web应用程序。
看到这里的小伙伴,恭喜你又掌握了一个技能👊
希望大家能取得胜利,坚持就是胜利💪
我是寸铁!我们下期再见💕
往期好文💕
保姆级教程
【保姆级教程】Windows11下go-zero的etcd安装与初步使用
【保姆级教程】Windows11安装go-zero代码生成工具goctl、protoc、go-zero
【Go-Zero】手把手带你在goland中创建api文件并设置高亮
报错解决
【Go-Zero】Error: user.api 27:9 syntax error: expected ‘:‘ | ‘IDENT‘ | ‘INT‘, got ‘(‘ 报错解决方案及api路由注意事项
【Go-Zero】Error: only one service expected goctl一键转换生成rpc服务错误解决方案
【Go-Zero】【error】 failed to initialize database, got error Error 1045 (28000):报错解决方案
【Go-Zero】Error 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using password: YES)报错解决方案
【Go-Zero】type mismatch for field “Auth.AccessSecret“, expect “string“, actual “number“报错解决方案
【Go-Zero】Error: user.api 30:2 syntax error: expected ‘)‘ | ‘KEY‘, got ‘IDENT‘报错解决方案
【Go-Zero】Windows启动rpc服务报错panic:context deadline exceeded解决方案
Go面试向
【Go面试向】defer与time.sleep初探
【Go面试向】defer与return的执行顺序初探
【Go面试向】Go程序的执行顺序
【Go面试向】rune和byte类型的认识与使用
【Go面试向】实现map稳定的有序遍历的方式
相关文章:

【Gin】深度解析:在Gin框架中优化应用程序流程的责任链设计模式(下)
【Gin】深度解析:在Gin框架中优化应用程序流程的责任链设计模式(下) 大家好 我是寸铁👊 【Gin】深度解析:在Gin框架中优化应用程序流程的责任链设计模式(下)✨ 喜欢的小伙伴可以点点关注 💝 前言 本次文章分为上下两部分…...

C语言——运算符及表达式
C语言——运算符及表达式 运算符运算符的分类(自增运算符)、--(自减运算符)赋值运算符逗号运算符(顺序求值运算符) 表达式 运算符 运算符的分类 C语言的运算符范围很宽,除了控制语句和输入输出…...

Python面试宝典第23题:分发糖果
题目 n 个孩子站成一排,给你一个整数数组 ratings 表示每个孩子的评分。你需要按照以下要求,给这些孩子分发糖果。 (1)每个孩子至少分配到 1 个糖果。 (2)相邻两个孩子评分更高的孩子会获得更多的糖果。 请…...

Java与模式及其应用场景知识点分享(电子版)
前言 Java 编程语言自1995年问世以来,其成功好像任何编程语言都无法媲美。生逢其时(互联网的兴起)固然是一方面的原因,而Java吸收总结了前人的经验教训,反映了最新技术(the state ofthe art),对其受到欢迎和采用,恐怕…...

软考高级第四版备考--第36天(审计内容)
IT内部控制审计:IT内部控制审计主要包括组织层面IT控制审计、IT一般控制审计及应用控制审计 IT专项审计:IT专项审计主要包括信息系统生命周期审计、信息系统开发过程审计、信息系统运行维护审计、网络与信息安全审计、信息系统项目审计、数据审计...

文件IO相关作业
1> 使用文件IO完成,将源文件中的所有内容进行加密(大写转小写、小写转大写)后写入目标文件中 源文件内容不变 #include<myhead.h>int main(int argc, const char *argv[]) {//判断传入的是否是两个文件if(argc!3){write(2,"inp…...

vue3 watch监听 父子组件通信
目录 01 watch监听方式 02 父子组件的通信 01 watch监听方式 1.watch(被监听的变量,(新值,旧值)>{ }) 默认直接就是深层监听 如果想要配置深度监听和默认触发 需要在第三个参数定义options对象 2.watch(被监听的变量,()>{},{ deep:true, immediate:true 项目打开后就执…...

【信创】adduser与useradd的区别 _ 统信 _ 麒麟 _ 中科方德
原文链接:【信创】adduser与useradd的区别 | 统信 | 麒麟 | 中科方德 Hello,大家好啊!今天给大家带来一篇关于在信创终端操作系统上adduser和useradd命令区别的文章。adduser和useradd都是用于在Linux系统上添加用户的命令,但它们…...

微软Win11 24H2最新可选更新补丁26100.1301来袭!
系统之家于7月31日发出最新报道,微软针对Win11 24H2用户推出七月最新的可选更新KB5040529,本次更新为开始菜单引入了全新的账号管理器,也改进了任务栏上的小组件图标。接下来跟随系统之家小编来看看本次更新的详细内容吧!【推荐下…...

层次特征的尺度艺术:sklearn中的缩放技术
层次特征的尺度艺术:sklearn中的缩放技术 在机器学习中,特征缩放(Feature Scaling)是数据预处理的重要步骤,尤其对于基于距离的算法,如K-近邻(KNN)和支持向量机(SVM&…...

Chapter 21 深入理解JSON
欢迎大家订阅【Python从入门到精通】专栏,一起探索Python的无限可能! 文章目录 前言一、JSON数据格式1. 什么是JSON?2. JSON数据的格式 二、JSON格式数据转化三、格式化JSON数据的在线工具 前言 在当今数据驱动的世界中,JSON&…...

【C++高阶数据结构】红黑树:全面剖析与深度学习
目录 🚀 前言:红黑树与AVL树的比较一: 🔥 红黑树的概念二: 🔥 红黑树的性质 三: 🔥 红黑树节点的定义和结构🚀 3.1 基本元素🚀 3.2 节点颜色🚀 3.…...

前端基于 axios 实现批量任务调度管理器 demo
一、背景介绍 这是一个基于 axios 实现的批量任务调度管理器的 demo。它使用了axios、promise 等多种技术和原理来实现批量处理多个异步请求,并确保所有请求都能正确处理并报告其状态。 假设有一个场景:有一个任务列表,有单个任务的处理功能…...

Docker容器下面home assistant忘记账号密码怎么重置?
环境: docker ha 问题描述: Docker容器下面home assistant忘记账号密码怎么重置? 解决方案: 你可以按照以下步骤来找回或重置密码: 方法一 (未解决) 停止并删除当前的Home Assistant容器(确保你已经保…...

CTF-NSSCTF[GKCTF 2021]
[GKCTF 2021]easycms 考察: 用扫描工具扫描目录,扫描到后台登录界面/admin.php 题目提示了密码是五位弱口令,试了试弱口令admin和12345直接成功了 任意文件下载 点击设计-->主题然后随便选择一个主题,点击自定义࿰…...

MSA+抑郁症模型总结(一)(论文复现)
MSA抑郁症模型总结(一)(论文复现) 本文所涉及所有资源均在传知代码平台可获取 文章目录 MSA抑郁症模型总结(一)(论文复现)情感分析在多场景的应用一、概述二、论文地址三、研究背景四…...

STM32智能农业灌溉系统教程
目录 引言环境准备智能农业灌溉系统基础代码实现:实现智能农业灌溉系统 4.1 数据采集模块 4.2 数据处理与分析模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景:农业监测与优化问题解决方案与优化收尾与总结 1. 引言 智能农业灌溉系统通…...

MySQL存储引擎和
MySQL存储引擎 在数据库中保存的是一张张有着千丝万缕关系的表,所以表设计的好坏,将直接影响着整个数据库。而在设计表的时候,最关注的一个问题是使用什么存储引擎。MySQL中的数据用各种不同的技术存储在文件(或者内存)中。这些技术中的每一种…...

Eclipse 主网向开发者开放
摘要:Eclipse 基金会宣布,Eclipse 主网已经向开发者开放。在接下来几周的时间里,Eclipse 将邀请开发者在主网上部署项目,并参加黑客马拉松活动——“Total Eclipse Challenge”。 Eclipse 是首个基于以太坊的 SVM Layer2 方案&am…...

国内NAT服务器docker方式搭建rustdesk服务
前言 如果遇到10054,就不要设置id服务器!!! 由于遇到大带宽,但是又贵,所以就NAT的啦,但是只有ipv4共享和一个ipv6,带宽50MB(活动免费会升130MB~) https://bigchick.xyz/aff.php?aff322 月付-5 循环 :CM-CQ-Monthly-5 年付-60循环:CM-CQ-Annually-60官方…...

锅总浅析链路追踪技术
链路追踪是什么?常用的链路追踪工具有哪些?它们的异同、架构、工作流程及关键指标有哪些?希望读完本文能帮您解答这些疑惑! 一、链路追踪简介 链路追踪技术(Distributed Tracing)是一种用于监控和分析分布…...

为什么阿里开发手册不建议使用Date类?
在日常编码中,基本上99%的项目都会有一个DateUtil工具类,而时间工具类里用的最多的就是java.util.Date。 大家都这么写,这还能有问题?? 当你的“默认常识”出现问题,这个打击,就是毁灭性的。 …...

中间层 k8s(Kubernetes) 到底是什么,架构是怎么样的?
你是一个程序员,你用代码写了一个博客应用服务,并将它部署在了云平台上。 但应用服务太过受欢迎,访问量太大,经常会挂。 所以你用了一些工具自动重启挂掉的应用服务,并且将应用服务部署在了好几个服务器上,…...

【CTFWP】ctfshow-web40
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 题目介绍:题目分析:payload:payload解释:payload2:payload2解释:flag 题目介绍: …...

项目实战1(30小时精通C++和外挂实战)
项目实战1(30小时精通C和外挂实战) 01-MFC1-图标02-MFC2-按钮、调试、打开网页05-MFC5-checkbox及按钮绑定对象06--文件格式、OD序列号08-暴力破解09-CE10-秒杀僵尸 01-MFC1-图标 这个外挂只针对植物大战僵尸游戏 开发这个外挂,首先要将界面…...

百日筑基第三十六天
今日论道还算顺利,只可惜感到也没学到什么东西。晚些时候师祖问话,主要是来这边之后有什么困难之类,好像也没遇到需要他来帮我解决的困难,于是问了些修炼方法之类。...

MySQL: ALTER
正文 在数据库管理系统(DBMS)中,DDL(Data Definition Language)、DCL(Data Control Language)、和 DML(Data Manipulation Language)是三种主要的SQL(Struct…...

微前端技术预研 - bit初体验
1.关于什么是微前端以及微前端的发展, 当前主流框架以及实现技术等,可参考这篇总结(非常全面), 微前端总结:目录详见下图 本文内容主要针对bit框架的实时思路以及具体使用。 1.什么是Bit? Bit 是可组合软件的构建…...

对象关系映射---ORM
一、什么是ORM? ORM(Object Relational Mapping),即对象关系映射,是一种程序设计技术,用于在面向对象编程语言中实现对象和关系型数据库之间的映射。 二、ORM是干什么的? ORM 的主要目的是简…...

Django REST Framework(十七)Authentication
1.认证Authentication 在 Django REST framework (DRF) 中,可以在配置文件中配置全局默认的认证方案。常见的认证方式包括 cookie、session、和 token。DRF 提供了灵活的认证机制,可以在全局配置文件中设置默认认证方式,也可以在具体的视图类…...