mirror of
https://gitee.com/johng/gf
synced 2026-06-06 02:25:47 +08:00
### 变更说明
本次重构将项目中用于**实例管理的容器**从 `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
**运行时**:
- 减少类型断言的反射开销
- 提升实例获取等热点路径的性能
219 lines
9.2 KiB
Go
219 lines
9.2 KiB
Go
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
|
//
|
|
// 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,
|
|
// You can obtain one at https://github.com/gogf/gf.
|
|
|
|
// Package ghttp provides powerful http server and simple client implements.
|
|
package ghttp
|
|
|
|
import (
|
|
"net/http"
|
|
"reflect"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/gorilla/websocket"
|
|
|
|
"github.com/gogf/gf/v2/container/gmap"
|
|
"github.com/gogf/gf/v2/container/gtype"
|
|
"github.com/gogf/gf/v2/errors/gcode"
|
|
"github.com/gogf/gf/v2/errors/gerror"
|
|
"github.com/gogf/gf/v2/net/ghttp/internal/graceful"
|
|
"github.com/gogf/gf/v2/net/goai"
|
|
"github.com/gogf/gf/v2/net/gsvc"
|
|
"github.com/gogf/gf/v2/os/gcache"
|
|
"github.com/gogf/gf/v2/os/gctx"
|
|
"github.com/gogf/gf/v2/os/gsession"
|
|
"github.com/gogf/gf/v2/os/gstructs"
|
|
"github.com/gogf/gf/v2/util/gtag"
|
|
)
|
|
|
|
type (
|
|
// Server wraps the http.Server and provides more rich features.
|
|
Server struct {
|
|
instance string // Instance name of current HTTP server.
|
|
config ServerConfig // Server configuration.
|
|
plugins []Plugin // Plugin array to extend server functionality.
|
|
servers []*graceful.Server // Underlying http.Server array.
|
|
serverCount *gtype.Int // Underlying http.Server number for internal usage.
|
|
closeChan chan struct{} // Used for underlying server closing event notification.
|
|
serveTree map[string]any // The route maps tree.
|
|
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.
|
|
serviceMu sync.Mutex // Concurrent safety for operations of attribute service.
|
|
service gsvc.Service // The service for Registry.
|
|
registrar gsvc.Registrar // Registrar for service register.
|
|
}
|
|
|
|
// 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.
|
|
}
|
|
|
|
// RouterItem is just for route dumps.
|
|
RouterItem struct {
|
|
Handler *HandlerItem // The handler.
|
|
Server string // Server name.
|
|
Address string // Listening address.
|
|
Domain string // Bound domain, eg: example.com
|
|
Type HandlerType // Route handler type.
|
|
Middleware string // Bound middleware.
|
|
Method string // Handler method name, eg: get, post.
|
|
Route string // Route URI, eg: /api/v1/user/{id}.
|
|
Priority int // Just for reference.
|
|
IsServiceHandler bool // Is a service handler.
|
|
}
|
|
|
|
// HandlerFunc is request handler function.
|
|
HandlerFunc = func(r *Request)
|
|
|
|
// handlerFuncInfo contains the HandlerFunc address and its reflection type.
|
|
handlerFuncInfo struct {
|
|
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.
|
|
}
|
|
|
|
// HandlerItem is the registered handler for route handling,
|
|
// including middleware and hook functions.
|
|
HandlerItem struct {
|
|
// 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
|
|
Name string // Handler name, which is automatically retrieved from runtime stack when registered.
|
|
Type HandlerType // Handler type: object/handler/middleware/hook.
|
|
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.
|
|
HookName HookName // Hook type name, only available for the hook type.
|
|
Router *Router // Router object.
|
|
Source string // Registering source file `path:line`.
|
|
}
|
|
|
|
// HandlerItemParsed is the item parsed from URL.Path.
|
|
HandlerItemParsed struct {
|
|
Handler *HandlerItem // Handler information.
|
|
Values map[string]string // Router values parsed from URL.Path.
|
|
}
|
|
|
|
// 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
|
|
|
|
// Listening file descriptor mapping.
|
|
// The key is either "http" or "https" and the value is its FD.
|
|
listenerFdMap = map[string]string
|
|
|
|
// internalPanic is the custom panic for internal usage.
|
|
internalPanic string
|
|
)
|
|
|
|
const (
|
|
// FreePortAddress marks the server listens using random free port.
|
|
FreePortAddress = graceful.FreePortAddress
|
|
)
|
|
|
|
const (
|
|
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
|
|
)
|
|
|
|
const (
|
|
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"
|
|
)
|
|
|
|
const (
|
|
exceptionExit internalPanic = "exit"
|
|
exceptionExitAll internalPanic = "exit_all"
|
|
exceptionExitHook internalPanic = "exit_hook"
|
|
)
|
|
|
|
var (
|
|
// methodsMap stores all supported HTTP method.
|
|
// It is used for quick HTTP method searching using map.
|
|
methodsMap = make(map[string]struct{})
|
|
|
|
// checker is used for checking whether the value is nil.
|
|
checker = func(v *Server) bool { return v == nil }
|
|
|
|
// serverMapping stores more than one server instances for current processes.
|
|
// The key is the name of the server, and the value is its instance.
|
|
serverMapping = gmap.NewKVMapWithChecker[string, *Server](checker, true)
|
|
|
|
// serverRunning marks the running server counts.
|
|
// If there is no successful server running or all servers' shutdown, this value is 0.
|
|
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
|
|
},
|
|
}
|
|
// allShutdownChan is the event for all servers have done its serving and exit.
|
|
// It is used for process blocking purpose.
|
|
allShutdownChan = make(chan struct{}, 1000)
|
|
|
|
// serverProcessInitialized is used for lazy initialization for server.
|
|
// The process can only be initialized once.
|
|
serverProcessInitialized = gtype.NewBool()
|
|
|
|
// gracefulEnabled is used for a graceful reload feature, which is false in default.
|
|
gracefulEnabled = false
|
|
|
|
// defaultValueTags are the struct tag names for default value storing.
|
|
defaultValueTags = []string{gtag.DefaultShort, gtag.Default}
|
|
)
|
|
|
|
var (
|
|
// ErrNeedJsonBody is the error that indicates the request body content should be JSON format.
|
|
ErrNeedJsonBody = gerror.NewWithOption(gerror.Option{
|
|
Text: "the request body content should be JSON format",
|
|
Code: gcode.CodeInvalidRequest,
|
|
})
|
|
)
|