[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"academy-blogs-th-1-1-all-golang-rate-limiting-ai-requests-all--*":3,"academy-blog-translations-rzql5nsqf8oaqxv":91},{"data":4,"page":77,"perPage":77,"totalItems":77,"totalPages":77},[5],{"alt":6,"collectionId":7,"collectionName":8,"content":9,"cover_image":10,"cover_image_path":11,"created":12,"created_by":13,"expand":14,"id":85,"keywords":86,"locale":57,"published_at":87,"scheduled_at":73,"school_blog":81,"short_description":88,"status":79,"title":89,"updated":90,"updated_by":13,"slug":82,"views":84},"กลไกอัลกอริทึม Token Bucket สำหรับระบบ Rate Limiting ด้วยภาษา Go","sclblg987654321","school_blog_translations","\u003Cp>สวัสดีครับชาว Gophers! ตอนที่แล้วเราวางระบบ Cache เพื่อลดภาระงานซ้ำและเซฟค่า Token ไปได้เยอะ แต่ในความเป็นจริงของระบบที่มีผู้ใช้งานจำนวนมาก ความเสี่ยงหลักที่เราต้องเจอเสมอคือเรื่องการใช้งานเกินขีดจำกัด (Abuse &amp; Overuse) ครับ\u003C\u002Fp>\u003Cp>ลองนึกภาพว่ามียูสเซอร์บางคนเขียนสคริปต์ลูปคำถามส่งมาหา AI รัวๆ หรือระบบภายในตัวอื่นเกิดบั๊กแล้วยิงถล่ม API เข้ามาหลักร้อยครั้งในหนึ่งวินาที สิ่งที่จะเกิดขึ้นคือเราจะโดนผู้ให้บริการภายนอกอย่าง OpenAI หรือค่ายอื่นๆ สั่งระงับการเข้าถึงชั่วคราวทันทีเพราะยิงเกินขีดจำกัด RPM (Requests Per Minute) ส่งผลให้ผู้ใช้อื่นในองค์กรใช้งานไม่ได้ไปด้วย หรือถ้าเราใช้โมเดล Open-source ที่รันภายในองค์กรเอง การสแปมแบบนี้จะทำให้หน่วยความจำการ์ดจอเต็ม (VRAM Out-of-Memory) และระบบพังลงมาทันที\u003C\u002Fp>\u003Cp>เพื่อปกป้องเซิร์ฟเวอร์และกระจายทรัพยากรการประมวลผลอย่างเท่าเทียม เราจำเป็นต้องวางระบบจำกัดความเร็ว หรือ \u003Cstrong>Rate Limiter Middleware\u003C\u002Fstrong> มาคั่นไว้ที่หน้าประตูเซิร์ฟเวอร์ Go ของเราครับ\u003C\u002Fp>\u003Ch2>กลไกการทำงานของ Token Bucket Algorithm\u003C\u002Fh2>\u003Cp>อัลกอริทึมที่นิยมและมีประสิทธิภาพสูงในภาษา Go สำหรับทำ Rate Limiting คือ \u003Cstrong>Token Bucket (ถังโทเคน)\u003C\u002Fstrong> หลักการคือระบบจะมีถังเก็บโทเคนขนาดคงที่ (เช่น จุได้สูงสุด 3 ใบ) และมี Background Job คอยเติมโทเคนเข้าไปเรื่อยๆ ตามอัตราที่กำหนด (เช่น วินาทีละ 1 ใบ)\u003C\u002Fp>\u003Cp>เมื่อมี Request เข้ามา ระบบจะเช็กว่ามีโทเคนเหลือในถังไหม ถ้ามี ก็จะหยิบออกไป 1 ใบแล้วปล่อยให้ผ่านไปหา AI Handler ได้ แต่ถ้าถังว่างเปล่า ระบบจะปฏิเสธและตีกลับ HTTP 429 Too Many Requests ทันที ข้อดีของวิธีนี้คือ สามารถรองรับการยิงกระชากแบบกะทันหัน (Bursting) ได้ตามขนาดความจุสูงสุดของถังที่เราตั้งไว้ครับ\u003C\u002Fp>\u003Cp>Plaintext\u003C\u002Fp>\u003Cpre>\u003Ccode>[ เติม Token อัตราคงที่ ] -&gt; (เช่น 1 ใบ \u002F วินาที)\n                                   │\n                                   ▼\n                        ┌──────────────────┐\n                        │  Bucket (Max=3)  │  &lt;- ถังเก็บโทเคนสะสม\n                        └──────────────────┘\n                                   │\n       [มี Request เข้ามา] --------┘\n               │\n               ▼\n     (มี Token เหลือในถัง?)\n      ├───&gt; ใช่ ───&gt; หยิบออก 1 ใบ ───&gt; ทะลุเข้าสู่ AI Handler\n      └───&gt; ไม่ ───&gt; ปฏิเสธ Request ──&gt; ตอบกลับ HTTP 429 Too Many Requests\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch2>การติดตั้ง Dependency\u003C\u002Fh2>\u003Cp>เราจะเลือกใช้แพ็กเกจย่อยอย่างเป็นทางการของทีมงาน Go นามว่า \u003Ccode>x\u002Ftime\u002Frate\u003C\u002Fcode> ซึ่งออกแบบและจัดการโครงสร้างให้ปลอดภัยต่อการทำงานแบบหลายเธรด (\u003Cstrong>Thread-safe\u003C\u002Fstrong>) มาให้เรียบร้อยแล้ว:\u003C\u002Fp>\u003Cp>Bash\u003C\u002Fp>\u003Cpre>\u003Ccode>go get golang.org\u002Fx\u002Ftime\u002Frate\ngo get github.com\u002Fgin-gonic\u002Fgin\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch2>โครงสร้างข้อมูลและระบบจัดการ Rate Limiter ราย Client\u003C\u002Fh2>\u003Cp>เราจะแยกส่วนประกอบออกเป็นโมดูลเพื่อความสะอาดของโค้ด พร้อมเพิ่มกลไก \u003Ccode>sync.RWMutex\u003C\u002Fcode> เพื่ออุดรอยรั่วเรื่อง Data Race ระหว่างฟังก์ชันหลักกับโปรแกรมทำความสะอาดคีย์ขยะหลังบ้าน\u003C\u002Fp>\u003Ch3>โครงสร้างข้อมูลและการทำความสะอาดหน่วยความจำ (State &amp; Background Cleanup)\u003C\u002Fh3>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>package main\n\nimport (\n\t\"net\u002Fhttp\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com\u002Fgin-gonic\u002Fgin\"\n\t\"golang.org\u002Fx\u002Ftime\u002Frate\"\n)\n\n\u002F\u002F clientLimiter เก็บสถานะถังโทเคนของ Client แต่ละคนแยกกัน\ntype clientLimiter struct {\n\tlimiter  *rate.Limiter\n\tmu       sync.RWMutex \u002F\u002F เพิ่ม Mutex เพื่อป้องกัน Data Race ในการอ่าน\u002Fเขียน lastSeen\n\tlastSeen time.Time\n}\n\nvar (\n\t\u002F\u002F ใช้ sync.Map เพื่อรองรับการเข้าถึงข้อมูลพร้อมกันจากหลาย Goroutines\n\tlimiters sync.Map \n)\n\n\u002F\u002F init สั่งทำงาน Background Worker เพื่อคอยสแกนล้างคีย์ที่เก่าเกินไป ป้องกัน Memory Leak\nfunc init() {\n\tgo func() {\n\t\tfor {\n\t\t\ttime.Sleep(10 * time.Minute) \u002F\u002F สแกนทุกๆ 10 นาที\n\t\t\tlimiters.Range(func(key, value any) bool {\n\t\t\t\tclient := value.(*clientLimiter)\n\t\t\t\t\n\t\t\t\tclient.mu.RLock()\n\t\t\t\tlastSeen := client.lastSeen\n\t\t\t\tclient.mu.RUnlock()\n\n\t\t\t\t\u002F\u002F หากไม่มีการเข้าใช้งานนานเกิน 1 ชั่วโมง ให้ลบข้อมูลออกจาก Map\n\t\t\t\tif time.Since(lastSeen) &gt; 1*time.Hour {\n\t\t\t\t\tlimiters.Delete(key)\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t})\n\t\t}\n\t}()\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>ฟังก์ชันตรวจสอบและสร้าง Limiter Middleware\u003C\u002Fh3>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>\u002F\u002F getLimiter ค้นหาหรือสร้าง Rate Limiter สำหรับ Client แต่ละราย\nfunc getLimiter(clientID string) *rate.Limiter {\n\tactual, loaded := limiters.Load(clientID)\n\tif !loaded {\n\t\t\u002F\u002F ตั้งค่า: เติมโทเคนอัตรา 1 ใบต่อวินาที และถังจุได้สูงสุด 3 ใบ (Burst)\n\t\tlimiterConfig := rate.NewLimiter(rate.Every(1*time.Second), 3)\n\t\tactual, _ = limiters.LoadOrStore(clientID, &amp;clientLimiter{\n\t\t\tlimiter:  limiterConfig,\n\t\t\tlastSeen: time.Now(),\n\t\t})\n\t}\n\n\tc := actual.(*clientLimiter)\n\t\n\tc.mu.Lock()\n\tc.lastSeen = time.Now() \u002F\u002F อัปเดต Timestamp ล่าสุดอย่างปลอดภัย\n\tc.mu.Unlock()\n\n\treturn c.limiter\n}\n\n\u002F\u002F RateLimitMiddleware ด่านตรวจจับความเร็วการส่ง Request\nfunc RateLimitMiddleware() gin.HandlerFunc {\n\treturn func(c *gin.Context) {\n\t\t\u002F\u002F ในระบบ Production แนะนำให้ดึงจาก User ID ที่แกะจาก JWT Token \n\t\t\u002F\u002F ตัวอย่างนี้ขอแยกแยะด้วย Client IP เพื่อความง่ายครับ\n\t\tclientID := c.ClientIP() \n\n\t\tlimiter := getLimiter(clientID)\n\n\t\t\u002F\u002F ตรวจสอบสถานะว่ามีโทเคนเหลือให้หยิบไปทำงานไหม (Allow จะไม่บล็อก Thread)\n\t\tif !limiter.Allow() {\n\t\t\tc.JSON(http.StatusTooManyRequests, gin.H{\n\t\t\t\t\"error\":       \"Too Many Requests\",\n\t\t\t\t\"message\":     \"คุณเรียกใช้งานระบบ AI บ่อยเกินไป กรุณารอเว้นช่วงสักครู่\",\n\t\t\t\t\"retry_after\": \"1s\",\n\t\t\t})\n\t\t\tc.Abort() \u002F\u002F สั่งระงับสิทธิ์ ไม่ให้ Request ทะลุไปถึงฟังก์ชัน Handler ด้านใน\n\t\t\treturn\n\t\t}\n\n\t\tc.Next()\n\t}\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>ส่วนควบคุมระบบหลักและการทดสอบระบบ: main.go\u003C\u002Fh3>\u003Cp>Go\u003C\u002Fp>\u003Cpre>\u003Ccode>func main() {\n\tr := gin.Default()\n\n\t\u002F\u002F เปิดใช้งาน Middleware คลุมกลุ่ม Endpoint ที่ใช้ประมวลผล AI\n\taiGroup := r.Group(\"\u002Fapi\u002Fv1\u002Fai\")\n\taiGroup.Use(RateLimitMiddleware())\n\t\n\taiGroup.POST(\"\u002Fchat\", func(c *gin.Context) {\n\t\tc.JSON(http.StatusOK, gin.H{\n\t\t\t\"status\": \"success\",\n\t\t\t\"answer\": \"นี่คือผลลัพธ์จากการประมวลผลของโมเดล AI ประจำองค์กร...\",\n\t\t})\n\t})\n\n\tr.Run(\":8080\")\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Ch3>🎯 ท้าให้ลอง (Daily Mission)\u003C\u002Fh3>\u003Cp>ให้คุณเปิดรันเซิร์ฟเวอร์ตัวนี้ทิ้งไว้ จากนั้นเปิดหน้าต่าง Terminal ขึ้นมาใหม่ แล้วสั่งจำลองการยิงสแปม Request แบบรัวๆ ติดต่อกันด้วยคำสั่งลูป curl ชุดนี้ครับ:\u003C\u002Fp>\u003Cp>Bash\u003C\u002Fp>\u003Cpre>\u003Ccode>for i in {1..6}; do curl -X POST http:\u002F\u002Flocalhost:8080\u002Fapi\u002Fv1\u002Fai\u002Fchat; echo \"\"; done\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>\u003Cstrong>การบ้านชวนวิเคราะห์:\u003C\u002Fstrong> ลองสังเกตผลลัพธ์บนหน้าจอ Terminal ดูครับว่า ในรอบการยิงที่เท่าไหร่ระบบเริ่มตัดบทและตีกลับข้อความมาเป็น HTTP Status Code \u003Ccode>429 Too Many Requests\u003C\u002Fcode>?\u003C\u002Fp>\u003Cp>และเพื่อทดสอบการดีไซน์ระดับแอดวานซ์: ถ้านโยบายบริษัทระบุว่า \u003Cem>\"พนักงานแผนก Engineering สามารถยิงใช้งาน AI ได้วินาทีละ 5 ครั้ง แต่พนักงานแผนก Marketing ยิงได้เพียงวินาทีละ 2 ครั้ง\"\u003C\u002Fem> คุณคิดว่าควรปรับปรุงโครงสร้างภายในฟังก์ชัน \u003Ccode>getLimiter\u003C\u002Fcode> อย่างไรเพื่อให้รองรับ Dynamic Configurations ตามกลุ่มผู้ใช้งานได้ดีที่สุด? ลองดีไซน์เล่นๆ ในหัวดูครับ!\u003C\u002Fp>\u003Ch2>FAQ: คำถามที่พบบ่อย\u003C\u002Fh2>\u003Ch3>ทำไมต้องนำ \u003Ccode>sync.RWMutex\u003C\u002Fcode> เข้ามาครอบฟิลด์ \u003Ccode>lastSeen\u003C\u002Fcode> ทั้งๆ ที่ใช้ \u003Ca rel=\"noopener noreferrer\" href=\"http:\u002F\u002Fsync.Map\">\u003Ccode>sync.Map\u003C\u002Fcode>\u003C\u002Fa> อยู่แล้ว?\u003C\u002Fh3>\u003Cp>แม้ว่า \u003Ca rel=\"noopener noreferrer\" href=\"http:\u002F\u002Fsync.Map\">\u003Ccode>sync.Map\u003C\u002Fcode>\u003C\u002Fa> จะช่วยให้การอ่านและเขียนตัวแปร Map ปลอดภัยจากหลาย Goroutines แต่ตัวโครงสร้างข้อมูลย่อย (Struct) ที่อยู่ข้างในไม่ได้ถูกปกป้องด้วย หากเกิดเหตุการณ์ที่ Background Job กำลังอ่านค่า \u003Ccode>client.lastSeen\u003C\u002Fcode> เพื่อเช็กเวลาล้างข้อมูล ในขณะที่ตัว Middleware หลักกำลังเขียนอัปเดต \u003Ccode>c.lastSeen = \u003C\u002Fcode>\u003Ca rel=\"noopener noreferrer\" href=\"http:\u002F\u002Ftime.Now\">\u003Ccode>time.Now\u003C\u002Fcode>\u003C\u002Fa>\u003Ccode>()\u003C\u002Fcode> พร้อมๆ กัน จะเกิดปัญหา Data Race ทันที การใส่ Mutex แยกใน Struct จึงจำเป็นเพื่อความปลอดภัย 100% ครับ\u003C\u002Fp>\u003Ch3>ฟังก์ชัน \u003Ccode>Allow()\u003C\u002Fcode> กับ \u003Ccode>Wait()\u003C\u002Fcode> ในแพ็กเกจ \u003Ccode>x\u002Ftime\u002Frate\u003C\u002Fcode> มีความแตกต่างกันอย่างไร?\u003C\u002Fh3>\u003Cp>\u003Ccode>Allow()\u003C\u002Fcode> จะทำงานแบบ Non-blocking คือถ้ามีโทเคนเหลือก็หยิบแล้วผ่านไปเลย ถ้าไม่มีก็ตีกลับค่าเป็น False ทันที เหมาะสำหรับทำ API Middleware ส่วน \u003Ccode>Wait()\u003C\u002Fcode> จะเป็นแบบ Blocking คือถ้าโทเคนหมด มันจะหยุดรอ (Sleep) จนกว่าถังจะได้รับโทเคนเติมเข้ามาใหม่จนครบ จึงค่อยทำงานต่อ เหมาะสำหรับเขียนสคริปต์ยิงงานหลังบ้านที่เราไม่อยากให้เกิน Rate Limit ของปลายทางครับ\u003C\u002Fp>\u003Ch3>ถ้าเซิร์ฟเวอร์ถูกขยายเป็น 10 เครื่อง (Distributed System) ท่านี้ยังใช้ได้ไหม?\u003C\u002Fh3>\u003Cp>ท่านี้จะใช้งานไม่ได้ผลเต็มที่ครับ เพราะเมมโมรีและถังโทเคนจะแยกกันอยู่คนละเครื่อง ยูสเซอร์คนเดิมยิงเข้าเครื่อง 1, 2, 3 โควตาก็จะถูกนับแยกกัน หากเป็นระบบสเกลใหญ่ แนะนำให้ย้ายสถานะการทำ Rate Limiting ไปไว้ที่ตัวกลางอย่าง Redis โดยใช้กลไกประมวลผลผ่าน Lua Script แทนครับ\u003C\u002Fp>\u003Cdiv data-type=\"horizontalRule\">\u003Chr>\u003C\u002Fdiv>\u003Ch2>สรุปและตอนต่อไป (EP.165)\u003C\u002Fh2>\u003Cp>การทำ Rate Limiting ด้วยอัลกอริทึม Token Bucket เป็นวิธีคัดกรองโหลดที่ง่ายและได้ผลดีที่สุดในการป้องกันการโจมตีหรือบั๊กที่ยิงสแปมเข้ามาหาเซิร์ฟเวอร์ AI ของเรา ช่วยรักษาเสถียรภาพภาพรวมของแอปพลิเคชันไว้ได้\u003C\u002Fp>\u003Cp>\u003Cstrong>ในตอนต่อไป (EP.165):\u003C\u002Fstrong> ถึงแม้เราจะมีด่านจำกัดความเร็วคอยช่วยป้องกันระบบล่มแล้ว แต่ถ้าเกิดความต้องการใช้งานของคนทั้งองค์กรเติบโตขึ้นอย่างถูกต้องพร้อมๆ กันล่ะ? การล็อกสิทธิ์ไว้จะทำให้พนักงานทำงานไม่ได้ และเซิร์ฟเวอร์ตัวเดียวรับน้ำหนักไม่ไหวแน่นอน ตอนหน้าเราจะก้าวเข้าสู่การขยายระบบระดับคลัสเตอร์ด้วยเรื่อง \u003Cstrong>Load Balancing AI Servers — การกระจายโหลดงานเมื่อมีผู้ใช้งานระบบ AI พร้อมกันจำนวนมาก\u003C\u002Fstrong> รอติดตามชมกันครับ Gophers!\u003C\u002Fp>\u003Cp>\u003Cstrong>ฝากกดติดตามพวกเราได้ที่ Superdev Academy\u003C\u002Fstrong> ในทุกช่องทางนะครับ!\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cp>\u003Cstrong>🔵 Facebook: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.facebook.com\u002Fsuperdev.academy.th\">\u003Cstrong>Superdev Academy Thailand\u003C\u002Fstrong>\u003C\u002Fa>\u003Cstrong> \u003C\u002Fstrong>(อัปเดตข่าวสารและบทความใหม่)\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>🎬 YouTube: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.youtube.com\u002F@SuperdevAcademy\">\u003Cstrong>Superdev Academy Channel\u003C\u002Fstrong>\u003C\u002Fa>\u003Cstrong> \u003C\u002Fstrong>(ติวเข้มแบบวิดีโอ)\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>📸 Instagram: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.instagram.com\u002Fsuperdevacademy\u002F\">\u003Cstrong>@superdevacademy\u003C\u002Fstrong>\u003C\u002Fa>\u003Cstrong> \u003C\u002Fstrong>(เกร็ดความรู้สั้นๆ และเบื้องหลังการทำงาน)\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>🎬 TikTok: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener\" class=\"ng-star-inserted\" href=\"https:\u002F\u002Fwww.tiktok.com\u002F@superdevacademy?lang=th-TH\">\u003Cstrong>@superdevacademy\u003C\u002Fstrong>\u003C\u002Fa>\u003Cstrong> \u003C\u002Fstrong>(Tips &amp; Tricks ฉบับย่อยง่าย)\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>🌐 Website: \u003C\u002Fstrong>\u003Ca target=\"_blank\" rel=\"noopener noreferrer\" href=\"http:\u002F\u002Fsuperdevacademy.com\">\u003Cstrong>superdevacademy.com\u003C\u002Fstrong>\u003C\u002Fa>\u003Cstrong> \u003C\u002Fstrong>(คลังบทความและคอร์สเรียนฉบับเต็ม)\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cp>\u003C\u002Fp>","47dznrwpj4u8_8lsn4jjb4p.png","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclblg987654321\u002Fvrpfrocshuxzpng\u002F47dznrwpj4u8_8lsn4jjb4p.png","2026-07-20 06:52:31.363Z","76qprkevbgfdps8",{"keywords":15,"locale":51,"school_blog":61},[16,23,28,32,36,41,46],{"collectionId":17,"collectionName":18,"created":19,"created_by":13,"id":20,"name":21,"updated":22,"updated_by":13},"sclkey987654321","school_keywords","2026-03-04 08:34:07.915Z","921nl48h9in67sw","Rate Limiting","2026-06-07 06:45:58.705Z",{"collectionId":17,"collectionName":18,"created":24,"created_by":13,"id":25,"name":26,"updated":27,"updated_by":13},"2026-03-04 08:44:38.026Z","m3dqo2zalnfaoof","Token Bucket","2026-06-07 06:46:36.495Z",{"collectionId":17,"collectionName":18,"created":29,"created_by":13,"id":30,"name":31,"updated":29,"updated_by":13},"2026-07-20 06:49:29.637Z","b4khguyqounk501","Golang Middleware",{"collectionId":17,"collectionName":18,"created":33,"created_by":13,"id":34,"name":35,"updated":33,"updated_by":13},"2026-07-20 06:49:32.925Z","lri8ql4zmbdf6fj","ป้องกันระบบล่ม",{"collectionId":17,"collectionName":18,"created":37,"created_by":13,"id":38,"name":39,"updated":40,"updated_by":13},"2026-03-04 08:20:11.547Z","ey3puyme01a9bsw","Go","2026-06-07 06:45:07.798Z",{"collectionId":17,"collectionName":18,"created":42,"created_by":13,"id":43,"name":44,"updated":45,"updated_by":13},"2026-03-04 08:20:14.253Z","ah6lvy4x8qe08l5","Golang","2026-06-07 06:45:08.193Z",{"collectionId":17,"collectionName":18,"created":47,"created_by":13,"id":48,"name":49,"updated":50,"updated_by":13},"2026-05-19 08:32:40.909Z","y6cwydp81xsem1f","AI API","2026-06-07 06:49:16.309Z",{"code":52,"collectionId":53,"collectionName":54,"created":55,"flag":56,"id":57,"is_default":58,"label":59,"updated":60},"th","pbc_1989393366","locales","2026-01-22 10:59:55.832Z","twemoji:flag-thailand","s8wri3bt4vgg2ji",true,"Thai","2026-04-10 15:42:46.614Z",{"category":62,"collectionId":63,"collectionName":64,"created":65,"expand":66,"id":81,"slug":82,"updated":83,"views":84},"wqxt7ag2gn7xcmk","pbc_2105096300","school_blogs","2026-07-20 06:52:31.026Z",{"category":67},{"blogIds":68,"collectionId":69,"collectionName":70,"created":71,"created_by":13,"id":62,"image":72,"image_alt":73,"image_path":74,"label":75,"name":76,"priority":77,"publish_at":78,"scheduled_at":73,"status":79,"updated":80,"updated_by":13},[],"sclcatblg987654321","school_category_blogs","2026-03-04 08:33:53.210Z","59ty92ns80w_15oc1implw.png","","https:\u002F\u002Ftwsme-r2.tumwebsme.com\u002Fsclcatblg987654321\u002Fwqxt7ag2gn7xcmk\u002F59ty92ns80w_15oc1implw.png",{"en":76,"th":76},"Golang The Series",1,"2026-03-16 04:39:38.440Z","published","2026-06-07 06:45:03.856Z","rzql5nsqf8oaqxv","golang-rate-limiting-ai-requests","2026-07-28 16:23:03.688Z",113,"vrpfrocshuxzpng",[20,25,30,34,38,43,48],"2026-07-28 14:15:09.240Z","ปกป้องแอปพลิเคชัน AI Backend ของคุณจากการสแปมและใช้งานเกินโควตา เรียนรู้วิธีสร้าง Rate Limiting Middleware ด้วยอัลกอริทึม Token Bucket ในภาษา Go อย่างปลอดภัยแบบ Thread-safe และไร้ปัญหา Data Race","Golang The Series EP.164: Rate Limiting AI Requests ป้องกันระบบพังจากการเรียกใช้งาน API เกินโควตา","2026-07-28 14:15:09.241Z",{"th":82,"en":82}]