Reverse Proxy
This recipe demonstrates how to use Echo as a reverse proxy and load balancer in front of your applications, such as WordPress, Node.js, Java, Python, Ruby, or Go. For simplicity, the upstreams here are Go servers that also handle WebSocket.
1) Identify upstream target URL(s)
Section titled “1) Identify upstream target URL(s)”url1, err := url.Parse("http://localhost:8081")if err != nil { e.Logger.Error("failed parse url", "error", err)}url2, err := url.Parse("http://localhost:8082")if err != nil { e.Logger.Error("failed parse url", "error", err)}targets := []*middleware.ProxyTarget{ { URL: url1, }, { URL: url2, },}2) Set up proxy middleware with upstream targets
Section titled “2) Set up proxy middleware with upstream targets”The snippet below uses round-robin load balancing. You can also use
middleware.NewRandomBalancer().
e.Use(middleware.Proxy(middleware.NewRoundRobinBalancer(targets)))To set up a proxy for a sub-route, use Echo#Group().
g := e.Group("/blog")g.Use(middleware.Proxy(...))3) Start upstream servers
Section titled “3) Start upstream servers”cd upstreamgo run server.go server1 :8081go run server.go server2 :80824) Start the proxy server
Section titled “4) Start the proxy server”go run server.goBrowse to http://localhost:1323, and you should see a webpage with an HTTP
request served from “server 1” and a WebSocket request served from “server 2”.
HTTP
Hello from upstream server server1
WebSocket
Hello from upstream server server2!Hello from upstream server server2!Hello from upstream server server2!Source code
Section titled “Source code”Upstream server
Section titled “Upstream server”package main
import ( "context" "fmt" "net/http" "os" "time"
"github.com/labstack/echo/v5" "github.com/labstack/echo/v5/middleware" "golang.org/x/net/websocket")
var index = ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Upstream Server</title> <style> h1, p { font-weight: 300; } </style> </head> <body> <h1>HTTP</h1> <p> Hello from upstream server %s </p> <h1>WebSocket</h1> <p id="output"></p> <script> var ws = new WebSocket('ws://localhost:1323/ws')
ws.onmessage = function(evt) { var out = document.getElementById('output'); out.innerHTML += evt.data + '<br>'; } </script> </body> </html>`
func main() { name := os.Args[1] port := os.Args[2] e := echo.New()
e.Use(middleware.RequestLogger()) e.Use(middleware.Recover())
e.GET("/", func(c *echo.Context) error { return c.HTML(http.StatusOK, fmt.Sprintf(index, name)) })
// WebSocket handler e.GET("/ws", func(c *echo.Context) error { websocket.Handler(func(ws *websocket.Conn) { defer ws.Close() for { // Write err := websocket.Message.Send(ws, fmt.Sprintf("Hello from upstream server %s!", name)) if err != nil { e.Logger.Error("failed to send message", "error", err) } select { case <-ws.Request().Context().Done(): return case <-time.After(1 * time.Second): continue } } }).ServeHTTP(c.Response(), c.Request()) return nil })
sc := echo.StartConfig{Address: port} if err := sc.Start(context.Background(), e); err != nil { e.Logger.Error("failed to start server", "error", err) }}Proxy server
Section titled “Proxy server”package main
import ( "context" "net/url"
"github.com/labstack/echo/v5" "github.com/labstack/echo/v5/middleware")
func main() { e := echo.New() e.Use(middleware.RequestLogger()) e.Use(middleware.Recover())
// Setup proxy url1, _ := url.Parse("http://localhost:8081") url2, _ := url.Parse("http://localhost:8082") targets := []*middleware.ProxyTarget{ {URL: url1}, {URL: url2}, } e.Use(middleware.Proxy(middleware.NewRoundRobinBalancer(targets)))
sc := echo.StartConfig{Address: ":1323"} if err := sc.Start(context.Background(), e); err != nil { e.Logger.Error("failed to start server", "error", err) }}