调整工具类的方法,优化方法调用及增加迁移工具及其用法
This commit is contained in:
@@ -2,12 +2,15 @@
|
||||
|
||||
## 概述
|
||||
|
||||
中间件工具提供了常用的HTTP中间件功能,包括CORS处理和时区管理。
|
||||
中间件工具提供了常用的HTTP中间件功能,包括CORS处理、时区管理、请求日志、Panic恢复和限流等。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- **CORS中间件**:支持跨域资源共享配置
|
||||
- **时区中间件**:从请求头读取时区信息,支持默认时区设置
|
||||
- **日志中间件**:自动记录每个HTTP请求的详细信息
|
||||
- **Recovery中间件**:捕获panic并恢复,防止服务崩溃
|
||||
- **限流中间件**:基于令牌桶算法的请求限流
|
||||
- **中间件链**:提供便捷的中间件链式调用
|
||||
|
||||
## CORS中间件
|
||||
@@ -233,6 +236,371 @@ X-Timezone: Asia/Shanghai
|
||||
3. 时区信息存储在context中,可以在整个请求生命周期中使用
|
||||
4. 建议在CORS配置中包含 `X-Timezone` 请求头
|
||||
|
||||
## 日志中间件
|
||||
|
||||
### 功能说明
|
||||
|
||||
日志中间件用于自动记录每个HTTP请求的详细信息,帮助监控和调试应用。
|
||||
|
||||
记录内容包括:
|
||||
- 请求方法、路径、查询参数
|
||||
- 响应状态码、响应大小
|
||||
- 请求处理时间(毫秒)
|
||||
- 客户端IP地址(支持X-Forwarded-For)
|
||||
- User-Agent、Referer等信息
|
||||
|
||||
### 使用方法
|
||||
|
||||
#### 基本使用(使用默认logger)
|
||||
|
||||
```go
|
||||
import (
|
||||
"net/http"
|
||||
"git.toowon.com/jimmy/go-common/middleware"
|
||||
)
|
||||
|
||||
func main() {
|
||||
chain := middleware.NewChain(
|
||||
middleware.Logging(nil), // 使用默认配置
|
||||
middleware.CORS(),
|
||||
middleware.Timezone,
|
||||
)
|
||||
|
||||
handler := chain.ThenFunc(apiHandler)
|
||||
http.Handle("/api", handler)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用自定义logger
|
||||
|
||||
```go
|
||||
import (
|
||||
"git.toowon.com/jimmy/go-common/middleware"
|
||||
"git.toowon.com/jimmy/go-common/logger"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 创建自定义logger(异步模式,输出到文件)
|
||||
loggerConfig := &logger.LoggerConfig{
|
||||
Level: "info",
|
||||
Output: "file",
|
||||
FilePath: "./logs/app.log",
|
||||
Async: true, // 异步模式,不阻塞请求
|
||||
BufferSize: 1000,
|
||||
}
|
||||
myLogger, _ := logger.NewLogger(loggerConfig)
|
||||
|
||||
// 配置日志中间件
|
||||
loggingConfig := &middleware.LoggingConfig{
|
||||
Logger: myLogger,
|
||||
SkipPaths: []string{"/health", "/metrics"}, // 跳过健康检查接口
|
||||
}
|
||||
|
||||
chain := middleware.NewChain(
|
||||
middleware.Logging(loggingConfig),
|
||||
middleware.CORS(),
|
||||
middleware.Timezone,
|
||||
)
|
||||
|
||||
handler := chain.ThenFunc(apiHandler)
|
||||
http.Handle("/api", handler)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
```
|
||||
|
||||
### LoggingConfig 配置说明
|
||||
|
||||
| 字段 | 类型 | 说明 | 默认值 |
|
||||
|------|------|------|--------|
|
||||
| Logger | *logger.Logger | 日志记录器 | 创建默认logger(stdout) |
|
||||
| SkipPaths | []string | 跳过记录的路径列表 | [] |
|
||||
| LogRequestBody | bool | 是否记录请求体(慎用) | false |
|
||||
| LogResponseBody | bool | 是否记录响应体(慎用) | false |
|
||||
|
||||
### 日志输出示例
|
||||
|
||||
```
|
||||
[INFO] HTTP Request method=GET path=/api/users query= status=200 size=1024 duration=45 ip=192.168.1.100 user_agent=Mozilla/5.0 referer=
|
||||
[WARN] HTTP Request method=POST path=/api/users query= status=400 size=128 duration=12 ip=192.168.1.100 user_agent=PostmanRuntime/7.29
|
||||
[ERROR] HTTP Request method=GET path=/api/error query= status=500 size=256 duration=89 ip=192.168.1.100 user_agent=curl/7.64.1 referer=
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. **异步模式推荐**:生产环境建议使用异步logger,避免日志写入阻塞请求
|
||||
2. **跳过路径**:健康检查、监控接口等高频接口建议跳过日志记录
|
||||
3. **日志级别**:根据状态码自动选择日志级别(5xx=ERROR, 4xx=WARN, 2xx-3xx=INFO)
|
||||
4. **客户端IP**:自动从X-Forwarded-For、X-Real-IP或RemoteAddr获取真实IP
|
||||
|
||||
## Recovery中间件
|
||||
|
||||
### 功能说明
|
||||
|
||||
Recovery中间件用于捕获HTTP处理过程中的panic,防止panic导致整个服务崩溃。
|
||||
|
||||
功能包括:
|
||||
- 捕获panic并恢复服务
|
||||
- 记录panic信息和堆栈跟踪
|
||||
- 返回500错误响应
|
||||
- 支持自定义错误处理
|
||||
|
||||
### 使用方法
|
||||
|
||||
#### 基本使用(使用默认配置)
|
||||
|
||||
```go
|
||||
import (
|
||||
"net/http"
|
||||
"git.toowon.com/jimmy/go-common/middleware"
|
||||
)
|
||||
|
||||
func main() {
|
||||
chain := middleware.NewChain(
|
||||
middleware.Recovery(nil), // 使用默认配置
|
||||
middleware.Logging(nil),
|
||||
middleware.CORS(),
|
||||
middleware.Timezone,
|
||||
)
|
||||
|
||||
handler := chain.ThenFunc(apiHandler)
|
||||
http.Handle("/api", handler)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
```
|
||||
|
||||
#### 使用自定义logger
|
||||
|
||||
```go
|
||||
import (
|
||||
"git.toowon.com/jimmy/go-common/middleware"
|
||||
"git.toowon.com/jimmy/go-common/logger"
|
||||
)
|
||||
|
||||
func main() {
|
||||
myLogger, _ := logger.NewLogger(nil)
|
||||
|
||||
recoveryConfig := &middleware.RecoveryConfig{
|
||||
Logger: myLogger,
|
||||
EnableStackTrace: true, // 启用堆栈跟踪
|
||||
}
|
||||
|
||||
chain := middleware.NewChain(
|
||||
middleware.Recovery(recoveryConfig),
|
||||
middleware.Logging(nil),
|
||||
)
|
||||
|
||||
handler := chain.ThenFunc(apiHandler)
|
||||
http.Handle("/api", handler)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
```
|
||||
|
||||
#### 自定义错误响应
|
||||
|
||||
```go
|
||||
import (
|
||||
"net/http"
|
||||
commonhttp "git.toowon.com/jimmy/go-common/http"
|
||||
"git.toowon.com/jimmy/go-common/middleware"
|
||||
)
|
||||
|
||||
func main() {
|
||||
recoveryConfig := &middleware.RecoveryConfig{
|
||||
EnableStackTrace: true,
|
||||
CustomHandler: func(w http.ResponseWriter, r *http.Request, err interface{}) {
|
||||
// 使用统一的JSON响应格式
|
||||
h := commonhttp.NewHandler(w, r)
|
||||
h.SystemError("服务器内部错误")
|
||||
},
|
||||
}
|
||||
|
||||
chain := middleware.NewChain(
|
||||
middleware.Recovery(recoveryConfig),
|
||||
)
|
||||
|
||||
handler := chain.ThenFunc(apiHandler)
|
||||
http.Handle("/api", handler)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
```
|
||||
|
||||
### RecoveryConfig 配置说明
|
||||
|
||||
| 字段 | 类型 | 说明 | 默认值 |
|
||||
|------|------|------|--------|
|
||||
| Logger | *logger.Logger | 日志记录器 | 创建默认logger |
|
||||
| EnableStackTrace | bool | 是否记录堆栈跟踪 | true |
|
||||
| CustomHandler | func(...) | 自定义错误处理函数 | nil(返回500文本) |
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. **放在最外层**:Recovery中间件应该放在中间件链的最前面,以捕获所有panic
|
||||
2. **日志记录**:建议配置logger,确保panic信息被记录下来
|
||||
3. **堆栈跟踪**:生产环境建议启用,方便排查问题
|
||||
4. **自定义响应**:可以自定义错误响应格式,统一错误处理
|
||||
|
||||
## 限流中间件
|
||||
|
||||
### 功能说明
|
||||
|
||||
限流中间件用于限制请求频率,防止API被滥用或遭受攻击。
|
||||
|
||||
特性:
|
||||
- 基于令牌桶算法
|
||||
- 支持按IP、用户ID等维度限流
|
||||
- 自动设置限流响应头
|
||||
- 内存存储,自动清理过期数据
|
||||
|
||||
### 使用方法
|
||||
|
||||
#### 基本使用(默认配置:100请求/分钟)
|
||||
|
||||
```go
|
||||
import (
|
||||
"net/http"
|
||||
"git.toowon.com/jimmy/go-common/middleware"
|
||||
)
|
||||
|
||||
func main() {
|
||||
chain := middleware.NewChain(
|
||||
middleware.Recovery(nil),
|
||||
middleware.Logging(nil),
|
||||
middleware.RateLimit(nil), // 默认100请求/分钟,按IP限流
|
||||
middleware.CORS(),
|
||||
)
|
||||
|
||||
handler := chain.ThenFunc(apiHandler)
|
||||
http.Handle("/api", handler)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
```
|
||||
|
||||
#### 自定义限流规则
|
||||
|
||||
```go
|
||||
import (
|
||||
"time"
|
||||
"git.toowon.com/jimmy/go-common/middleware"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 创建限流器:10请求/分钟
|
||||
limiter := middleware.NewTokenBucketLimiter(10, time.Minute)
|
||||
|
||||
rateLimitConfig := &middleware.RateLimitConfig{
|
||||
Limiter: limiter,
|
||||
}
|
||||
|
||||
chain := middleware.NewChain(
|
||||
middleware.RateLimit(rateLimitConfig),
|
||||
)
|
||||
|
||||
handler := chain.ThenFunc(apiHandler)
|
||||
http.Handle("/api", handler)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
```
|
||||
|
||||
#### 按用户ID限流
|
||||
|
||||
```go
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
"git.toowon.com/jimmy/go-common/middleware"
|
||||
)
|
||||
|
||||
func main() {
|
||||
limiter := middleware.NewTokenBucketLimiter(100, time.Minute)
|
||||
|
||||
rateLimitConfig := &middleware.RateLimitConfig{
|
||||
Limiter: limiter,
|
||||
KeyFunc: func(r *http.Request) string {
|
||||
// 从请求头或JWT token中获取用户ID
|
||||
userID := r.Header.Get("X-User-ID")
|
||||
if userID != "" {
|
||||
return "user:" + userID
|
||||
}
|
||||
// 如果没有用户ID,使用IP
|
||||
return "ip:" + r.RemoteAddr
|
||||
},
|
||||
OnRateLimitExceeded: func(w http.ResponseWriter, r *http.Request, key string) {
|
||||
// 记录限流事件
|
||||
println("Rate limit exceeded for:", key)
|
||||
},
|
||||
}
|
||||
|
||||
chain := middleware.NewChain(
|
||||
middleware.RateLimit(rateLimitConfig),
|
||||
)
|
||||
|
||||
handler := chain.ThenFunc(apiHandler)
|
||||
http.Handle("/api", handler)
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
```
|
||||
|
||||
#### 便捷函数
|
||||
|
||||
```go
|
||||
// 按IP限流:10请求/分钟
|
||||
chain := middleware.NewChain(
|
||||
middleware.RateLimitByIP(10, time.Minute),
|
||||
)
|
||||
|
||||
// 或使用自定义速率
|
||||
chain := middleware.NewChain(
|
||||
middleware.RateLimitWithRate(50, time.Minute),
|
||||
)
|
||||
```
|
||||
|
||||
### RateLimitConfig 配置说明
|
||||
|
||||
| 字段 | 类型 | 说明 | 默认值 |
|
||||
|------|------|------|--------|
|
||||
| Limiter | RateLimiter | 限流器实例 | 100请求/分钟 |
|
||||
| KeyFunc | func(*http.Request) string | 生成限流键的函数 | 使用客户端IP |
|
||||
| OnRateLimitExceeded | func(...) | 限流触发回调 | nil |
|
||||
|
||||
### 响应头说明
|
||||
|
||||
限流中间件会自动设置以下响应头:
|
||||
|
||||
| 响应头 | 说明 |
|
||||
|--------|------|
|
||||
| X-RateLimit-Limit | 窗口期内允许的请求数 |
|
||||
| X-RateLimit-Remaining | 当前窗口剩余配额 |
|
||||
| X-RateLimit-Reset | 配额重置时间(Unix时间戳) |
|
||||
| Retry-After | 限流时,建议重试的等待时间(秒) |
|
||||
|
||||
### 响应示例
|
||||
|
||||
正常请求:
|
||||
```
|
||||
HTTP/1.1 200 OK
|
||||
X-RateLimit-Limit: 100
|
||||
X-RateLimit-Remaining: 95
|
||||
X-RateLimit-Reset: 1640000000
|
||||
```
|
||||
|
||||
触发限流:
|
||||
```
|
||||
HTTP/1.1 429 Too Many Requests
|
||||
X-RateLimit-Limit: 100
|
||||
X-RateLimit-Remaining: 0
|
||||
X-RateLimit-Reset: 1640000000
|
||||
Retry-After: 45
|
||||
Too Many Requests
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. **内存存储**:当前实现使用内存存储,适用于单机部署。如需分布式限流,建议使用Redis
|
||||
2. **键设计**:合理设计限流键,可以按IP、用户、API等维度限流
|
||||
3. **清理机制**:自动清理过期的限流数据,避免内存泄漏
|
||||
4. **算法选择**:使用令牌桶算法,支持突发流量
|
||||
|
||||
## 中间件链
|
||||
|
||||
### 功能说明
|
||||
@@ -277,7 +645,107 @@ handler := chain.ThenFunc(handler)
|
||||
|
||||
## 完整示例
|
||||
|
||||
### 示例1:CORS + 时区中间件
|
||||
### 示例1:完整的生产级中间件配置
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.toowon.com/jimmy/go-common/middleware"
|
||||
"git.toowon.com/jimmy/go-common/logger"
|
||||
commonhttp "git.toowon.com/jimmy/go-common/http"
|
||||
"git.toowon.com/jimmy/go-common/datetime"
|
||||
)
|
||||
|
||||
func apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||
h := commonhttp.NewHandler(w, r)
|
||||
|
||||
// 从Handler获取时区
|
||||
timezone := h.GetTimezone()
|
||||
now := datetime.Now(timezone)
|
||||
|
||||
h.Success(map[string]interface{}{
|
||||
"message": "Hello",
|
||||
"timezone": timezone,
|
||||
"time": datetime.FormatDateTime(now),
|
||||
})
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 1. 配置logger(异步模式,输出到文件)
|
||||
loggerConfig := &logger.LoggerConfig{
|
||||
Level: "info",
|
||||
Output: "both", // 同时输出到stdout和文件
|
||||
FilePath: "./logs/app.log",
|
||||
Async: true,
|
||||
BufferSize: 1000,
|
||||
}
|
||||
myLogger, err := logger.NewLogger(loggerConfig)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer myLogger.Close() // 确保程序退出时关闭logger
|
||||
|
||||
// 2. 配置CORS
|
||||
corsConfig := &middleware.CORSConfig{
|
||||
AllowedOrigins: []string{"*"},
|
||||
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
||||
AllowedHeaders: []string{"Content-Type", "Authorization", "X-Timezone"},
|
||||
}
|
||||
|
||||
// 3. 配置日志中间件
|
||||
loggingConfig := &middleware.LoggingConfig{
|
||||
Logger: myLogger,
|
||||
SkipPaths: []string{"/health", "/metrics"},
|
||||
}
|
||||
|
||||
// 4. 配置Recovery中间件
|
||||
recoveryConfig := &middleware.RecoveryConfig{
|
||||
Logger: myLogger,
|
||||
EnableStackTrace: true,
|
||||
CustomHandler: func(w http.ResponseWriter, r *http.Request, err interface{}) {
|
||||
h := commonhttp.NewHandler(w, r)
|
||||
h.SystemError("服务器内部错误")
|
||||
},
|
||||
}
|
||||
|
||||
// 5. 配置限流中间件(100请求/分钟)
|
||||
rateLimiter := middleware.NewTokenBucketLimiter(100, time.Minute)
|
||||
rateLimitConfig := &middleware.RateLimitConfig{
|
||||
Limiter: rateLimiter,
|
||||
OnRateLimitExceeded: func(w http.ResponseWriter, r *http.Request, key string) {
|
||||
myLogger.Warnf(map[string]interface{}{
|
||||
"key": key,
|
||||
"path": r.URL.Path,
|
||||
}, "Rate limit exceeded")
|
||||
},
|
||||
}
|
||||
|
||||
// 6. 创建中间件链(顺序很重要!)
|
||||
chain := middleware.NewChain(
|
||||
middleware.Recovery(recoveryConfig), // 最外层:捕获panic
|
||||
middleware.Logging(loggingConfig), // 日志记录
|
||||
middleware.RateLimit(rateLimitConfig), // 限流
|
||||
middleware.CORS(corsConfig), // CORS处理
|
||||
middleware.Timezone, // 时区处理
|
||||
)
|
||||
|
||||
// 7. 应用中间件
|
||||
http.Handle("/api", chain.ThenFunc(apiHandler))
|
||||
http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte("OK"))
|
||||
})
|
||||
|
||||
log.Println("Server started on :8080")
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
```
|
||||
|
||||
### 示例2:基础的CORS + 时区中间件
|
||||
|
||||
```go
|
||||
package main
|
||||
@@ -306,23 +774,13 @@ func apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 配置CORS
|
||||
corsConfig := &middleware.CORSConfig{
|
||||
AllowedOrigins: []string{"*"},
|
||||
AllowedMethods: []string{"GET", "POST", "OPTIONS"},
|
||||
AllowedHeaders: []string{"Content-Type", "Authorization", "X-Timezone"},
|
||||
}
|
||||
|
||||
// 创建中间件链
|
||||
// 创建简单的中间件链
|
||||
chain := middleware.NewChain(
|
||||
middleware.CORS(corsConfig),
|
||||
middleware.Timezone,
|
||||
middleware.CORS(nil), // 使用默认CORS配置
|
||||
middleware.Timezone, // 使用默认时区
|
||||
)
|
||||
|
||||
// 应用中间件
|
||||
handler := chain.ThenFunc(apiHandler)
|
||||
|
||||
http.Handle("/api", handler)
|
||||
http.Handle("/api", chain.ThenFunc(apiHandler))
|
||||
|
||||
log.Println("Server started on :8080")
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
@@ -408,6 +866,65 @@ func getPosts(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
从context中获取时区。
|
||||
|
||||
### 日志中间件
|
||||
|
||||
#### Logging(config *LoggingConfig) func(http.Handler) http.Handler
|
||||
|
||||
创建日志中间件。
|
||||
|
||||
**参数:**
|
||||
- `config`: 日志配置,nil则使用默认配置
|
||||
|
||||
**返回:** 中间件函数
|
||||
|
||||
### Recovery中间件
|
||||
|
||||
#### Recovery(config *RecoveryConfig) func(http.Handler) http.Handler
|
||||
|
||||
创建Recovery中间件。
|
||||
|
||||
**参数:**
|
||||
- `config`: Recovery配置,nil则使用默认配置
|
||||
|
||||
**返回:** 中间件函数
|
||||
|
||||
#### RecoveryWithLogger(log *logger.Logger) func(http.Handler) http.Handler
|
||||
|
||||
使用指定logger的Recovery中间件(便捷函数)。
|
||||
|
||||
#### RecoveryWithCustomHandler(customHandler func(...)) func(http.Handler) http.Handler
|
||||
|
||||
使用自定义错误处理的Recovery中间件(便捷函数)。
|
||||
|
||||
### 限流中间件
|
||||
|
||||
#### RateLimit(config *RateLimitConfig) func(http.Handler) http.Handler
|
||||
|
||||
创建限流中间件。
|
||||
|
||||
**参数:**
|
||||
- `config`: 限流配置,nil则使用默认配置(100请求/分钟)
|
||||
|
||||
**返回:** 中间件函数
|
||||
|
||||
#### NewTokenBucketLimiter(rate int, windowSize time.Duration) RateLimiter
|
||||
|
||||
创建令牌桶限流器。
|
||||
|
||||
**参数:**
|
||||
- `rate`: 每个窗口期允许的请求数
|
||||
- `windowSize`: 窗口大小
|
||||
|
||||
**返回:** 限流器实例
|
||||
|
||||
#### RateLimitWithRate(rate int, windowSize time.Duration) func(http.Handler) http.Handler
|
||||
|
||||
使用指定速率创建限流中间件(便捷函数)。
|
||||
|
||||
#### RateLimitByIP(rate int, windowSize time.Duration) func(http.Handler) http.Handler
|
||||
|
||||
按IP限流(便捷函数)。
|
||||
|
||||
### 中间件链
|
||||
|
||||
#### NewChain(middlewares ...func(http.Handler) http.Handler) *Chain
|
||||
@@ -428,19 +945,63 @@ func getPosts(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **CORS配置**:
|
||||
- 生产环境建议明确指定允许的源,避免使用 "*"
|
||||
- 如果使用凭证(cookies),必须明确指定源,不能使用 "*"
|
||||
### 1. CORS配置
|
||||
- 生产环境建议明确指定允许的源,避免使用 "*"
|
||||
- 如果使用凭证(cookies),必须明确指定源,不能使用 "*"
|
||||
- CORS中间件应该在Recovery和Logging之后,以便正确处理预检请求
|
||||
|
||||
2. **时区处理**:
|
||||
- 时区信息存储在context中,确保中间件在处理器之前执行
|
||||
- 时区验证失败时会自动回退到默认时区,不会返回错误
|
||||
### 2. 时区处理
|
||||
- 时区信息存储在context中,确保中间件在处理器之前执行
|
||||
- 时区验证失败时会自动回退到默认时区,不会返回错误
|
||||
- 建议在CORS配置中包含 `X-Timezone` 请求头
|
||||
|
||||
3. **中间件顺序**:
|
||||
- CORS中间件应该放在最外层,以便处理预检请求
|
||||
- 时区中间件可以放在CORS之后
|
||||
### 3. 日志记录
|
||||
- **生产环境推荐异步模式**:避免日志写入阻塞请求,提升性能
|
||||
- **跳过高频接口**:健康检查、监控接口等高频接口建议跳过日志
|
||||
- **日志轮转**:使用文件输出时,建议配合日志轮转工具(如logrotate)
|
||||
- **敏感信息**:不要记录请求体和响应体,避免泄露敏感信息
|
||||
|
||||
4. **性能考虑**:
|
||||
- CORS预检请求会被缓存,减少重复请求
|
||||
- 时区验证只在请求头存在时进行,性能影响很小
|
||||
### 4. Panic恢复
|
||||
- **放在最外层**:Recovery中间件应该放在中间件链的最前面
|
||||
- **记录日志**:务必配置logger,确保panic信息被记录
|
||||
- **监控告警**:建议将panic事件接入监控系统,及时发现问题
|
||||
- **堆栈跟踪**:生产环境建议启用,方便排查问题
|
||||
|
||||
### 5. 限流配置
|
||||
- **合理设置阈值**:根据实际业务需求设置限流阈值
|
||||
- **分布式部署**:当前实现使用内存存储,适用于单机。分布式部署建议使用Redis
|
||||
- **键设计**:合理设计限流键,可以按IP、用户、API等维度限流
|
||||
- **响应头**:客户端可以根据X-RateLimit-*响应头实现智能重试
|
||||
|
||||
### 6. 中间件顺序(推荐)
|
||||
建议的中间件顺序(从外到内):
|
||||
1. **Recovery** - 最外层,捕获所有panic
|
||||
2. **Logging** - 记录所有请求(包括限流的请求)
|
||||
3. **RateLimit** - 限流保护
|
||||
4. **CORS** - 处理跨域
|
||||
5. **Timezone** - 时区处理
|
||||
6. **业务中间件** - 认证、授权等
|
||||
|
||||
```go
|
||||
chain := middleware.NewChain(
|
||||
middleware.Recovery(recoveryConfig), // 1. Panic恢复
|
||||
middleware.Logging(loggingConfig), // 2. 日志记录
|
||||
middleware.RateLimit(rateLimitConfig), // 3. 限流
|
||||
middleware.CORS(corsConfig), // 4. CORS
|
||||
middleware.Timezone, // 5. 时区
|
||||
)
|
||||
```
|
||||
|
||||
### 7. 性能考虑
|
||||
- **异步日志**:使用异步logger,避免IO阻塞
|
||||
- **限流算法**:令牌桶算法支持突发流量
|
||||
- **自动清理**:限流数据会自动清理,避免内存泄漏
|
||||
- **跳过路径**:合理使用SkipPaths,减少不必要的处理
|
||||
|
||||
### 8. 生产环境建议
|
||||
- 使用异步logger,配置日志文件和轮转
|
||||
- 启用Recovery中间件,配置告警
|
||||
- 根据业务设置合理的限流阈值
|
||||
- 配置监控指标(请求量、错误率、限流触发次数等)
|
||||
- 定期review日志,优化性能瓶颈
|
||||
|
||||
|
||||
Reference in New Issue
Block a user