Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?

Golang Reverse Proxy Practices – DEV Community ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป



Hertz

Hertz is an ultra-large-scale enterprise-level microservice HTTP framework, that includes excessive ease of use, straightforward growth, and low latency and so on.

Hertz makes use of the self-developed high-performance community library Netpoll by default. In some particular situations, Hertz has sure benefits in QPS and latency in comparison with go web.

In inner apply, some typical providers, similar to providers with a excessive proportion of frameworks, gateways and different providers, after migrating to Hertz, in comparison with the Gin framework, the useful resource utilization is considerably decreased, CPU utilization is decreased by 30%-60% with the dimensions of the site visitors.

For extra particulars, see cloudwego/hertz.



Reverse proxy

In laptop networks, a reverse proxy is an software that sits in entrance of back-end functions and forwards shopper (e.g. browser) requests to these functions.

Reverse proxies assist improve scalability, efficiency, resilience and safety. The sources returned to the shopper seem as in the event that they originated from the net server itself.



Utilizing reverse proxy in Hertz

Utilizing a reverse proxy with Hertz requires pulling the reverseproxy extension supplied by the neighborhood.

$ go get github.com/hertz-contrib/reverseproxy
Enter fullscreen mode

Exit fullscreen mode



Fundamental utilizing

bundle foremost

import (
        "context"

        "github.com/cloudwego/hertz/pkg/app"
        "github.com/cloudwego/hertz/pkg/app/server"
        "github.com/cloudwego/hertz/pkg/widespread/utils"
        "github.com/hertz-contrib/reverseproxy"
)

func foremost() {
        h := server.Default(server.WithHostPorts("127.0.0.1:8000"))
        proxy, err := reverseproxy.NewSingleHostReverseProxy("http://127.0.0.1:8000/proxy")
        if err != nil {
                panic(err)
        }
        h.GET("/proxy/backend", func(cc context.Context, c *app.RequestContext) {
                c.JSON(200, utils.H{
                        "msg": "proxy success!!",
                })
        })
        h.GET("/backend", proxy.ServeHTTP)
        h.Spin()
}
Enter fullscreen mode

Exit fullscreen mode

We set the goal path of the reverse proxy /proxy by way of the NewSingleHostReverseProxy operate. Subsequent, the trail of the registered route is the sub-path /proxy/backend of the goal path of the reverse proxy, and eventually the reverse proxy service proxy.ServeHTTP is mapped by registering /backend. On this means, once we entry /backend by way of the GET technique, we’ll entry the content material in /proxy/backend.

curl 127.0.0.1:8000/backend

{"msg":"proxy success!!"}
Enter fullscreen mode

Exit fullscreen mode



Customized configuration

After all, the extension isn’t solely in a position to implement a easy reverse proxy, there are numerous customizable choices supplied within the reverseproxy extension.

Technique Description
SetDirector use to customise protocol.Request
SetClient use to customise shopper
SetModifyResponse use to customise modify response operate
SetErrorHandler use to customise error handler



SetDirector & SetClient

We apply utilizing SetDirector and SetClient by implementing a easy service registration discovery.



Server
bundle foremost

import (
        "context"

        "github.com/cloudwego/hertz/pkg/app"
        "github.com/cloudwego/hertz/pkg/app/server"
        "github.com/cloudwego/hertz/pkg/app/server/registry"
        "github.com/cloudwego/hertz/pkg/widespread/utils"
        "github.com/cloudwego/hertz/pkg/protocol/consts"
        "github.com/hertz-contrib/registry/nacos"
)

func foremost() {
        addr := "127.0.0.1:8000"
        r, _ := nacos.NewDefaultNacosRegistry()
        h := server.Default(
                server.WithHostPorts(addr),
                server.WithRegistry(r, &registry.Data{
                        ServiceName: "demo.hertz-contrib.reverseproxy",
                        Addr:        utils.NewNetAddr("tcp", addr),
                        Weight:      10,
                }),
        )
        h.GET("/backend", func(cc context.Context, c *app.RequestContext) {
                c.JSON(consts.StatusOK, utils.H{"ping": "pong"})
        })
        h.Spin()
}
Enter fullscreen mode

