Golang中如何清理HTTP处理程序的资源
Golang中如何清理HTTP处理程序的资源 我是Go语言的新手。我使用了一些基于Go的http包的处理程序。Go版本是1.6.x。在处理程序执行之前,有一些过滤器操作,可能会执行诸如速率限制之类的操作。速率限制会进行漏桶检查,因此有一个类似列表的队列,每个处理程序都会检查并将其时间戳注册到Redis中。
现在我知道,如[1]所述,每个处理程序都由Go协程处理。因此我在想,当处理程序完成其执行后,我可以从Redis中清理其相关信息。然而,我在网上查阅后,没有找到文档说明如何执行后置操作。
到目前为止,我找到的唯一信息是[2],其中似乎可以通过启动一个Go协程来使用通道接收通知以进行清理。这是唯一的方法吗?如果是这样,我似乎需要在我所有的端点/处理程序中插入这样的清理函数。如果有100个端点/处理程序,我需要在所有这些处理程序/端点的末尾插入100行代码。有更好的方法吗?谢谢
[1]. https://stackoverflow.com/questions/34386232/why-isnt-this-go-http-server-spawning-a-goroutine-per-request-in-chrome-47/34386358#34386358 [2]. https://gianarb.it/blog/go-http-cleanup-http-connection-terminated
更多关于Golang中如何清理HTTP处理程序的资源的实战教程也可以访问 https://www.itying.com/category-94-b0.html
在处理器执行之前,有一些过滤操作可能会执行诸如速率限制之类的操作。
你是如何在处理器执行之前执行这些操作的?如果是通过某种包装器包裹 goroutine 的方式,你能否在处理器被调用后,添加一个调用来清理 Redis 中的相关数据?
更多关于Golang中如何清理HTTP处理程序的资源的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
我不知道这些处理器资源是如何创建的,但通常这是中间件模式的经典案例:
func middleware(handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
fmt.Println("Pre-Hooks")
handler(w,r)
fmt.Println("Post-Hooks")
}
}
除了使用Redis,还有另一种方法可以实现此功能,例如令牌桶算法。你可以使用这个包:
https://github.com/didip/tollbooth
或者,一个更简单但需要手动实现的方案,可以参考这里: https://www.alexedwards.net/blog/how-to-rate-limit-http-requests
如果有一百个端点/处理器,我需要在所有这些处理器/端点的末尾插入一百行代码。有没有更好的方法来实现这一点?谢谢
在我的网站上,我使用了一种方法。可以说是动态的方式。这个方法已经为一个静态网站服务了几年。端点变量是根据 r.URL.Path 创建的,如果没有对应的 HTML 页面,它会返回一个 404 页面。Web 服务器 这段代码服务大约 50 个端点。
func main() {
fmt.Println("hello world")
}
在Go中清理HTTP处理程序的资源,可以通过中间件模式统一处理。以下是一个示例,展示如何创建中间件来执行后置清理操作:
package main
import (
"context"
"fmt"
"net/http"
"time"
)
// 模拟Redis清理函数
func cleanupFromRedis(requestID string) {
fmt.Printf("清理Redis中的请求记录: %s\n", requestID)
// 这里实现实际的Redis清理逻辑
}
// 清理中间件
func cleanupMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 生成请求ID用于跟踪
requestID := fmt.Sprintf("%d", time.Now().UnixNano())
// 创建自定义ResponseWriter来捕获状态码
rw := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
// 在处理程序执行前可以执行预处理
fmt.Printf("开始处理请求: %s\n", requestID)
// 调用下一个处理程序
next.ServeHTTP(rw, r)
// 在处理程序执行后执行清理
cleanupFromRedis(requestID)
fmt.Printf("完成清理请求: %s\n", requestID)
})
}
// 自定义ResponseWriter
type responseWriter struct {
http.ResponseWriter
statusCode int
}
func (rw *responseWriter) WriteHeader(code int) {
rw.statusCode = code
rw.ResponseWriter.WriteHeader(code)
}
// 示例处理程序
func helloHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
}
// 另一个处理程序示例
func apiHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("API Response"))
}
func main() {
// 创建路由器
mux := http.NewServeMux()
// 注册处理程序
mux.HandleFunc("/hello", helloHandler)
mux.HandleFunc("/api", apiHandler)
// 包装路由器使用清理中间件
handler := cleanupMiddleware(mux)
// 启动服务器
fmt.Println("服务器启动在 :8080")
http.ListenAndServe(":8080", handler)
}
对于更复杂的场景,可以使用context来传递清理函数:
package main
import (
"context"
"fmt"
"net/http"
"time"
)
// 清理函数类型
type CleanupFunc func()
// 上下文键类型
type contextKey string
const cleanupKey contextKey = "cleanupFunc"
// 带清理功能的中间件
func withCleanup(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 创建清理函数列表
var cleanupFuncs []CleanupFunc
// 将清理函数添加到上下文
ctx := context.WithValue(r.Context(), cleanupKey, &cleanupFuncs)
// 创建自定义ResponseWriter
rw := &trackingResponseWriter{
ResponseWriter: w,
statusCode: http.StatusOK,
}
// 调用处理程序
next.ServeHTTP(rw, r.WithContext(ctx))
// 执行所有清理函数
if funcs, ok := ctx.Value(cleanupKey).(*[]CleanupFunc); ok {
for _, cleanup := range *funcs {
cleanup()
}
}
})
}
// 注册清理函数
func registerCleanup(r *http.Request, cleanup CleanupFunc) {
if funcs, ok := r.Context().Value(cleanupKey).(*[]CleanupFunc); ok {
*funcs = append(*funcs, cleanup)
}
}
// 跟踪ResponseWriter
type trackingResponseWriter struct {
http.ResponseWriter
statusCode int
written bool
}
func (rw *trackingResponseWriter) WriteHeader(code int) {
if !rw.written {
rw.statusCode = code
rw.written = true
rw.ResponseWriter.WriteHeader(code)
}
}
func (rw *trackingResponseWriter) Write(b []byte) (int, error) {
if !rw.written {
rw.WriteHeader(http.StatusOK)
}
return rw.ResponseWriter.Write(b)
}
// 示例处理程序使用清理注册
func rateLimitedHandler(w http.ResponseWriter, r *http.Request) {
// 模拟速率限制检查
requestID := fmt.Sprintf("req_%d", time.Now().UnixNano())
// 注册Redis清理函数
registerCleanup(r, func() {
fmt.Printf("清理Redis记录: %s\n", requestID)
// 实际Redis清理逻辑
})
// 注册其他资源清理
registerCleanup(r, func() {
fmt.Printf("清理其他资源: %s\n", requestID)
})
w.Write([]byte("处理完成"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/limited", rateLimitedHandler)
handler := withCleanup(mux)
fmt.Println("服务器启动在 :8080")
http.ListenAndServe(":8080", handler)
}
对于需要确保清理操作执行的情况,可以使用defer在中间件中处理:
func ensureCleanupMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 使用defer确保清理函数执行
defer func() {
if r := recover(); r != nil {
// 即使发生panic也执行清理
cleanupFromRedis("panic_recovery")
panic(r) // 重新抛出panic
}
}()
// 正常处理流程
next.ServeHTTP(w, r)
// 正常清理
cleanupFromRedis("normal_completion")
})
}
这些方法避免了在每个处理程序中重复编写清理代码,通过中间件统一管理资源清理。

