这里提供一个使用 golang 实现限制每天 http 请求次数的方法,并进行封装的示例代码:
package main
import (
"fmt"
"net/http"
"sync"
"time"
)
// 定义 HTTP 请求处理函数
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, world!")
}
// 封装每天最大访问次数控制方法
func LimitDailyRequest(maxCount int, fn http.HandlerFunc) http.HandlerFunc {
// 定义一个读写并发安全的映射对象
var m sync.Map
// 返回一个新的 HTTP 请求处理函数,用于在原有请求处理基础上增加每天最大访问次数限制功能
return func(w http.ResponseWriter, r *http.Request) {
// 获取当前时间和当天日期字符串(例如20220101)
nowTime := time.Now()
todayStr := nowTime.Truncate(24*time.Hour).Format("20060102")
// 构建已处理请求数量、上一次请求时间和访问计数器的 key
countKey := fmt.Sprintf("%s:%s", "requests", todayStr)
timeKey := fmt.Sprintf("%s:%s", "last_request_time", todayStr)
counterKey := fmt.Sprintf("%s:%s", "counter", todayStr)
// 使用读写锁来保证并发安全性
var mu sync.RWMutex
mu.RLock()
defer mu.RUnlock()
// 从映射中获取已处理请求数量、上一次请求时间和访问计数器,并判断是否超出限制
currentCount, found := m.LoadOrStore(countKey, 0) // 如果不存在,则设置为0;否则返回已有值
lastRequestTime, found := m.LoadOrStore(timeKey, nowTime.Add(-time.Second)) // 如果不存在,则设置为当前时间的前一秒;否则返回已有值
counter, found := m.LoadOrStore(counterKey, 0)
// 判断是否超出限制
if currentCount.(int) >= maxCount {
http.Error(w, "Exceeding the maximum number of requests per day", http.StatusTooManyRequests)
return // 超出限制直接返回响应,不再执行原有请求处理函数
} else {
// 增加访问计数器,并更新本次请求时间和已处理请求数量到映射中
mu.Lock()
defer mu.Unlock()
m.Store(countKey, currentCount.(int)+1)
m.Store(timeKey, nowTime)
m.Store(counterKey, counter.(int)+1)
fn(w, r) // 执行原有请求处理函数
// 打印当前已处理请求数量和剩余请求数量(可选)
fmt.Printf("今日已处理 %d 次,请求数量剩余 %d\n", currentCount.(int)+1, maxCount-currentCount.(int)-1)
}
}
}
func main() {
// 创建一个 HTTP 服务器并注册 HTTP 请求处理函数(使用 LimitDailyRequest 封装后的新函数)
http.HandleFunc("/", LimitDailyRequest(20, handler))
// 启动 HTTP 服务器并监听在本地 8080 端口上
http.ListenAndServe(":8080", nil)
}
使用 LimitDailyRequest
封装 HTTP 请求处理函数后,每个客户端每天最多只能访问该接口指定的次数。如果超出限制,则会直接返回响应,并不再执行原有请求处理函数。同时,该封装方法是线程安全的,可以保证在高并发情况下仍能正确地统计和限制请求次数。