Exit fullscreen mode

The pattern code on the server aspect within the hertz-contrib/registry extension is used right here. Since this isn’t the primary content material of this text, it is not going to be expanded. For extra info, you possibly can Go to the registry repository.



Consumer
bundle foremost

import (
        "github.com/cloudwego/hertz/pkg/app/shopper"
        "github.com/cloudwego/hertz/pkg/app/middlewares/shopper/sd"
        "github.com/cloudwego/hertz/pkg/app/server"
        "github.com/cloudwego/hertz/pkg/widespread/config"
        "github.com/cloudwego/hertz/pkg/protocol"
        "github.com/hertz-contrib/registry/nacos"
        "github.com/hertz-contrib/reverseproxy"
)

func foremost() {
        cli, err := shopper.NewClient()
        if err != nil {
                panic(err)
        }
        r, err := nacos.NewDefaultNacosResolver()
        if err != nil {
                panic(err)
        }
        cli.Use(sd.Discovery(r))
        h := server.New(server.WithHostPorts(":8741"))
        proxy, _ := reverseproxy.NewSingleHostReverseProxy("http://demo.hertz-contrib.reverseproxy")
        proxy.SetClient(cli)
        proxy.SetDirector(func(req *protocol.Request) {
                req.SetRequestURI(string(reverseproxy.JoinURLPath(req, proxy.Goal)))
                req.Header.SetHostBytes(req.URI().Host())
                req.Choices().Apply([]config.RequestOption{config.WithSD(true)})
        })
        h.GET("/backend", proxy.ServeHTTP)
        h.Spin()
}
Enter fullscreen mode

Exit fullscreen mode

Within the Consumer part we used a reverse proxy for service discovery. First, specify the shopper utilizing the service discovery middleware as our forwarding shopper by way of SetClient, after which use SetDirector to specify our protocol.Request , and configure the usage of service discovery within the new Request.



SetModifyResponse & SetErrorHandler

SetModifyResponse and SetErrorHandler respectively set the response from the backend and the dealing with of errors arriving within the backend. SetModifyResponse is definitely modifyResponse within the reverse proxy extension. If the backend returns any response, it doesn’t matter what the standing code is, this technique might be known as. If the modifyResponse technique returns an error, the errorHandler technique might be known as with the error as an argument.



SetModifyResponse
bundle foremost

import (
        "github.com/cloudwego/hertz/pkg/app/server"
        "github.com/cloudwego/hertz/pkg/protocol"
        "github.com/hertz-contrib/reverseproxy"
)

func foremost() {
        h := server.Default(server.WithHostPorts("127.0.0.1:8000"))
        // modify response
        proxy, _ := reverseproxy.NewSingleHostReverseProxy("http://127.0.0.1:8000/proxy")
        proxy.SetModifyResponse(func(resp *protocol.Response) error {
                resp.SetStatusCode(200)
                resp.SetBodyRaw([]byte("change response success"))
                return nil
        })
        h.GET("/modifyResponse", proxy.ServeHTTP)

        h.Spin()
}
Enter fullscreen mode

Exit fullscreen mode

Right here, modifyResponse is modified by SetModifyResponse to vary the processing content material of the response.

Take a look at

curl 127.0.0.1:8000/modifyResponse

change response success
Enter fullscreen mode

Exit fullscreen mode



SetErrorHandler
bundle foremost

import (
        "context"

        "github.com/cloudwego/hertz/pkg/app"
        "github.com/cloudwego/hertz/pkg/app/server"
        "github.com/cloudwego/hertz/pkg/widespread/utils"
        "github.com/hertz-contrib/reverseproxy"
)

