
在构建某些 web 服务时,我们可能需要根据业务逻辑在程序运行时动态地注册或注销 http 处理器。例如,一个管理系统可能需要根据用户操作创建或删除特定的资源,并为这些资源动态生成对应的 api 路由。go 语言的标准库 net/http 提供了 http.handle 和 http.handlefunc 方法来注册处理器,但它没有提供直接的注销机制。
标准库中的 http.ServeMux 结构体维护着一个私有的 m 字段(map[string]muxEntry),用于存储路径模式到处理器的映射。由于 m 是私有字段,我们无法直接访问或修改它来移除已注册的处理器。这就要求我们寻找一种替代方案来实现动态注销。
解决这个问题的核心思路是创建一个自定义的 ServeMux 实现。这个自定义的 ServeMux 将模仿标准库 http.ServeMux 的内部机制,包括路径匹配、处理器存储和并发安全,并在此基础上增加一个 Deregister 方法。
我们将创建一个 MyMux 结构体,它包含一个用于存储处理器映射的 map 和一个 sync.RWMutex 来确保并发安全。
package main
import (
"fmt"
"net/http"
"strings"
"sync"
)
// muxEntry 存储处理器和对应的模式
type muxEntry struct {
h http.Handler
pattern string
}
// MyMux 结构体,自定义的 HTTP 请求多路复用器
type MyMux struct {
mu sync.RWMutex // 读写锁,保护 m 字段的并发访问
m map[string]muxEntry // 存储路径模式到处理器的映射
hosts bool // 标记是否存在带有主机名的模式
// 默认处理器,当没有匹配的路径时使用
NotFoundHandler http.Handler
}
// NewMyMux 创建并返回一个 MyMux 实例
func NewMyMux() *MyMux {
return &MyMux{
m: make(map[string]muxEntry),
NotFoundHandler: http.NotFoundHandler(), // 默认使用 http.NotFoundHandler
}
}MyMux 的 Handle 方法将与 http.ServeMux 的行为保持一致,负责将路径模式与处理器关联起来。需要注意的是,标准库 ServeMux 会自动为 /foo 和 /foo/ 这样的路径模式进行关联。我们的 Handle 方法也应模拟此行为。
// Handle 注册一个处理器,与 http.ServeMux 的 Handle 方法类似
func (mux *MyMux) Handle(pattern string, handler http.Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()
if pattern == "" {
panic("http: invalid pattern")
}
if handler == nil {
panic("http: nil handler")
}
if mux.m[pattern].h != nil {
panic("http: multiple registrations for " + pattern)
}
if pattern[0] != '/' {
mux.hosts = true
}
mux.m[pattern] = muxEntry{h: handler, pattern: pattern}
// 模拟 http.ServeMux 的行为:如果注册了 /foo,也会自动处理 /foo/
if pattern[len(pattern)-1] == '/' && len(pattern) > 1 {
// 如果注册了 /path/,也为 /path 注册
if mux.m[pattern[:len(pattern)-1]].h == nil {
mux.m[pattern[:len(pattern)-1]] = muxEntry{h: handler, pattern: pattern[:len(pattern)-1]}
}
} else if pattern[len(pattern)-1] != '/' {
// 如果注册了 /path,也为 /path/ 注册
if mux.m[pattern+"/"] == (muxEntry{}) { // 使用空结构体判断是否已注册
mux.m[pattern+"/"] = muxEntry{h: handler, pattern: pattern + "/"}
}
}
}Deregister 方法是我们的核心功能。它负责从 m 映射中删除指定的处理器。同样,为了与 Handle 方法的行为保持一致,当注销 /foo 时,也应同时注销 /foo/。
// Deregister 注销一个处理器
func (mux *MyMux) Deregister(pattern string) error {
mux.mu.Lock()
defer mux.mu.Unlock()
if _, ok := mux.m[pattern]; !ok {
return fmt.Errorf("pattern %s not registered", pattern)
}
delete(mux.m, pattern)
// 模拟 http.ServeMux 的行为:如果注销了 /foo,也尝试注销 /foo/
if pattern[len(pattern)-1] == '/' && len(pattern) > 1 {
delete(mux.m, pattern[:len(pattern)-1])
} else if pattern[len(pattern)-1] != '/' {
delete(mux.m, pattern+"/")
}
return nil
}MyMux 需要实现 http.Handler 接口,即 ServeHTTP 方法。这个方法负责接收传入的请求,查找匹配的处理器并调用其 ServeHTTP 方法。路径匹配逻辑是 ServeMux 的核心,它涉及路径清理(cleanPath)和匹配算法(match)。为了与标准库的行为保持一致,我们需要复制或重新实现这些逻辑。
// ServeHTTP 实现 http
以上就是Go net/http 运行时动态注销处理器教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号