تحديد المعدل (Rate Limiting) في Go (Golang): المكتبات والأنماط وأمثلة التنفيذ
تحديد المعدل (Rate Limiting) هو أحد الآليات الأساسية لحماية والتحكم في الحمل في خدمات الويب الحديثة والهندسة المعمارية للخدمات المصغرة. في نظام Go (Golang) البيئي، توجد عدة أدوات ناضجة لتنفيذ تحديد المعدل، بدءًا من الحزمة المدمجة time وصولاً إلى المكتبات المتخصصة مثل golang.org/x/time/rate. في هذه المقالة، سنشرح بالتفصيل كيفية عمل تحديد المعدل في Go، وما هي المكتبات التي يجب استخدامها، وسنقدم أمثلة عملية على الكود.
ما هو تحديد المعدل (Rate Limiting) ولماذا هو مطلوب؟
تحديد المعدل هو تقنية للتحكم في عدد الطلبات التي يمكن للعميل إرسالها إلى الخادم خلال فترة زمنية محددة. الأهداف الرئيسية:
- الحماية من هجمات DDoS — منع تحميل الخادم الزائد.
- التوزيع العادل للموارد — تقييد العملاء "الجشعين".
- الامتثال لاتفاقيات مستوى الخدمة (SLA) — ضمان التوفر لجميع المستخدمين.
- توفير التكاليف — تقليل الحمل على قواعد البيانات وواجهات API الخارجية.
في Go، يتم تنفيذ تحديد المعدل باستخدام خوارزميات Token Bucket (دلو الرموز) و Leaky Bucket (الدلو المتسرب). الأكثر شيوعًا هو Token Bucket.
مكتبة golang.org/x/time/rate
المكتبة الرئيسية والموصى بها لتحديد المعدل في Go هي golang.org/x/time/rate. وهي تنفذ خوارزمية Token Bucket وتنتمي إلى المجموعة الموسعة لحزم Go (sub-repositories).
التثبيت:
go get golang.org/x/time/rateالمكونات الأساسية
rate.Limiter— كائن المحدد الرئيسي.rate.NewLimiter(r rate.Limit, b int)— ينشئ محددًا بمعدلrطلب في الثانية وحجم دلوb.Allow()— يتحقق مما إذا كان يمكن تنفيذ الطلب الآن (غير محظور).Wait(ctx)— يحظر التنفيذ حتى يتوفر رمز مميز (يراعي السياق).Reserve()— يُرجعReservation، مما يسمح بتأجيل التنفيذ.
مثال 1: محدد معدل أساسي
package main
import ( "fmt" "golang.org/x/time/rate" "time")
func main() { // طلبان في الثانية، دلو بسعة 5 رموز limiter := rate.NewLimiter(2, 5)
for i := 0; i < 10; i++ { if limiter.Allow() { fmt.Printf("تم تنفيذ الطلب %d", i) } else { fmt.Printf("تم رفض الطلب %d (حد)", i) } time.Sleep(200 * time.Millisecond) }}مثال 2: محدد معدل مع الانتظار (Wait)
package main
import ( "context" "fmt" "golang.org/x/time/rate" "time")
func main() { limiter := rate.NewLimiter(1, 3) // طلب واحد في الثانية، دلو بسعة 3 ctx := context.Background()
for i := 0; i < 6; i++ { err := limiter.Wait(ctx) if err != nil { fmt.Println("خطأ في الانتظار:", err) return } fmt.Printf("تم تنفيذ الطلب %d في %s", i, time.Now().Format("15:04:05.000")) }}تنفيذ HTTP middleware مع تحديد المعدل
عمليًا، يتم تطبيق تحديد المعدل في أغلب الأحيان على مستوى خادم HTTP. لنلقِ نظرة على مثال middleware لـ Gin أو net/http القياسي.
package main
import ( "net/http" "golang.org/x/time/rate")
// RateLimiterMiddleware يُرجع middleware مع تحديد معدلfunc RateLimiterMiddleware(r rate.Limit, b int) func(http.Handler) http.Handler { limiter := rate.NewLimiter(r, b) return func(next http.Handler) http.Handler { return http.HandlerFunc(f