Limit resource access by rate limit


After we did an API server with Golang, what we are going to do in this article is about how to limit access from users to our server.

Rate limit is mainly used for spam detection. My application in this example will have only one endpoint. And I'm going to limit each person to access it 5 times per minute

First, create a Go module

mkdir rate-limit
cd rate-limit
go mod init golimit
touch main.go
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	r.GET("/", func(ctx *gin.Context) {
		ctx.String(http.StatusOK, "The resource is available")
	})
}

Next, I will create a struct for doing rate limit

type Limiter struct {
	mu       sync.Mutex
	duration time.Duration
	limit    int
	slots    int
}

slots is to check for available places, duration will be the time interval to reset all slots and mu is to prevent data race while editing the slots value.

Then implement functions for this struct

func NewLimiter(req int, t time.Duration) Limiter {
	return Limiter{
		duration: t,
		limit:    req,
	}
}

func (l *Limiter) Start() chan<- bool {
	stop := make(chan bool)
	ticker := time.NewTicker(l.duration)
	l.mu.Lock()
	l.slots = l.limit
	defer l.mu.Unlock()
	go func() {
		for {
			select {
			case <-stop:
				break
			case <-ticker.C:
				l.mu.Lock()
				l.slots = l.limit
				l.mu.Unlock()
			}
		}
	}()
	return stop
}

func (l *Limiter) AllowAndTake() bool {
	l.mu.Lock()
	defer l.mu.Unlock()
	if l.slots > 0 {
		l.slots -= 1
		return true
	}
	return false
}

Finally, replace the main function with this

func main() {
  r := gin.Default()
  limiter := NewLimiter(5, time.Minute)
  stop := limiter.Start()
  r.GET("/", func(ctx *gin.Context) {
    if limiter.AllowAndTake() {
      ctx.String(http.StatusOK, "The resource is available")
      return
    }
    ctx.String(http.StatusServiceUnavailable, "The resource is closed")
  })
  r.Run()
  stop <- true
}

Then try to access the resource via localhost:8080 more than 5 times in a minute. If the resouce isn't available, try to access later after a minute passes.

sources