2021-01-17 21:46:25 +08:00
|
|
|
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
2018-01-03 10:38:53 +08:00
|
|
|
//
|
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
2019-02-02 16:18:25 +08:00
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2018-01-03 10:38:53 +08:00
|
|
|
|
2019-06-18 20:58:47 +08:00
|
|
|
// Package ghttp provides powerful http server and simple client implements.
|
2018-01-03 10:38:53 +08:00
|
|
|
package ghttp
|
2020-11-08 15:44:04 +08:00
|
|
|
|
|
|
|
|
import (
|
2021-11-13 23:23:55 +08:00
|
|
|
"net/http"
|
|
|
|
|
"reflect"
|
2023-10-09 20:03:45 +08:00
|
|
|
"sync"
|
2021-11-13 23:23:55 +08:00
|
|
|
"time"
|
|
|
|
|
|
2021-12-19 00:18:18 +08:00
|
|
|
"github.com/gorilla/websocket"
|
|
|
|
|
|
2021-10-11 21:41:56 +08:00
|
|
|
"github.com/gogf/gf/v2/container/gmap"
|
|
|
|
|
"github.com/gogf/gf/v2/container/gtype"
|
2022-11-01 20:12:21 +08:00
|
|
|
"github.com/gogf/gf/v2/errors/gcode"
|
|
|
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
2024-12-10 09:52:48 +08:00
|
|
|
"github.com/gogf/gf/v2/net/ghttp/internal/graceful"
|
2022-05-24 18:53:10 +08:00
|
|
|
"github.com/gogf/gf/v2/net/goai"
|
2022-03-19 17:58:21 +08:00
|
|
|
"github.com/gogf/gf/v2/net/gsvc"
|
2021-10-11 21:41:56 +08:00
|
|
|
"github.com/gogf/gf/v2/os/gcache"
|
2023-06-28 21:04:49 +08:00
|
|
|
"github.com/gogf/gf/v2/os/gctx"
|
2021-10-11 21:41:56 +08:00
|
|
|
"github.com/gogf/gf/v2/os/gsession"
|
2023-09-25 21:34:58 +08:00
|
|
|
"github.com/gogf/gf/v2/os/gstructs"
|
2022-11-10 19:56:12 +08:00
|
|
|
"github.com/gogf/gf/v2/util/gtag"
|
2020-11-08 15:44:04 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type (
|
2021-06-26 11:18:44 +08:00
|
|
|
// Server wraps the http.Server and provides more rich features.
|
2020-11-08 15:44:04 +08:00
|
|
|
Server struct {
|
2022-05-06 20:25:21 +08:00
|
|
|
instance string // Instance name of current HTTP server.
|
|
|
|
|
config ServerConfig // Server configuration.
|
|
|
|
|
plugins []Plugin // Plugin array to extend server functionality.
|
2024-12-10 09:52:48 +08:00
|
|
|
servers []*graceful.Server // Underlying http.Server array.
|
2022-05-06 20:25:21 +08:00
|
|
|
serverCount *gtype.Int // Underlying http.Server number for internal usage.
|
|
|
|
|
closeChan chan struct{} // Used for underlying server closing event notification.
|
refactor: interface{} to any and reflect.Ptr to reflect.Pointer (#4395)
This pull request standardizes the use of the Go 1.18+ `any` type alias
instead of `interface{}` throughout the codebase. The change improves
code readability and aligns with modern Go best practices. The update
touches many files, including core data structures, code generation
templates, logging utilities, and test data, ensuring consistency across
all usages.
**Type alias migration to `any`:**
* Replaced all instances of `interface{}` with `any` in core data
structures such as `garray` and in generated model structs (e.g.,
`TableUser`, `User1`, `User2`) to modernize type usage.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[3]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[4]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[5]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[6]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
* Updated function signatures, method parameters, and return types from
`interface{}` to `any` in various parts of the codebase, including code
generation, service logic, and logging utilities (e.g., `mlog`).
[[1]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[2]](diffhunk://#diff-2b1953fb78cf3593d8c2c7d911e95b65fd0b847c30ed0b4d167d16fe6d781235L54-R74)
[[3]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[4]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
[[5]](diffhunk://#diff-c5d51d56f487779a2b6207c7ad26c7a20bbadcc846ce094fe60ab4cabff58c51L107-R107)
[[6]](diffhunk://#diff-f96e6a9fdb416eb1804ceaba1fe0ac637bff22c43837f8bb849c2366ce72d4a1L116-R121)
[[7]](diffhunk://#diff-f94c83a1b08ae060d9346f4a6031fc4a7b9a0b894e02d9afaa09018b6598eac0L112-R112)
[[8]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L36-R36)
[[9]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L74-R74)
[[10]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L96-R96)
**Generated code and templates:**
* Adjusted generated files and code generation templates to output `any`
instead of `interface{}` for relevant struct fields and function
signatures, ensuring that new code generation aligns with the updated
convention.
[[1]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[2]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[3]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[4]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[5]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
[[6]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[7]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[8]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
**Container and utility updates:**
* Refactored the `garray` container implementation and related
constructors/methods to use `[]any` instead of `[]interface{}`, along
with corresponding function signatures.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L52-R52)
[[3]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L62-R62)
[[4]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L73-R86)
[[5]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L96-R97)
[[6]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L107-R114)
[[7]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L124-R124)
[[8]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L135-R143)
[[9]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L167-R167)
These changes collectively modernize the codebase and prepare it for
future Go developments by using the idiomatic `any` type.
2025-08-28 16:53:19 +08:00
|
|
|
serveTree map[string]any // The route maps tree.
|
2022-05-06 20:25:21 +08:00
|
|
|
serveCache *gcache.Cache // Server caches for internal usage.
|
|
|
|
|
routesMap map[string][]*HandlerItem // Route map mainly for route dumps and repeated route checks.
|
|
|
|
|
statusHandlerMap map[string][]HandlerFunc // Custom status handler map.
|
|
|
|
|
sessionManager *gsession.Manager // Session manager.
|
|
|
|
|
openapi *goai.OpenApiV3 // The OpenApi specification management object.
|
2023-10-09 20:03:45 +08:00
|
|
|
serviceMu sync.Mutex // Concurrent safety for operations of attribute service.
|
2022-05-23 15:08:11 +08:00
|
|
|
service gsvc.Service // The service for Registry.
|
2023-03-08 14:12:51 +08:00
|
|
|
registrar gsvc.Registrar // Registrar for service register.
|
2020-11-08 15:44:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Router object.
|
|
|
|
|
Router struct {
|
|
|
|
|
Uri string // URI.
|
|
|
|
|
Method string // HTTP method
|
|
|
|
|
Domain string // Bound domain.
|
|
|
|
|
RegRule string // Parsed regular expression for route matching.
|
|
|
|
|
RegNames []string // Parsed router parameter names.
|
|
|
|
|
Priority int // Just for reference.
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-26 11:18:44 +08:00
|
|
|
// RouterItem is just for route dumps.
|
2020-11-08 15:44:04 +08:00
|
|
|
RouterItem struct {
|
2022-05-06 20:25:21 +08:00
|
|
|
Handler *HandlerItem // The handler.
|
2020-11-08 15:44:04 +08:00
|
|
|
Server string // Server name.
|
|
|
|
|
Address string // Listening address.
|
2025-03-17 09:21:00 +08:00
|
|
|
Domain string // Bound domain, eg: example.com
|
2023-07-06 21:29:33 +08:00
|
|
|
Type HandlerType // Route handler type.
|
2020-11-08 15:44:04 +08:00
|
|
|
Middleware string // Bound middleware.
|
2025-03-17 09:21:00 +08:00
|
|
|
Method string // Handler method name, eg: get, post.
|
|
|
|
|
Route string // Route URI, eg: /api/v1/user/{id}.
|
2020-11-08 15:44:04 +08:00
|
|
|
Priority int // Just for reference.
|
2024-12-10 09:52:48 +08:00
|
|
|
IsServiceHandler bool // Is a service handler.
|
2020-11-08 15:44:04 +08:00
|
|
|
}
|
|
|
|
|
|
2021-07-13 23:01:31 +08:00
|
|
|
// HandlerFunc is request handler function.
|
|
|
|
|
HandlerFunc = func(r *Request)
|
|
|
|
|
|
2021-10-02 22:34:39 +08:00
|
|
|
// handlerFuncInfo contains the HandlerFunc address and its reflection type.
|
2021-07-13 23:01:31 +08:00
|
|
|
handlerFuncInfo struct {
|
2023-09-25 21:34:58 +08:00
|
|
|
Func HandlerFunc // Handler function address.
|
|
|
|
|
Type reflect.Type // Reflect type information for current handler, which is used for extensions of the handler feature.
|
|
|
|
|
Value reflect.Value // Reflect value information for current handler, which is used for extensions of the handler feature.
|
|
|
|
|
IsStrictRoute bool // Whether strict route matching is enabled.
|
|
|
|
|
ReqStructFields []gstructs.Field // Request struct fields.
|
2021-07-13 23:01:31 +08:00
|
|
|
}
|
|
|
|
|
|
2022-05-06 20:25:21 +08:00
|
|
|
// HandlerItem is the registered handler for route handling,
|
2020-11-08 15:44:04 +08:00
|
|
|
// including middleware and hook functions.
|
2022-05-06 20:25:21 +08:00
|
|
|
HandlerItem struct {
|
2023-10-16 20:41:10 +08:00
|
|
|
// Unique handler item id mark.
|
|
|
|
|
// Note that the handler function may be registered multiple times as different handler items,
|
|
|
|
|
// which have different handler item id.
|
|
|
|
|
Id int
|
2021-07-30 15:15:44 +08:00
|
|
|
Name string // Handler name, which is automatically retrieved from runtime stack when registered.
|
2023-07-06 21:29:33 +08:00
|
|
|
Type HandlerType // Handler type: object/handler/middleware/hook.
|
2021-07-30 15:15:44 +08:00
|
|
|
Info handlerFuncInfo // Handler function information.
|
|
|
|
|
InitFunc HandlerFunc // Initialization function when request enters the object (only available for object register type).
|
|
|
|
|
ShutFunc HandlerFunc // Shutdown function when request leaves out the object (only available for object register type).
|
|
|
|
|
Middleware []HandlerFunc // Bound middleware array.
|
2023-07-06 21:29:33 +08:00
|
|
|
HookName HookName // Hook type name, only available for the hook type.
|
2021-07-30 15:15:44 +08:00
|
|
|
Router *Router // Router object.
|
|
|
|
|
Source string // Registering source file `path:line`.
|
2020-11-08 15:44:04 +08:00
|
|
|
}
|
|
|
|
|
|
2022-11-01 20:17:59 +08:00
|
|
|
// HandlerItemParsed is the item parsed from URL.Path.
|
|
|
|
|
HandlerItemParsed struct {
|
2022-05-06 20:25:21 +08:00
|
|
|
Handler *HandlerItem // Handler information.
|
2021-07-19 20:06:44 +08:00
|
|
|
Values map[string]string // Router values parsed from URL.Path.
|
2020-11-08 15:44:04 +08:00
|
|
|
}
|
|
|
|
|
|
2023-07-06 21:29:33 +08:00
|
|
|
// ServerStatus is the server status enum type.
|
|
|
|
|
ServerStatus = int
|
|
|
|
|
|
|
|
|
|
// HookName is the route hook name enum type.
|
|
|
|
|
HookName string
|
|
|
|
|
|
|
|
|
|
// HandlerType is the route handler enum type.
|
|
|
|
|
HandlerType string
|
|
|
|
|
|
2020-11-08 15:44:04 +08:00
|
|
|
// Listening file descriptor mapping.
|
|
|
|
|
// The key is either "http" or "https" and the value is its FD.
|
|
|
|
|
listenerFdMap = map[string]string
|
2022-07-13 20:20:38 +08:00
|
|
|
|
|
|
|
|
// internalPanic is the custom panic for internal usage.
|
|
|
|
|
internalPanic string
|
2020-11-08 15:44:04 +08:00
|
|
|
)
|
|
|
|
|
|
2022-10-08 21:45:21 +08:00
|
|
|
const (
|
|
|
|
|
// FreePortAddress marks the server listens using random free port.
|
2024-12-10 09:52:48 +08:00
|
|
|
FreePortAddress = graceful.FreePortAddress
|
2022-10-08 21:45:21 +08:00
|
|
|
)
|
|
|
|
|
|
2020-11-08 15:44:04 +08:00
|
|
|
const (
|
2024-12-10 09:52:48 +08:00
|
|
|
HeaderXUrlPath = "x-url-path" // Used for custom route handler, which does not change URL.Path.
|
|
|
|
|
HookBeforeServe HookName = "HOOK_BEFORE_SERVE" // Hook handler before route handler/file serving.
|
|
|
|
|
HookAfterServe HookName = "HOOK_AFTER_SERVE" // Hook handler after route handler/file serving.
|
|
|
|
|
HookBeforeOutput HookName = "HOOK_BEFORE_OUTPUT" // Hook handler before response output.
|
|
|
|
|
HookAfterOutput HookName = "HOOK_AFTER_OUTPUT" // Hook handler after response output.
|
|
|
|
|
DefaultServerName = "default"
|
|
|
|
|
DefaultDomainName = "default"
|
|
|
|
|
HandlerTypeHandler HandlerType = "handler"
|
|
|
|
|
HandlerTypeObject HandlerType = "object"
|
|
|
|
|
HandlerTypeMiddleware HandlerType = "middleware"
|
|
|
|
|
HandlerTypeHook HandlerType = "hook"
|
|
|
|
|
ServerStatusStopped = graceful.ServerStatusStopped
|
|
|
|
|
ServerStatusRunning = graceful.ServerStatusRunning
|
2021-10-06 14:22:58 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
2024-12-10 09:52:48 +08:00
|
|
|
supportedHttpMethods = "GET,PUT,POST,DELETE,PATCH,HEAD,CONNECT,OPTIONS,TRACE"
|
|
|
|
|
defaultMethod = "ALL"
|
|
|
|
|
routeCacheDuration = time.Hour
|
|
|
|
|
ctxKeyForRequest gctx.StrKey = "gHttpRequestObject"
|
|
|
|
|
contentTypeXml = "text/xml"
|
|
|
|
|
contentTypeHtml = "text/html"
|
|
|
|
|
contentTypeJson = "application/json"
|
|
|
|
|
contentTypeJavascript = "application/javascript"
|
|
|
|
|
swaggerUIPackedPath = "/goframe/swaggerui"
|
|
|
|
|
responseHeaderTraceID = "Trace-ID"
|
|
|
|
|
specialMethodNameInit = "Init"
|
|
|
|
|
specialMethodNameShut = "Shut"
|
|
|
|
|
specialMethodNameIndex = "Index"
|
|
|
|
|
defaultEndpointPort = 80
|
|
|
|
|
noPrintInternalRoute = "internalMiddlewareServerTracing"
|
2020-11-08 15:44:04 +08:00
|
|
|
)
|
|
|
|
|
|
2022-07-13 20:20:38 +08:00
|
|
|
const (
|
|
|
|
|
exceptionExit internalPanic = "exit"
|
|
|
|
|
exceptionExitAll internalPanic = "exit_all"
|
|
|
|
|
exceptionExitHook internalPanic = "exit_hook"
|
|
|
|
|
)
|
|
|
|
|
|
2020-11-08 15:44:04 +08:00
|
|
|
var (
|
2022-03-19 17:58:21 +08:00
|
|
|
// methodsMap stores all supported HTTP method.
|
|
|
|
|
// It is used for quick HTTP method searching using map.
|
2020-11-08 15:44:04 +08:00
|
|
|
methodsMap = make(map[string]struct{})
|
|
|
|
|
|
feat(instance): migrate instance containers to type-safe generics (#4617)
### 变更说明
本次重构将项目中用于**实例管理的容器**从 `StrAnyMap`/`IntAnyMap` 迁移到类型安全的泛型实现
`KVMapWithChecker`,同时将相关的 `glist.List` 和 `gqueue.Queue`
替换为对应的泛型版本,以提高实例管理的类型安全性。并且减少原先代码中的大量类型断言,提高性能。
### 前因
目前`goframe`中大量使用了包含`any`的容器,然后通过断言去转换类型,麻烦且影响性能,尤其是对`gdb/gredis/glog`等需要高频获取`instance`实例的组件影响较大。最近几个版本中gf完成了数据结构容器的泛型化改造,以及我最近解决了其中几个泛型容器对于`typed
nil`过滤的问题,所以可以逐步迁移这些实例容器到泛型容器,减少断言优化性能
### 主要改进
#### 1. 实例容器泛型化
以下模块的实例管理容器已迁移到泛型实现:
**核心实例管理**:
- `database/gdb`: 数据库实例容器 → `KVMap[string, DB]`
- `database/gredis`: Redis 实例容器 → `KVMap[string, *Redis]`
- `database/gredis`: Redis 配置容器 → `KVMap[string, *Config]`
- `os/gcfg`: 配置实例容器 → `KVMap[string, *Config]`
- `os/glog`: 日志实例容器 → `KVMap[string, *Logger]`
- `os/gview`: 视图实例容器 → `KVMap[string, *View]`
- `i18n/gi18n`: 国际化实例容器 → `KVMap[string, *Manager]`
**网络服务实例**:
- `net/ghttp`: HTTP 服务器容器 → `KVMap[string, *Server]`
- `net/gtcp`: TCP 服务器容器 → `KVMap[any, *Server]`
- `net/gudp`: UDP 服务器容器 → `KVMap[string, *Server]`
**其他实例容器**:
- `os/gres`: 资源实例容器 → `KVMap[string, *Resource]`
- `os/gfpool`: 文件池容器 → `KVMap[string, *Pool]`
- `os/gspath`: 路径搜索容器 → `KVMap[string, *SPath]`
- `net/gtcp`: 连接池容器 → `KVMap[string, *gpool.Pool]`
#### 2. 相关数据结构泛型化
- `os/gfsnotify`: 回调列表 → `TList[*Callback]`,事件队列 → `TQueue[*Event]`
- `os/grpool`: 任务队列 → `TList[*localPoolItem]`
- `os/gcache`: 事件队列 → `TList[*adapterMemoryEvent]`
- `net/ghttp`: 解析项列表 → `TList[*HandlerItemParsed]`
- `os/gproc`: 消息队列 → `TQueue[*MsgRequest]`
- `os/gmlock`: 锁映射 → `KVMap[string, *sync.RWMutex]`
### 技术实现
1. **引入检查器函数**: 为每个实例容器添加 `checker` 函数用于空值检测
2. **消除类型断言**: 实例获取时无需 `v.(*Type)` 转换
3. **明确函数签名**: `GetOrSetFuncLock` 的回调从 `func() any` 改为 `func() T`
### 使用示例
#### 实例容器的变更
**变更前**:
```go
// 旧的实例管理方式
var instances = gmap.NewStrAnyMap(true)
func Instance(name string) *Logger {
v := instances.GetOrSetFuncLock(name, func() any {
return New()
})
return v.(*Logger) // 需要类型断言
}
```
**变更后**:
```go
// 新的泛型实例容器
var (
checker = func(v *Logger) bool { return v == nil }
instances = gmap.NewKVMapWithChecker[string, *Logger](checker, true)
)
func Instance(name string) *Logger {
return instances.GetOrSetFuncLock(name, New) // 直接返回,无需断言
}
```
#### 队列容器的变更
**变更前**:
```go
// 旧的队列方式
events := gqueue.New()
events.Push(&Event{Path: "/tmp/file"})
if v := events.Pop(); v != nil {
event := v.(*Event) // 需要类型断言
handleEvent(event)
}
```
**变更后**:
```go
// 新的泛型队列
events := gqueue.NewTQueue[*Event]()
events.Push(&Event{Path: "/tmp/file"})
if event := events.Pop(); event != nil {
handleEvent(event) // event 已是 *Event 类型
}
```
### 收益
- ✅ **编译时类型安全**: 实例容器的类型错误在编译期捕获
- ✅ **消除运行时断言**: 避免类型断言带来的 panic 风险
- ✅ **提升代码可读性**: 实例管理逻辑更清晰
- ✅ **改善开发体验**: IDE 类型提示和代码补全更准确
### 性能权衡
**编译时**:
- 泛型实例化会增加编译时间和二进制体积
- 预估编译时间增加 5-15%,二进制体积增加约 1-2MB
**运行时**:
- 减少类型断言的反射开销
- 提升实例获取等热点路径的性能
2026-01-16 15:23:13 +08:00
|
|
|
// checker is used for checking whether the value is nil.
|
|
|
|
|
checker = func(v *Server) bool { return v == nil }
|
|
|
|
|
|
2022-03-19 17:58:21 +08:00
|
|
|
// serverMapping stores more than one server instances for current processes.
|
2020-11-08 15:44:04 +08:00
|
|
|
// The key is the name of the server, and the value is its instance.
|
feat(instance): migrate instance containers to type-safe generics (#4617)
### 变更说明
本次重构将项目中用于**实例管理的容器**从 `StrAnyMap`/`IntAnyMap` 迁移到类型安全的泛型实现
`KVMapWithChecker`,同时将相关的 `glist.List` 和 `gqueue.Queue`
替换为对应的泛型版本,以提高实例管理的类型安全性。并且减少原先代码中的大量类型断言,提高性能。
### 前因
目前`goframe`中大量使用了包含`any`的容器,然后通过断言去转换类型,麻烦且影响性能,尤其是对`gdb/gredis/glog`等需要高频获取`instance`实例的组件影响较大。最近几个版本中gf完成了数据结构容器的泛型化改造,以及我最近解决了其中几个泛型容器对于`typed
nil`过滤的问题,所以可以逐步迁移这些实例容器到泛型容器,减少断言优化性能
### 主要改进
#### 1. 实例容器泛型化
以下模块的实例管理容器已迁移到泛型实现:
**核心实例管理**:
- `database/gdb`: 数据库实例容器 → `KVMap[string, DB]`
- `database/gredis`: Redis 实例容器 → `KVMap[string, *Redis]`
- `database/gredis`: Redis 配置容器 → `KVMap[string, *Config]`
- `os/gcfg`: 配置实例容器 → `KVMap[string, *Config]`
- `os/glog`: 日志实例容器 → `KVMap[string, *Logger]`
- `os/gview`: 视图实例容器 → `KVMap[string, *View]`
- `i18n/gi18n`: 国际化实例容器 → `KVMap[string, *Manager]`
**网络服务实例**:
- `net/ghttp`: HTTP 服务器容器 → `KVMap[string, *Server]`
- `net/gtcp`: TCP 服务器容器 → `KVMap[any, *Server]`
- `net/gudp`: UDP 服务器容器 → `KVMap[string, *Server]`
**其他实例容器**:
- `os/gres`: 资源实例容器 → `KVMap[string, *Resource]`
- `os/gfpool`: 文件池容器 → `KVMap[string, *Pool]`
- `os/gspath`: 路径搜索容器 → `KVMap[string, *SPath]`
- `net/gtcp`: 连接池容器 → `KVMap[string, *gpool.Pool]`
#### 2. 相关数据结构泛型化
- `os/gfsnotify`: 回调列表 → `TList[*Callback]`,事件队列 → `TQueue[*Event]`
- `os/grpool`: 任务队列 → `TList[*localPoolItem]`
- `os/gcache`: 事件队列 → `TList[*adapterMemoryEvent]`
- `net/ghttp`: 解析项列表 → `TList[*HandlerItemParsed]`
- `os/gproc`: 消息队列 → `TQueue[*MsgRequest]`
- `os/gmlock`: 锁映射 → `KVMap[string, *sync.RWMutex]`
### 技术实现
1. **引入检查器函数**: 为每个实例容器添加 `checker` 函数用于空值检测
2. **消除类型断言**: 实例获取时无需 `v.(*Type)` 转换
3. **明确函数签名**: `GetOrSetFuncLock` 的回调从 `func() any` 改为 `func() T`
### 使用示例
#### 实例容器的变更
**变更前**:
```go
// 旧的实例管理方式
var instances = gmap.NewStrAnyMap(true)
func Instance(name string) *Logger {
v := instances.GetOrSetFuncLock(name, func() any {
return New()
})
return v.(*Logger) // 需要类型断言
}
```
**变更后**:
```go
// 新的泛型实例容器
var (
checker = func(v *Logger) bool { return v == nil }
instances = gmap.NewKVMapWithChecker[string, *Logger](checker, true)
)
func Instance(name string) *Logger {
return instances.GetOrSetFuncLock(name, New) // 直接返回,无需断言
}
```
#### 队列容器的变更
**变更前**:
```go
// 旧的队列方式
events := gqueue.New()
events.Push(&Event{Path: "/tmp/file"})
if v := events.Pop(); v != nil {
event := v.(*Event) // 需要类型断言
handleEvent(event)
}
```
**变更后**:
```go
// 新的泛型队列
events := gqueue.NewTQueue[*Event]()
events.Push(&Event{Path: "/tmp/file"})
if event := events.Pop(); event != nil {
handleEvent(event) // event 已是 *Event 类型
}
```
### 收益
- ✅ **编译时类型安全**: 实例容器的类型错误在编译期捕获
- ✅ **消除运行时断言**: 避免类型断言带来的 panic 风险
- ✅ **提升代码可读性**: 实例管理逻辑更清晰
- ✅ **改善开发体验**: IDE 类型提示和代码补全更准确
### 性能权衡
**编译时**:
- 泛型实例化会增加编译时间和二进制体积
- 预估编译时间增加 5-15%,二进制体积增加约 1-2MB
**运行时**:
- 减少类型断言的反射开销
- 提升实例获取等热点路径的性能
2026-01-16 15:23:13 +08:00
|
|
|
serverMapping = gmap.NewKVMapWithChecker[string, *Server](checker, true)
|
2020-11-08 15:44:04 +08:00
|
|
|
|
2022-03-19 17:58:21 +08:00
|
|
|
// serverRunning marks the running server counts.
|
2021-08-20 23:15:48 +08:00
|
|
|
// If there is no successful server running or all servers' shutdown, this value is 0.
|
2020-11-08 15:44:04 +08:00
|
|
|
serverRunning = gtype.NewInt()
|
|
|
|
|
|
|
|
|
|
// wsUpGrader is the default up-grader configuration for websocket.
|
|
|
|
|
wsUpGrader = websocket.Upgrader{
|
|
|
|
|
// It does not check the origin in default, the application can do it itself.
|
|
|
|
|
CheckOrigin: func(r *http.Request) bool {
|
|
|
|
|
return true
|
|
|
|
|
},
|
|
|
|
|
}
|
2022-10-08 21:45:21 +08:00
|
|
|
// allShutdownChan is the event for all servers have done its serving and exit.
|
2020-11-08 15:44:04 +08:00
|
|
|
// It is used for process blocking purpose.
|
2022-10-08 21:45:21 +08:00
|
|
|
allShutdownChan = make(chan struct{}, 1000)
|
2020-11-08 15:44:04 +08:00
|
|
|
|
|
|
|
|
// serverProcessInitialized is used for lazy initialization for server.
|
|
|
|
|
// The process can only be initialized once.
|
|
|
|
|
serverProcessInitialized = gtype.NewBool()
|
|
|
|
|
|
2022-03-19 17:58:21 +08:00
|
|
|
// gracefulEnabled is used for a graceful reload feature, which is false in default.
|
2020-11-08 15:44:04 +08:00
|
|
|
gracefulEnabled = false
|
|
|
|
|
|
2022-03-19 17:58:21 +08:00
|
|
|
// defaultValueTags are the struct tag names for default value storing.
|
2022-11-07 17:51:37 +08:00
|
|
|
defaultValueTags = []string{gtag.DefaultShort, gtag.Default}
|
2020-11-08 15:44:04 +08:00
|
|
|
)
|
2022-07-27 19:52:02 +08:00
|
|
|
|
|
|
|
|
var (
|
2024-12-11 10:14:12 +08:00
|
|
|
// ErrNeedJsonBody is the error that indicates the request body content should be JSON format.
|
2023-12-04 19:34:48 +08:00
|
|
|
ErrNeedJsonBody = gerror.NewWithOption(gerror.Option{
|
2022-07-27 19:52:02 +08:00
|
|
|
Text: "the request body content should be JSON format",
|
|
|
|
|
Code: gcode.CodeInvalidRequest,
|
|
|
|
|
})
|
|
|
|
|
)
|