Engine
Hertz
的路由、中间件的注册,服务启动,退出等重要方法都是包含在 server.Hertz
这个核心类型之中的。
它由 route.Engine
以及 signalWaiter
组成。以下是 Hertz
的定义:
// Hertz is the core struct of hertz.
type Hertz struct {
*route.Engine
// 用于接收信号以实现优雅退出
signalWaiter func (err chan error) error
}
server.Hertz
初始化服务
Hertz 在 server
包中提供了 New
和 Default
函数用于初始化服务。
Default
默认使用了 Recovery
中间件以保证服务在运行时不会因为 panic
导致服务崩溃。
// New creates a hertz instance without any default config.
func New(opts ...config.Option) *Hertz {
options := config.NewOptions(opts)
h := &Hertz{Engine: route.NewEngine(options)}
return h
}
// Default creates a hertz instance with default middlewares.
func Default(opts ...config.Option) *Hertz {
h := New(opts...)
// 在 New 的基础上使用了内置的 Recovery 中间件
h.Use(recovery.Recovery())
return h
}
若想详细地了解可选的配置项,可以在 配置说明 中查看。
示例代码
package main
import (
"github.com/cloudwego/hertz/pkg/app/server"
)
func main() {
h := server.New()
// 使用 Default
// h := server.Default()
h.Spin()
}
运行服务
Hertz
提供了 Spin
函数用于启动服务器。
和 route.Engine
中提供的 Run
不同,除非有特殊需求,不然一般使用 Spin
函数用于运行服务。
在使用 服务注册发现 的功能时,Spin
会在服务启动时将服务注册进入注册中心,并使用 signalWaiter
监测服务异常。
只有使用 Spin
来启动服务才能支持 优雅退出 的特性。
package main
import (
"github.com/cloudwego/hertz/pkg/app/server"
)
func main() {
h := server.New()
// 我们通常推荐使用 Spin
h.Spin()
}
package main
import (
"github.com/cloudwego/hertz/pkg/app/server"
)
func main() {
h := server.New()
// 使用 Run 函数启动
if err := h.Run(); err != nil {
// ...
panic(err)
}
}
route.Engine
route.Engine
为 server.Hertz
的重要组成部分,其中拥有大量的在开发中常用的方法,尤为 重要
package route
type Engine struct {
noCopy nocopy.NoCopy //lint:ignore U1000 until noCopy is used
// Engine 名称
Name string
serverName atomic.Value
// 路由和协议服务器的选项
options *config.Options
// router 前缀树
RouterGroup
trees MethodTrees
maxParams uint16
allNoMethod app.HandlersChain
allNoRoute app.HandlersChain
noRoute app.HandlersChain
noMethod app.HandlersChain
// 用于渲染 HTML
delims render.Delims
funcMap template.FuncMap
htmlRender render.HTMLRender
// NoHijackConnPool 将控制是否使用缓存池来获取/释放劫持连接
// 如果很难保证 hijackConn 不会重复关闭,请将其设置为 true
NoHijackConnPool bool
hijackConnPool sync.Pool
// KeepHijackedConns 是一个可选择的禁用连接的选项
// 在连接的 HijackHandler 返回后由 Hertz 关闭。
// 这的选项允许保存在 goroutine 中
// 例如当 hertz 将 http 连接升级为 websocket 时,
// 连接会转到另一个处理程序,该处理程序会在需要时关闭它
KeepHijackedConns bool
// 底层传输的网络库,现在有 go net 和 netpoll 两个选择
transport network.Transporter
// 用于链路追踪
tracerCtl tracer.Controller
enableTrace bool
// 用于管理协议层
protocolSuite *suite.Config
protocolServers map[string]protocol.Server
protocolStreamServers map[string]protocol.StreamServer
// RequestContext 连接池
ctxPool sync.Pool
// 处理从 http 处理程序中恢复的 panic 的函数
// 它应该用于生成错误页面并返回 http 错误代码 500(内部服务器错误)
// 处理程序可用于防止服务器因未恢复的 panic 而崩溃
PanicHandler app.HandlerFunc
// 在收到 Expect 100 Continue Header 后调用 ContinueHandler。
//
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.2.3
// https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.1.1
// 使用 ContinueHandler,服务器可以基于头信息决定是否读取可能较大的请求正文。
//
// 默认情况下就像它们是普通请求一样,自动读取 Expect 100 Continue 请求的请求正文,
ContinueHandler func(header *protocol.RequestHeader) bool
// 用于表示 Engine 状态(Init/Running/Shutdown/Closed)。
status uint32
// Engine 启动时依次触发的 hook 函数
OnRun []CtxErrCallback
// Engine 关闭时同时触发的 hook 函数
OnShutdown []CtxCallback
clientIPFunc app.ClientIP
formValueFunc app.FormValueFunc
}
设置服务器名
示例代码
package main
func main() {
h := server.New()
// 用于设置 response header 中的 Server 字段,默认为 Hertz
h.Name = ""
}
渲染 template
engine 提供了 Delims
, SetFuncMap
, LoadHTMLGlob
, LoadHTMLFiles
, SetHTMLTemplate
, SetAutoReloadHTMLTemplate
等方法用于渲染 HTML 或模板文件。
Delims
用于设置 template 的分隔符
函数签名:
func (engine *Engine) Delims(left, right string) *Engine
SetFuncMap
用于设置 template 的数据源
函数签名:
type FuncMap map[string]interface{}
func (engine *Engine) SetFuncMap(funcMap template.FuncMap)
package main
func main() {
h := server.New()
h.SetFuncMap(template.FuncMap{
"time": time.Now.String(),
})
}
LoadHTMLGlob
用于全局加载 template 文件,可以使用 *
通配符来指定模板文件夹
函数签名:
// LoadHTMLGlob loads HTML files identified by glob pattern
// and associates the result with HTML renderer.
func (engine *Engine) LoadHTMLGlob(pattern string)
示例代码:
// 加载 render/html 目录下的所有 html 模板文件
h.LoadHTMLGlob("render/html/*")
// 加载 render/html/index.tmpl 模板文件
h.LoadHTMLGlob("index.tmpl")
LoadHTMLFiles
用于加载指定的 template 文件,参数为 string 切片
函数签名:
// LoadHTMLFiles loads a slice of HTML files
// and associates the result with HTML renderer.
func (engine *Engine) LoadHTMLFiles(files ...string)
SetHTMLTemplate/SetAutoReloadHTMLTemplate
这两个方法在渲染的内部逻辑使用,不推荐直接使用
注册中间件
Hertz 提供 Use
函数用于将中间件注册进入路由。
我们支持用户自定义中间件,与此同时我们也提供了一些常用的中间件实现, 详情见 hertz-contrib
于此同时,虽然常见的中间件的使用方法为全局注册, 但是我们也支持路由组级别和单一路由级别的注册,详见
Use
函数中 middleware
的形参必须为 app.HandlerFunc
的 http 处理函数。
type HandlerFunc func (ctx context.Context, c *app.RequestContext)
函数签名:
func (engine *Engine) Use(middleware ...app.HandlerFunc) IRoutes
示例代码
package main
// ...
func main() {
h := server.New()
// 将内置的 Recovery 中间件注册进入路由
h.Use(recovery.Recovery())
// 使用自定义的中间件
h.Use(exampleMiddleware())
}
func exampleMiddleware() app.handlerFunc {
return func(ctx context.Context, c *app.RequestContext) {
// 在 Next 中的函数执行之前打印日志
hlog.Info("print before...")
// 使用 Next 使得路由匹配的函数执行
c.Next(ctx)
// 在 Next 中的函数执行之后打印日志
hlog.Ingo("print after...")
}
}
更多示例详见 仓库
服务退出
hertz 提供 Shutdown
函数用于进行优雅退出。
若是使用了 服务注册与发现 的功能,在服务退出发生时也会从注册中心下线相应数据。
函数签名:
func (engine *Engine) Shutdown(ctx context.Context) (err error)
示例代码:
package main
// ...
func main() {
h := server.New()
// 在访问该路径时,会触发 shutdown 函数触发下线
h.GET("/shutdown", func(ctx context.Context, c *app.RequestContext) {
h.ShutDown(ctx)
})
h.Spin()
}
关于优雅退出的更多的信息见 文档
设置 hook 函数
在 Engine 启动和关闭时会触发相应的 hook 函数.
OnRun
和 OnShutdown
是两个 hook 函数的切片,用于存储 hook 函数,为了不影响原有的 hook 函数,
在使用时需要使用 append
函数将新的 hook 函数添加进入切片.
详细设置方式见 Hooks
函数签名:
type CtxCallback func (ctx context.Context)
type CtxErrCallback func (ctx context.Context) error
// Engine 启动时依次触发的 hook 函数
OnRun []CtxErrCallback
// Engine 关闭时同时触发的 hook 函数
OnShutdown []CtxCallback
示例代码:
package main
func main() {
h := server.New()
h.OnRun = append(h.OnRun, func(ctx context.Context) error {
return nil
})
h.OnShutdown = append(h.OnShutdown, func(ctx context.Context) {
//...
})
}
错误处理器
PanicHandler
用于设置当程序发生 panic 时的处理函数,默认为 nil
.
注意: 如果同时设置了 PanicHandler
和 Recovery
中间件,则 Recovery
中间件会覆盖 PanicHandler
的处理逻辑.
示例代码:
package main
func main() {
h := server.New()
// 在 panic 时,会触发 PanicHandler 中的函数,返回 500 状态码并携带错误信息
h.PanicHandler = func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(500, utils.H{
"message": "panic",
})
}
h.GET("/hello", func(c context.Context, ctx *app.RequestContext) {
panic("panic")
})
h.Spin()
}
Hijack
NoHijackConnPool
Hertz 连接劫持时所使用的 hijack conn 是池化管理的,因此被劫持的连接在 websocket 中使用的时候,不支持异步操作。
劫持的连接仅能被关闭一次,第二次关闭会导致空指针异常。
NoHijackConnPool 将控制是否使用缓存池来获取/释放劫持连接。如果使用池,将提升内存资源分配的性能,但无法避免二次关闭连接导致的异常。
如果很难保证 hijackConn 不会被反复关闭,可以将其设置为 true。
示例代码:
package main
func main() {
// https://github.com/cloudwego/hertz/issues/121
h.NoHijackConnPool = true
}
获取路由信息
Hertz 提供了 Routes
获取注册的路由信息供用户使用。
路由信息结构:
// RouteInfo represents a request route's specification which contains method and path and its handler.
type RouteInfo struct {
Method string // http method
Path string // url path
Handler string // handler name
HandlerFunc app.HandlerFunc
}
// RoutesInfo defines a RouteInfo array.
type RoutesInfo []RouteInfo
示例代码:
package main
import (
//...
"github.com/cloudwego/hertz/pkg/app/server"
//...
)
func main() {
h := server.Default()
h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(consts.StatusOK, utils.H{"ping": "pong"})
})
routeInfo := h.Routes()
hlog.Info(routeInfo)
h.Spin()
}
配置
这里是 engine 所涉及的可以使用的配置项集合
Use
请查看 此处
NoHijackConnPool
请查看 此处
OnRun/OnShutdown
请查看 此处
PanicHandler
请查看 此处
GetTransporterName
获取当前使用的网络库名称,现在有原生的 go net
和 netpoll
两种.
linux 默认使用 netpoll
, windows 只能使用 go net
.
如果对如何使用对应的网络库有疑惑,请查看 此处
函数签名:
func (engine *Engine) GetTransporterName() (tName string)
// Deprecated: This only get the global default transporter - may not be the real one used by the engine.
// Use engine.GetTransporterName for the real transporter used.
func GetTransporterName() (tName string)
SetTransporter
SetTransporter
只设置 Engine 的全局默认值。
所以具体在初始化 Engine 时使用 WithTransporter 来设置网络库时会覆盖掉 SetTransporter 的设置。
函数签名:
func SetTransporter(transporter func (options *config.Options) network.Transporter)
IsRunning
判断当前 Engine 是否已经启动.
函数签名:
func (engine *Engine) IsRunning() bool
代码示例:
package main
func main() {
h := server.New()
// 可以通过 /live 接口来判断当前服务的运行状态
h.GET("/live", func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(200, utils.H{
"isLive": h.IsRunning(),
})
})
h.Spin()
}
IsTraceEnable
判断是否启用了 trace 功能.
函数签名:
func (engine *Engine) IsTraceEnable() bool
GetCtxPool
获取当前 Engine 的 ctxPool.
函数签名:
func (engine *Engine) GetCtxPool() *sync.Pool
代码示例:
h := server.New()
// 从 ctxPool 中获取一个 ctx
h.GetCtxPool().Get().(*app.RequestContext)
// 将 ctx 放回 ctxPool
h.GetCtxPool().Put(ctx)
GetServiceName
获取当前 Engine 的服务名.
函数签名:
func (engine *Engine) GetServerName() []byte
NoRoute
用于设置当请求的路由不存在时的处理函数,默认返回 404 状态码
函数签名:
// NoRoute adds handlers for NoRoute. It returns a 404 code by default.
func (engine *Engine) NoRoute(handlers ...app.HandlerFunc)
示例代码:
package main
func main() {
h := server.New()
h.NoRoute(func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(404, utils.H{
"msg": "cannot found resource",
})
})
h.Spin()
}
NoMethod
用于设置当请求的方法不存在时的处理函数,它默认返回一个 405 状态码
当使用 NoMethod 时需要与 server.WithHandleMethodNotAllowed 配合使用
函数签名:
// NoMethod adds handlers for NoMethod. It returns a 405 code by default.
// NoMethod sets the handlers called when the HTTP method does not match.
func (engine *Engine) NoMethod(handlers ...app.HandlerFunc)
示例代码:
package main
func main() {
h := server.New()
h.NoRoute(func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(405, utils.H{
"msg": "cannot match HTTP method",
})
})
h.Spin()
}
Delims
请查看 此处
SetFuncMap
请查看 此处
LoadHTMLGlob
请查看 此处
LoadHTMLFiles
请查看 此处