Engine
Server.Hertz
is the core type of Hertz
, consisting of route.Engine
and signalWaiter
. The important methods for starting, registering routes, registering middleware, and exiting the Hertz
server are all included in server.Hertz
. The following is the definition of server.Hertz
:
type Hertz struct {
*route.Engine
// used to receive signal for elegant exit
signalWaiter func (err chan error) error
}
Route.Engine
is an important component of server.Hertz
, and the definition of Engine
is located in Engine.
Client Config
Option | Default | Description |
---|---|---|
WithTransport | network.NewTransporter | Replace the transport |
WithHostPorts | :8888 |
Specify the listening address and port |
WithKeepAliveTimeout | 1min | Set the keep-alive time of tcp persistent connection, generally no need to modify it, you should more pay attention to idleTimeout rather than modifying it |
WithReadTimeout | 3min | The timeout of data reading |
WithIdleTimeout | 3min | The free timeout of the request link for persistent connection |
WithMaxRequestBodySize | 4 * 1024 * 1024 | Max body size of a request |
WithRedirectTrailingSlash | true | Whether to redirect with the / which is at the end of the router automatically. For example: If there is only /foo/ in the router, /foo will be redirected to /foo/. And if there is only /foo in the router, /foo/ will be redirected to /foo |
WithRemoveExtraSlash | false | RemoveExtraSlash makes the parameter still valid when it contains an extra /. For example, if WithRemoveExtraSlash is true user//xiaoming can match the user/:name router |
WithUnescapePathValues | true | If true, the request path will be escaped automatically (eg. ‘%2F’ -> ‘/'). If UseRawPath is false (the default), UnescapePathValues is true, because URI().Path() will be used and it is already escaped. To set WithUnescapePathValues to false, you need to set WithUseRawPath to true |
WithUseRawPath | false | If true, the original path will be used to match the route |
WithHandleMethodNotAllowed | false | If true when the current path cannot match any method, the server will check whether other methods are registered with the route of the current path, and if exist other methods, it will respond “Method Not Allowed” and return the status code 405; if not, it will use the handler of NotFound to handle it |
WithDisablePreParseMultipartForm | false | If true, the multipart form will not be preprocessed. The body can be obtained via ctx.Request.Body() and then can be processed by user |
WithStreamBody | false | If true, the body will be handled by stream processing |
WithNetwork | “tcp” | Set the network protocol, optional: tcp,udp,unix(unix domain socket) |
WithExitWaitTime | 5s | Set the graceful exit time. the Server will stop connection establishment for new requests and set the Connection: Close header for each request after closing. When the set time is reached, Server will to be closed. the Server can be closed early when all connections have been closed |
WithTLS | nil | Configuring server tls capabilities, For detailed information, please refer to TLS |
WithListenConfig | nil | Set the listener configuration. Can be used to set whether to allow reuse ports, etc. |
WithALPN | false | Whether to enable ALPN |
WithTracer | []interface{}{} | Inject tracer implementation, if not inject Tracer, default: close. |
WithTraceLevel | LevelDetailed | Set trace level |
WithWriteTimeout | infinite | The timeout of data writing |
WithRedirectFixedPath | false | If enabled, if the current request path does not match, the server will try to repair the request path and re-match, if the match is successful and the request is a GET request, it will return status code 301 for redirect, other requests will return 308 for redirect |
WithBasePath | / |
Set the base path, which must be prefixed and suffixed with / |
WithMaxKeepBodySize | 4 * 1024 * 1024 | Sets the maximum size of the request body and response body to be retained during reclaim. Unit: Byte |
WithGetOnly | false | If enabled, only GET requests are accepted |
WithKeepAlive | true | If enabled, use HTTP keepalive |
WithAltTransport | network.NewTransporter | Set up the alternate transport |
WithH2C | false | Sets whether H2C is enabled |
WithReadBufferSize | 4 * 1024 | Set the read buffer size while limiting the HTTP header size |
WithRegistry | registry.NoopRegistry, nil | Setup registry configuration, service registration information |
WithAutoReloadRender | false, 0 | Set up the automatic reload rendering configuration |
WithDisablePrintRoute | false | Sets whether debugPrintRoute is disabled |
WithOnAccept | nil | Set the callback function when a new connection is accepted but cannot receive data in netpoll. In go net, it will be called before converting tls connection |
WithOnConnect | nil | Set the onConnect function. It can received data from connection in netpoll. In go net, it will be called after converting tls connection |
Server Connection limitation:
- If you are using the standard network library, there is no such restriction.
- If netpoll is used, the maximum number of connections is 10000 (this is
the gopool)
used at the bottom of netpoll. Yes, the modification method is also very simple, just call the function provided by
gopool:
gopool.SetCap(xxx)
(you can call it once in main.go).
The configuration items on the server side are initialized using the server.WithXXX
method, such as:
func main() {
h := server.New(server.WithXXXX())
...
}
Initial Service
func Default(opts ...config.Option) *Hertz
func New(opts ...config.Option) *Hertz
Default
Default
is used to initialize the service, and the Recovery
middleware is used by default to ensure that the service will not crash during runtime due to panic
.
Function Signature:
func Default(opts ...config.Option) *Hertz
Example Code:
func main() {
h := server.Default()
h.Spin()
}
New
New
is used to initialize service and does not use the default Recovery
middleware.
Function Signature:
func New(opts ...config.Option) *Hertz
Example Code:
func main() {
h := server.New()
h.Spin()
}
Service Run and Exit
func (h *Hertz) Spin()
func (engine *Engine) Run() (err error)
func (h *Hertz) SetCustomSignalWaiter(f func(err chan error) error)
Spin
The Spin
function is used to run the Hertz server and can exit the service upon receiving an exit signal.
This function supports graceful shutdown of services. For detailed information on graceful shutdown, please refer to graceful-shutdown.
When using the function of service_discovery, Spin
will register the service into the registry when it is started, and use signalWaiter
to monitor service exceptions.
Function Signature:
func (h *Hertz) Spin()
Example Code:
func main() {
h := server.Default()
h.Spin()
}
Run
The Run
function is used to run the Hertz server and can exit the service upon receiving an exit signal.
This function does not support graceful shutdown of service. Unless there are special requirements, the Spin function is generally used to run service.
Function Signature:
func (engine *Engine) Run() (err error)
Example Code:
func main() {
h := server.Default()
if err := h.Run(); err != nil {
// ...
panic(err)
}
}
SetCustomSignalWaiter
The SetCustomimSignalWaiter
function is used to customize the processing function of the server after receiving signals. If no custom function is set, Hertz uses the waitSignal
function as the default implementation method for signal processing. For more details, please refer to graceful-shutdown.
Function Signature:
func (h *Hertz) SetCustomSignalWaiter(f func(err chan error) error)
Example Code:
func main() {
h := server.New()
h.SetCustomSignalWaiter(func(err chan error) error {
return nil
})
h.Spin()
}
Middleware
func (engine *Engine) Use(middleware ...app.HandlerFunc) IRoutes
Use
The Use
function is used to register the middleware into the router.
Hertz supports user-defined middleware, and has implemented some commonly used middleware. Please refer to hertz contrib for details.
The usage methods of middleware supported by Hertz include global registration, routing group level, and single routing level registration. For details, please refer to server-side-middleware.
The formal parameter of middleware
in the Use
function must be the http processing function of app.HandlerFunc
:
type HandlerFunc func (ctx context.Context, c *app.RequestContext)
Function Signature:
func (engine *Engine) Use(middleware ...app.HandlerFunc) IRoutes
Example Code:
func main() {
h := server.New()
// Register built-in Recovery middleware into routes.
h.Use(recovery.Recovery())
// Use custom middleware.
h.Use(exampleMiddleware())
}
func exampleMiddleware() app.handlerFunc {
return func(ctx context.Context, c *app.RequestContext) {
// Print logs before executing functions in Next.
hlog.Info("print before...")
// Use Next to execute the matching function in route.
c.Next(ctx)
// Print logs after executing functions in Next.
hlog.Ingo("print after...")
}
}
Streaming
Hertz supports server streaming processing, including streaming read and streaming write.
Note: Due to the different triggering modes between netpoll and go net, netpoll streaming is “pseudo” (due to LT triggering, data will be read into the buffer of the network library by the network library). In scenarios with large packets (such as uploading files), there may be memory issues, and it is recommended to use go net.
Streaming Write
Hertz Server supports streaming read request content.
Example Code:
func main() {
h := server.Default(server.WithHostPorts("127.0.0.1:8080"), server.WithStreamBody(true), server.WithTransport(standard.NewTransporter))
h.POST("/bodyStream", handler)
h.Spin()
}
func handler(ctx context.Context, c *app.RequestContext) {
// Acquire body streaming
bodyStream := c.RequestBodyStream()
// Read half of body bytes
p := make([]byte, c.Request.Header.ContentLength()/2)
r, err := bodyStream.Read(p)
if err != nil {
panic(err)
}
left, _ := ioutil.ReadAll(bodyStream)
c.String(consts.StatusOK, "bytes streaming_read: %d\nbytes left: %d\n", r, len(left))
}
Streaming Read
Hertz Server supports streaming write responses.
Two methods are provided:
-
The user passes in a
io.Reader
through thectx.SetBodyStream
function in the handler, and then reads and writes data in blocks in a similar manner to the example code (using channel to control data partitioning and read/write order) Note that data needs to be written asynchronously.If the user knows the total length of the transmitted data in advance, they can pass in the length in the
ctx.SetBodyStream
function for streaming writing, as shown in the example code/streamWrite1
.If the user does not know the total length of the transmitted data in advance, they can pass in -1 in the
ctx.SetBodyStream
function to write the stream in the form ofTransfer-Encoding: chunked
, as shown in the example code/streamWrite2
.Example Code:
func main() { h := server.Default(server.WithHostPorts("127.0.0.1:8080"), server.WithStreamBody(true), server.WithTransport(standard.NewTransporter)) h.GET("/streamWrite1", func(c context.Context, ctx *app.RequestContext) { rw := newChunkReader() line := []byte("line\r\n") ctx.SetBodyStream(rw, 500*len(line)) go func() { for i := 1; i <= 500; i++ { // For each streaming_write, the upload_file prints rw.Write(line) fmt.Println(i) time.Sleep(10 * time.Millisecond) } rw.Close() }() go func() { <-ctx.Finished() fmt.Println("request process end") }() }) h.GET("/streamWrite2", func(c context.Context, ctx *app.RequestContext) { rw := newChunkReader() // Content-Length may be negative: // -1 means Transfer-Encoding: chunked. ctx.SetBodyStream(rw, -1) go func() { for i := 1; i < 1000; i++ { // For each streaming_write, the upload_file prints rw.Write([]byte(fmt.Sprintf("===%d===\n", i))) fmt.Println(i) time.Sleep(100 * time.Millisecond) } rw.Close() }() go func() { <-ctx.Finished() fmt.Println("request process end") }() }) h.Spin() } type ChunkReader struct { rw bytes.Buffer w2r chan struct{} r2w chan struct{} } func newChunkReader() *ChunkReader { var rw bytes.Buffer w2r := make(chan struct{}) r2w := make(chan struct{}) cr := &ChunkReader{rw, w2r, r2w} return cr } var closeOnce = new(sync.Once) func (cr *ChunkReader) Read(p []byte) (n int, err error) { for { _, ok := <-cr.w2r if !ok { closeOnce.Do(func() { close(cr.r2w) }) n, err = cr.rw.Read(p) return } n, err = cr.rw.Read(p) cr.r2w <- struct{}{} if n == 0 { continue } return } } func (cr *ChunkReader) Write(p []byte) (n int, err error) { n, err = cr.rw.Write(p) cr.w2r <- struct{}{} <-cr.r2w return } func (cr *ChunkReader) Close() { close(cr.w2r) }
-
Users can use the
NewChunkedBodyWriter
method provided underpkg/protocol/http1/resp/writer
in the handler to hijack the response writer, and then use thectx.Write
function to write the partitioned data to the body and immediately send it to the client using thectx.Flush
function.Example Code:
h.GET("/flush/chunk", func(c context.Context, ctx *app.RequestContext) { // Hijack the writer of response ctx.Response.HijackWriter(resp.NewChunkedBodyWriter(&ctx.Response, ctx.GetWriter())) for i := 0; i < 10; i++ { ctx.Write([]byte(fmt.Sprintf("chunk %d: %s", i, strings.Repeat("hi~", i)))) // nolint: errcheck ctx.Flush() // nolint: errcheck time.Sleep(200 * time.Millisecond) } })
The difference between these two methods: the first method sends the data to the client in blocks after executing the handler logic, and the second method can send the partitioned data out in the handler logic.
For more example code, please refer to example.
Register Custom Protocol
func (engine *Engine) AddProtocol(protocol string, factory interface{})
Detailed information can be found in registration-of-custom-protocol-server-into-hertz.
SetClientIPFunc
The parameter f of this function will be passed to the RequestContext.SetClientIPFunc
function. The function and example code are shown in SetClientIPFunc.
Function Signature:
func (engine *Engine) SetClientIPFunc(f app.ClientIP)
SetFormValueFunc
The parameter f of this function will be passed to the RequestContext.SetFormValueFunc
function. The function and example code are shown in SetFormValueFunc.
Function Signature:
func (engine *Engine) SetFormValueFunc(f app.FormValueFunc)
Hooks
Hook function is a general concept that represents the operations that accompany an event when it is triggered.
Hertz provides global Hook injection capabilities for injecting its own processing logic after service triggering and before exiting. For detailed information, please refer to Hooks.
PanicHandler
Used to set the handler function when panic occurs in the program, default to nil
.
Note: If both
PanicHandler
andRecovery
middleware are set, theRecovery
middleware will override the handler logic ofPanicHandler
.
Example Code:
func main() {
h := server.New()
// When in Panic, the function in PanicHandler will be triggered, returning a 500 status code and carrying error information
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()
}
ContinueHandler
Call ContinueHandler after receiving the Expect 100 Continue header sent by the client. Using ContinueHandler, the server can decide whether to read potentially large request bodies, which will be read by default.
Example Code:
h := server.Default()
h.ContinueHandler = func(header *protocol.RequestHeader) bool {
return false
}
Rendering Template
Hertz provides methods such as Delims
, SetFuncMap
, LoadHTMLGlob
, and LoadHTMLFiles
for rendering HTML or template files. For detailed information, please refer to HTML.
Using NoRoute and NoMethod
Hertz provides NoRoute
and NoMethod
methods for global processing of HTTP 404 and 405 requests. For detailed information, please refer to NoRoute And NoMethod.
Get Route Information
func (engine *Engine) Routes() (routes RoutesInfo)
Routes
The Routes
function returns a slice divided by HTTP methods that contains routing information (HTTP method name, routing path, request handler function name).
Function Signature:
func (engine *Engine) Routes() (routes RoutesInfo)
Example Code:
func getHandler() app.HandlerFunc {
return func(c context.Context, ctx *app.RequestContext) {
ctx.String(consts.StatusOK, "get handler")
}
}
func postHandler() app.HandlerFunc {
return func(c context.Context, ctx *app.RequestContext) {
ctx.String(consts.StatusOK, "post handler")
}
}
func main() {
h := server.Default()
h.GET("/get", getHandler())
h.POST("/post", postHandler())
routesInfo := h.Routes()
fmt.Printf("%v\n", routesInfo)
// [{GET /get main.getHandler.func1 0xb2afa0} {POST /post main.postHandler.func1 0xb2b060}]
}
Transporter
func (engine *Engine) GetTransporterName() (tName string)
func SetTransporter(transporter func (options *config.Options) network.Transporter)
GetTransporterName
Obtain the name of the currently used network library, which now has two native options: go net
and netpoll
.
Linux uses netpoll
by default, while Windows can only use go net
.
If you have any doubts about how to use the corresponding network library, please refer to here.
Function Signature:
func (engine *Engine) GetTransporterName() (tName string)
Example Code:
h := server.New()
tName := h.GetTransporterName()
SetTransporter
SetTransporter
is used to set network library.
Note:
SetTransporter
only sets the global default values of the Engine, so usingWithTransporter
to set the network library when initializing the Engine will overwrite the settings ofSetTransporter
.
Function Signature:
func SetTransporter(transporter func (options *config.Options) network.Transporter)
Example Code:
route.SetTransporter(standard.NewTransporter)
Tracing
Hertz provides the capability of link tracking and also supports user-defined link tracking. For details, please refer to tracking.
Hijack
NoHijackConnPool
The hijacked connection used during Hertz connection hijacking is managed by pool management. Therefore, when the hijacked connection is used for websockets it does not support asynchronous operations.
The hijacked connection can only be closed once; closing it a second time will result in a null pointer exception.
NoHijackConnPool controls whether to use cache pools to obtain/release hijacked connections. Using pools improves performance of memory resource allocation but cannot prevent exceptions caused by closing connections twice.
If it is difficult to ensure that hijackConn won’t be closed repeatedly, it can be set as true.
Example Code:
package main
func main() {
// https://github.com/cloudwego/hertz/issues/121
h.NoHijackConnPool = true
}
HijackConnHandle
Set the Hijack connection processing function.
Function Signature:
func (engine *Engine) HijackConnHandle(c network.Conn, h app.HijackHandler)