Halo Semua, kali ini saya akan sharing tentang rate limiter di golang. rate limiter sendiri adalah sebuah metode untuk membatalkan request dari pengguna yang terlalu banyak dalam waktu bersamaan.
Yang Ingin Membantu Saya Untuk Terus Berkontribusi Boleh Banget Klik Dibawah
Implementasi
Kita membuat sebuah folder bernama middleware dan di dalamnya buat sebuah file bernama limiter.go
package middleware
import (
"log"
"net"
"net/http"
"sync"
"time"
"golang.org/x/time/rate"
)
// getVisitor
type visitor struct {
limiter *rate.Limiter
lastSeen time.Time
}
var visitors = make(map[string]*visitor)
var mu sync.Mutex
// menjalankan di belakang clean up visitor
func init() {
go cleanupVisitors()
}
// membersihkan map visitor ketika sudah tiga menit
func cleanupVisitors() {
for {
time.Sleep(time.Minute)
mu.Lock()
for ip, v := range visitors {
if time.Since(v.lastSeen) > 3*time.Minute {
delete(visitors, ip)
}
}
mu.Unlock()
}
}
// menangkap semua pengunjung berdasarkan ip yang melakukan request
func getVisitor(ip string) *rate.Limiter {
mu.Lock()
defer mu.Unlock()
v, exists := visitors[ip]
// mengecheck apakah ip ini ada
// jika tidak ada daftarkan ipnya di map
if !exists {
// setiap satu detik hanya ada 15 request yang diperbolahkan
limiter := rate.NewLimiter(1, 15)
// menyimpan ip dan menetapkan limiter
visitors[ip] = &visitor{limiter, time.Now()}
return limiter
}
// ketika ip ditemukan jalankan limiter dari ip itu
// Update waktu terakhir dia request
v.lastSeen = time.Now()
return v.limiter
}
// ini bisa di gunakan nanti sebagai middleware
func Limiter(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var host, _, err = net.SplitHostPort(r.RemoteAddr)
if err != nil {
log.Print(err.Error())
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// check visitor
var limiter = getVisitor(host)
// jika limit penuh return error to many request
if !limiter.Allow() {
http.Error(w, http.StatusText(http.StatusTooManyRequests), http.StatusTooManyRequests)
return
}
h.ServeHTTP(w, r)
})
}
isikan dengan
package main
import (
"log"
"net/http"
limit "github.com/aryadiahmad4689/rate-limit-go/middleware"
)
func okHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("OK"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", okHandler)
// Wrap the servemux with the limit middleware.
log.Print("Listening on :4000...")
http.ListenAndServe(":4000", limit.Limiter(mux))
}
package main
import (
"fmt"
"io/ioutil"
"net/http"
"time"
)
func test() {
c := http.Client{Timeout: time.Duration(1) * time.Second}
resp, err := c.Get("http://localhost:4000")
if err != nil {
fmt.Printf("Error %s", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
fmt.Printf("Body : %s", body)
}
func main() {
var i int
for i = 0; i < 20; i++ {
test()
fmt.Println(i)
}
}
jalankan server terlebih dahulu baru jalankan root main.go dan hasilnya
lihat hasilnya ketika request ke lima belas dia akan mengembalikan error bahwa request terlalu banyak.
Yang Ingin Membantu Saya Untuk Terus Berkontribusi Boleh Banget Klik Dibawah
Kita membuat sebuah function public bernama limiter yang melewati http handler. function ini digunakan untuk menangkap semua request yang masuk berdasarkan ip dan di validasi oleh get visitor. untuk penjelasan silahkan lihat komentar di codenya.
Coba Kita Test
kita membuat sebuah folder bernama server dialamnya ada file bernama server.go
untuk di root main.go isikan dengan
foldenya sekarang seperti
Itulah teman teman tentang rate-limiter di golang. semoga membantu