Hertz
Hertz 是一个超大规模的企业级微服务 HTTP 框架,具有高易用性、易扩展、低时延等特点。
Hertz 默认使用自研的高性能网络库 Netpoll,在一些特殊场景中,相较于 go net,Hertz 在 QPS、时延上均具有一定优势。
在内部实践中,某些典型服务,如框架占比较高的服务、网关等服务,迁移 Hertz 后相比 Gin 框架,资源使用显著减少,CPU 使用率随流量大小降低 30%—60% 。
关于 Hertz 更多的信息可移步至 cloudwego/hertz
反向代理
反向代理在计算机网络中是代理服务器的一种。
服务器根据客户端的请求,从其关系的一组或多组后端服务器(如 Web 服务器)上获取资源,然后再将这些资源返回给客户端,客户端只会得知反向代理的 IP 地址,而不知道在代理服务器后面的服务器集群的存在。
Hertz 反向代理实战
在 Hertz 中使用反向代理需要拉取社区提供的 reverseproxy 拓展。
$ go get github.com/hertz-contrib/reverseproxy
基本使用
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/utils"
"github.com/hertz-contrib/reverseproxy"
)
func main() {
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()
}
NewSingleHostReverseProxy/proxy/proxy/backend/backendproxy.ServeHTTP/backend/proxy/backend
curl 127.0.0.1:8000/backend
{"msg":"proxy success!!"}
自定义配置
当然,拓展不只是能够实现简单的反向代理,在 reverseproxy 拓展中提供了许多可以自定义的可选项。
SetDirectorSetClientSetModifyResponseSetErrorHandler
SetDirector & SetClient
SetDirectorSetClient
Server
package main
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/common/utils"
"github.com/cloudwego/hertz/pkg/protocol/consts"
"github.com/hertz-contrib/registry/nacos"
)
func main() {
addr := "127.0.0.1:8000"
r, _ := nacos.NewDefaultNacosRegistry()
h := server.Default(
server.WithHostPorts(addr),
server.WithRegistry(r, ®istry.Info{
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()
}
这里使用了 hertz-contrib/registry 拓展中 server 端的示例代码,由于这并不是本文的主要内容所以不做展开,关于更多信息可以去到 registry 库中。
Client
package main
import (
"github.com/cloudwego/hertz/pkg/app/client"
"github.com/cloudwego/hertz/pkg/app/middlewares/client/sd"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/config"
"github.com/cloudwego/hertz/pkg/protocol"
"github.com/hertz-contrib/registry/nacos"
"github.com/hertz-contrib/reverseproxy"
)
func main() {
cli, err := client.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.Target)))
req.Header.SetHostBytes(req.URI().Host())
req.Options().Apply([]config.RequestOption{config.WithSD(true)})
})
h.GET("/backend", proxy.ServeHTTP)
h.Spin()
}
SetClientSetDirector
SetModifyResponse & SetErrorHandler
SetModifyResponseSetErrorHandlerSetModifyResponsemodifyResponsemodifyResponseerrorHandler
SetModifyResponse
package main
import (
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/protocol"
"github.com/hertz-contrib/reverseproxy"
)
func main() {
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()
}
SetModifyResponsemodifyResponse
测试
curl 127.0.0.1:8000/modifyResponse
change response success
SetErrorHandler
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/utils"
"github.com/hertz-contrib/reverseproxy"
)
func main() {
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, "fake 404 not found")
})
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()
}
SetErrorHandlermodifyResponse
测试
curl 127.0.0.1:8002/backend
fake 404 not found
中间件使用
除了基本使用外,Hertz 反向代理还支持在中间件中使用。
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/utils"
"github.com/hertz-contrib/reverseproxy"
)
func main() {
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.Query("country") == "cn" {
proxy.ServeHTTP(c, ctx)
ctx.Response.Header.Set("key", "value")
ctx.Abort()
} else {
ctx.Next(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()
}
NewSingleHostReverseProxy
测试1
curl 127.0.0.1:9997/backend
{"message":"pong2"}
curl 127.0.0.1:9998/backend
{"message":"pong1"}
Usectx.Query("country") == "cn"proxy.ServeHTTP(c, ctx)/backend
测试2
curl 127.0.0.1:9998/backend?country=cn
{"message":"pong2"}
注意项
NewSingleHostReverseProxyconfig.ClientOptionclient.Clientconfig.ClientOptionclient.Clientclient.ClientReverseProxy.SetClient
- 反向代理会重置响应头,如果在请求之前修改了响应头将不会生效,这与标准库的行为不一致。