Foreword
As backend developers, we are certainly familiar with Nginx. It is the absolute dominator of reverse proxies and load balancing. However, have you encountered this scenario: your business is in a rapid iteration phase, backend service nodes change frequently, or you need to perform canary releases. Every time you adjust the Upstream servers, you have to modify nginx.conf and then carefully execute nginx -s reload.
Although Nginx has powerful performance, its configuration management can feel slightly "heavy" in certain dynamic scenarios (while Nginx Plus supports dynamic APIs, that is a paid feature; Lua scripts can also achieve this, but maintenance costs are high).
If you are a Go developer and are currently using the Gin framework, you can completely "embed" reverse proxy capabilities into your business code. Today, I am introducing a production-grade reverse proxy library based on Gin —— Gin Reverse Proxy. It not only replaces some Nginx functions but, more importantly: it supports dynamic management of routes and nodes via API without restarting the service.
Why Implement Reverse Proxy in Code?
Usually, we consider reverse proxies an operations matter. But in microservices or cloud-native environments, enabling the gateway layer with programmable capabilities is essential.
This library github.com/go-dev-frame/sponge/pkg/gin/proxy is actually built on the Go standard library net/http/httputil, but it adds a very practical layer of encapsulation:
- Dynamic Awareness: Bring backend machines online or offline at any time via HTTP API.
- Health Checks: Similar to Nginx's
health_check, automatically removing bad nodes and reviving them after recovery. - Load Balancing Strategies: Built-in Round Robin, Least Connections, and IP Hash.
This means you can write a simple Go program that handles some business logic while also distributing traffic like a gateway, and it is extremely flexible.
Quick Start: Build Your Gateway
Assume we want to start a gateway locally to distribute traffic to two backend service clusters (e.g., "Transaction Service" and "User Service").
1. Basic Code Implementation
With just a few lines of code, your Gin service can transform into a reverse proxy:
package main
import (
"fmt"
"github.com/go-dev-frame/sponge/pkg/gin/proxy"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// Initialize proxy middleware; default management interface is mounted at /endpoints
p := proxy.New(r)
// Configure routing rules
setupProxyRoutes(p)
// Your Gin can still handle normal routes
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "I am the gateway itself"})
})
fmt.Println("Gateway started, listening on :8080")
r.Run(":8080")
}
func setupProxyRoutes(p *proxy.Proxy) {
// Rule 1: Dispatch requests starting with /proxy/ to cluster A
// Defaults to Round Robin strategy, health check interval of 5 seconds
err := p.Pass("/proxy/", []string{
"http://localhost:8081",
"http://localhost:8082",
})
if err != nil {
panic(err)
}
// Rule 2: Dispatch requests starting with /personal/ to cluster B
err = p.Pass("/personal/", []string{
"http://localhost:8083",
"http://localhost:8084",
})
if err != nil {
panic(err)
}
}
Looking at this, you will find it very similar to Nginx's proxy_pass logic, but it is compiled into the binary file.
2. Advanced Configuration: More Than Just Forwarding
Production environments often require finer control. For example, for services requiring Session persistence, we need the IP Hash strategy; for critical services, we need custom health check timings.
proxy.New and p.Pass both support Option pattern configuration, which is very "Go Style":
// Fine-grained configuration when registering routes
err := p.Pass("/proxy/", []string{"http://localhost:8081", "http://localhost:8082"}, proxy.Config{
// Use IP Hash algorithm to ensure requests from the same user hit the same machine
proxy.WithPassBalancer(proxy.BalancerIPHash),
// Custom health check: once every 5 seconds, timeout 3 seconds
proxy.WithPassHealthCheck(time.Second * 5, time.Second * 3),
// You can even add middleware (e.g., authentication) to this proxy route individually
proxy.WithPassMiddlewares(AuthMiddleware()),
})
This step is very powerful. In Nginx, configuring authentication is usually troublesome (requiring modules like auth_request), whereas in Go, this is just a standard Gin Middleware.
Killer Feature: Runtime Dynamic Management
This is the most attractive part of this library.
Previously, if localhost:8081 went down, or you needed to scale out to include a new machine 8085, you usually had to modify configuration and restart the gateway. But here, after the proxy service starts, it automatically exposes management APIs (default under /endpoints).
You can command your gateway directly via HTTP requests.
Scenario 1: Service Scaling
Traffic spikes during a sale, and you temporarily start a new machine http://localhost:8085. You want the gateway to distribute traffic to it immediately.
Send a POST request directly:
curl -X POST http://localhost:8080/endpoints/add \
-H "Content-Type: application/json" \
-d '{
"prefixPath": "/proxy/",
"targets": ["http://localhost:8085"]
}'
Takes effect instantly. The new node automatically joins the load balancing pool and begins accepting health checks.
Scenario 2: Node Offline / Canary Release
If you find 8085 has a high error rate or needs an upgrade, you need to take it offline gracefully.
curl -X POST http://localhost:8080/endpoints/remove \
-H "Content-Type: application/json" \
-d '{
"prefixPath": "/proxy/",
"targets": ["http://localhost:8085"]
}'
Scenario 3: Monitoring Dashboard
If you want to know the current health status of all backend nodes, directly call the list interface:
curl http://localhost:8080/endpoints/list?prefixPath=/proxy/
The returned result is clear:
{
"prefixPath": "/proxy/",
"targets": [
{"target": "http://localhost:8081", "healthy": true},
{"target": "http://localhost:8082", "healthy": false}
]
}
Summary and Suggestions
When should you use it?
- Go Tech Stack Teams: If your team primarily uses Go, maintaining a Go-based gateway is much easier than maintaining Nginx configuration files.
- High Customization Logic: For example, if you need to read a blocklist from Redis before forwarding, or perform complex signature verification on the request body, writing middleware in Go is much more comfortable than writing Nginx Lua scripts.
- Small/Medium Dynamic Environments: For lightweight deployments outside of k8s, or development/test environments, dynamically adjusting routing via API is very convenient.
When should you still use Nginx?
- Static resource serving (CDN level).
- Extremely massive concurrency (Millions of QPS), where Nginx's C-based optimizations remain the ceiling.
Overall, Gin Reverse Proxy offers a middle ground between "hard coding" and "pure operations tools." It returns control of load balancing and reverse proxying to developers, allowing the network layer to be as flexible and versatile as business logic.
If you are fed up with frequently reloading Nginx, try this solution in your next project.
proxy is a built-in library of Sponge. Sponge is a powerful and easy-to-use Go development framework that integrates code generation, Web framework (Gin), and microservice framework (gRPC), covering the full lifecycle from project generation, development, and testing to API documentation and deployment. Sponge aims to improve backend service development efficiency and code quality, eliminating tedious repetitive work and focusing on the implementation of core business logic.
Sponge's core philosophy is "Definitions as Code". By parsing SQL, Protobuf, and JSON configuration files, it generates modular service code. Developers can flexibly combine these modules to quickly build various backend systems such as RESTful APIs, gRPC, HTTP+gRPC, gRPC Gateway, or microservice clusters via a low-code approach.
Sponge Github Address: https://github.com/go-dev-frame/sponge
Top comments (0)