In a microservice architecture, managing cross-cutting concerns like routing, authentication, authorization, and rate limiting at each service level introduces duplicate code and maintenance overhead. The standard pattern to solve this is a Unified API Gateway. By handling these concerns at the edge of your infrastructure, downstream services can remain focused entirely on their core business logic.
When selecting a language to build an API gateway, Go stands out as an exceptional choice. Its lightweight goroutines, built-in net/http package, and memory efficiency make it ideal for high-throughput edge proxies.
1. Dynamic Reverse Proxying
Go's standard library provides a built-in reverse proxy implementation in the net/http/httputil package. We can utilize httputil.NewSingleHostReverseProxy to route incoming HTTP requests to their corresponding backend microservices based on prefix paths:
func NewGatewayProxy(targetURL string) *httputil.ReverseProxy {
url, _ := url.Parse(targetURL)
proxy := httputil.NewSingleHostReverseProxy(url)
// Customize director to adjust headers
originalDirector := proxy.Director
proxy.Director = func(req *http.Request) {
originalDirector(req)
req.Header.Set("X-Forwarded-Host", req.Header.Get("Host"))
req.Header.Set("X-Gateway-Request-ID", uuid.New().String())
}
return proxy
}
2. Token-Bucket Rate Limiting
To protect downstream services from denial-of-service attempts and traffic spikes, we implement a middleware utilizing Go's golang.org/x/time/rate package. This implements the classic token-bucket algorithm:
- Limiters Map: A thread-safe map storing limiters keyed by Client IP or API Key.
- Cleanup Routine: A background goroutine that cleans up idle limiters to prevent memory leaks.
type IPBasedLimiter struct {
ips map[string]*rate.Limiter
mu sync.Mutex
r rate.Limit
b int
}
func (l *IPBasedLimiter) GetLimiter(ip string) *rate.Limiter {
l.mu.Lock()
defer l.mu.Unlock()
limiter, exists := l.ips[ip]
if !exists {
limiter = rate.NewLimiter(l.r, l.b)
l.ips[ip] = limiter
}
return limiter
}
By routing all requests through this gateway, we reduce latency overhead to sub-millisecond ranges while securing the internal network topology.