func foremost() {
        h := server.Default(server.WithHostPorts("127.0.0.1:8002"))
        proxy, err := reverseproxy.NewSingleHostReverseProxy("http://127.0.0.1:8000/proxy")
        if err != nil {
                panic(err)
        }
        proxy.SetErrorHandler(func(c *app.RequestContext, err error) {
                c.Response.SetStatusCode(404)
                c.String(404, "pretend 404 not discovered")
        })

        h.GET("/proxy/backend", func(cc context.Context, c *app.RequestContext) {
                c.JSON(200, utils.H{
                        "msg": "proxy success!!",
                })
        })
        h.GET("/backend", proxy.ServeHTTP)
        h.Spin()
}
Enter fullscreen mode

Exit fullscreen mode

We use SetErrorHandler to specify the right way to deal with errors that arrive within the background. When an error arrives within the background or there may be an error from modifyResponse, the desired processing logic might be run.

Take a look at

curl 127.0.0.1:8002/backend

pretend 404 not discovered
Enter fullscreen mode

Exit fullscreen mode



Middleware utilization

Along with fundamental use, Hertz reverse proxy additionally helps use in middleware.

bundle foremost

import (
        "context"
        "github.com/cloudwego/hertz/pkg/app"
        "github.com/cloudwego/hertz/pkg/app/server"
        "github.com/cloudwego/hertz/pkg/widespread/utils"
        "github.com/hertz-contrib/reverseproxy"
)

func foremost() {
        r := server.Default(server.WithHostPorts("127.0.0.1:9998"))
        r2 := server.Default(server.WithHostPorts("127.0.0.1:9997"))

        proxy, err := reverseproxy.NewSingleHostReverseProxy("http://127.0.0.1:9997")
        if err != nil {
                panic(err)
        }

        r.Use(func(c context.Context, ctx *app.RequestContext) {
                if ctx.Question("nation") == "cn" {
                        proxy.ServeHTTP(c, ctx)
                        ctx.Response.Header.Set("key", "worth")
                        ctx.Abort()
                } else {
                        ctx.Subsequent(c)
                }
        })

        r.GET("/backend", func(c context.Context, ctx *app.RequestContext) {
                ctx.JSON(200, utils.H{
                        "message": "pong1",
                })
        })

        r2.GET("/backend", func(c context.Context, ctx *app.RequestContext) {
                ctx.JSON(200, utils.H{
                        "message": "pong2",
                })
        })

        go r.Spin()
        r2.Spin()
}
Enter fullscreen mode

Exit fullscreen mode

On this instance code, two Hertz cases are initialized first, then NewSingleHostReverseProxy is used to set the reverse proxy goal to port 9997, and eventually two routes with the identical path are registered for the 2 cases respectively.

Test1

curl 127.0.0.1:9997/backend

{"message":"pong2"}
curl 127.0.0.1:9998/backend

{"message":"pong1"}
Enter fullscreen mode

Exit fullscreen mode

The primary a part of this code is the middleware utilization half. We use the middleware by way of Use. Within the middleware logic, when the logic of ctx.Question("nation") == "cn" is established, name proxy. ServeHTTP(c, ctx) makes use of a reverse proxy, and if you request /backend by way of occasion r, it’ll request the content material of the reverse proxy goal port 9997.

Test2

curl 127.0.0.1:9998/backend?nation=cn

{"message":"pong2"}
Enter fullscreen mode

Exit fullscreen mode



Suggestions

  • For NewSingleHostReverseProxy operate, if no config.ClientOption is handed it’ll use the default world shopper.Consumer occasion. When passing config.ClientOption it’ll initialize a neighborhood shopper.Consumer occasion. Utilizing ReverseProxy.SetClient if there may be want for shared personalized shopper.Consumer occasion.
  • The reverse proxy resets the header of the response, any such modifications earlier than the request is made might be discarded.



Reference

Add a Comment

Your email address will not be published. Required fields are marked *

Want to Contribute to us or want to have 15k+ Audience read your Article ? Or Just want to make a strong Backlink?