Hertz 基础
概述
Hertz 是 CloudWeGo 的高性能 HTTP 框架,专为构建微服务和 API 网关设计。Hertz 参考了 Gin 框架的设计,但底层使用自研的高性能网络库 Netpoll,在性能和稳定性方面表现优异,经过字节跳动超大规模流量验证。
核心内容
Hertz 架构
┌─────────────────────────────────────────────────────────────┐
│ Application │
│ (User Handler/Business Logic) │
├─────────────────────────────────────────────────────────────┤
│ Hertz Core │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Router │ │ Context │ │Middleware│ │ Codec │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Protocol Layer │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ HTTP/1.1 │ │ HTTP/2 │ │ HTTPS │ │
│ └──────────┘ └──────────┘ └──────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Netpoll │
│ (High Performance Network) │
└─────────────────────────────────────────────────────────────┘核心概念
1. Engine
Engine 是 Hertz 的核心引擎,负责处理 HTTP 请求:
go
package main
import (
"github.com/cloudwego/hertz/pkg/app/server"
)
func main() {
h := server.Default()
h.GET("/hello", func(c context.Context, ctx *app.RequestContext) {
ctx.String(200, "Hello, Hertz!")
})
h.Spin()
}2. RequestContext
RequestContext 封装了请求和响应:
go
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
)
func handler(c context.Context, ctx *app.RequestContext) {
// 获取请求方法
method := string(ctx.Method())
// 获取请求路径
path := string(ctx.URI().Path())
// 获取请求头
contentType := string(ctx.GetHeader("Content-Type"))
// 获取查询参数
name := ctx.Query("name")
// 设置响应
ctx.JSON(200, map[string]interface{}{
"method": method,
"path": path,
"name": name,
"message": "Hello, " + name,
})
}3. Router
路由器负责将请求路由到对应的处理器:
go
h := server.Default()
// 基本路由
h.GET("/users", listUsers)
h.POST("/users", createUser)
h.PUT("/users/:id", updateUser)
h.DELETE("/users/:id", deleteUser)
// 路由组
api := h.Group("/api/v1")
{
api.GET("/users", listUsers)
api.POST("/users", createUser)
}4. Middleware
中间件用于处理通用逻辑:
go
func Logger() app.HandlerFunc {
return func(c context.Context, ctx *app.RequestContext) {
start := time.Now()
ctx.Next(c)
latency := time.Since(start)
klog.Infof("[%s] %s %d %v",
ctx.Method(),
ctx.URI().Path(),
ctx.Response.StatusCode(),
latency,
)
}
}
h := server.Default()
h.Use(Logger())创建应用
默认配置
go
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
)
func main() {
h := server.Default()
h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(200, map[string]string{
"message": "pong",
})
})
h.Spin()
}自定义配置
go
package main
import (
"time"
"github.com/cloudwego/hertz/pkg/app/server"
)
func main() {
h := server.New(
server.WithHostPorts(":8080"),
server.WithMaxRequestBodySize(20*1024*1024),
server.WithReadTimeout(30*time.Second),
server.WithWriteTimeout(30*time.Second),
server.WithIdleTimeout(60*time.Second),
server.WithKeepAlive(true),
server.WithNetwork("tcp"),
server.WithTLS("cert.pem", "key.pem"),
)
h.GET("/hello", func(c context.Context, ctx *app.RequestContext) {
ctx.String(200, "Hello, World!")
})
h.Spin()
}请求处理
获取请求信息
go
func handler(c context.Context, ctx *app.RequestContext) {
// 请求方法
method := string(ctx.Method())
// 请求路径
path := string(ctx.URI().Path())
// 完整 URL
uri := ctx.URI().String()
// 请求头
ctx.VisitAllHeaders(func(key, value []byte) {
fmt.Printf("%s: %s\n", key, value)
})
// Cookie
session := ctx.Cookie("session")
// 请求体
body := ctx.Request.Body()
// 客户端 IP
clientIP := ctx.ClientIP()
ctx.JSON(200, map[string]interface{}{
"method": method,
"path": path,
"uri": string(uri),
"client_ip": string(clientIP),
})
}获取查询参数
go
func handler(c context.Context, ctx *app.RequestContext) {
// 单个参数
name := ctx.Query("name")
age := ctx.DefaultQuery("age", "18")
// 多个同名参数
tags := ctx.QueryArray("tags")
// 参数 map
params := ctx.QueryArgs().String()
ctx.JSON(200, map[string]interface{}{
"name": name,
"age": age,
"tags": tags,
})
}获取路径参数
go
h.GET("/users/:id", func(c context.Context, ctx *app.RequestContext) {
id := ctx.Param("id")
ctx.String(200, "User ID: %s", id)
})
h.GET("/files/*filepath", func(c context.Context, ctx *app.RequestContext) {
filepath := ctx.Param("filepath")
ctx.String(200, "File path: %s", filepath)
})响应处理
文本响应
go
func handler(c context.Context, ctx *app.RequestContext) {
ctx.String(200, "Hello, World!")
ctx.String(200, "Hello, %s!", "Hertz")
}JSON 响应
go
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
func handler(c context.Context, ctx *app.RequestContext) {
ctx.JSON(200, map[string]interface{}{
"code": 0,
"message": "success",
"data": map[string]interface{}{
"id": 1,
"name": "Hertz",
},
})
ctx.JSON(200, &User{
ID: 1,
Name: "Hertz",
})
}文件响应
go
func handler(c context.Context, ctx *app.RequestContext) {
ctx.File("./static/index.html")
ctx.FileAttachment("./files/report.pdf", "report.pdf")
ctx.Data(200, "application/pdf", fileBytes)
}重定向
go
func handler(c context.Context, ctx *app.RequestContext) {
ctx.Redirect(302, []byte("https://example.com"))
ctx.Redirect(301, []byte("/new-path"))
}静态文件服务
go
func main() {
h := server.Default()
// 静态文件目录
h.Static("/static", "./static")
// 静态文件
h.StaticFile("/", "./public/index.html")
// 静态文件系统
h.StaticFS("/assets", &app.FS{
Root: "./assets",
Index: "index.html",
ListFiles: true,
})
h.Spin()
}启动与关闭
基本启动
go
func main() {
h := server.Default()
h.GET("/hello", func(c context.Context, ctx *app.RequestContext) {
ctx.String(200, "Hello!")
})
h.Spin()
}优雅关闭
go
func main() {
h := server.Default()
h.GET("/hello", func(c context.Context, ctx *app.RequestContext) {
ctx.String(200, "Hello!")
})
go func() {
if err := h.Run(); err != nil {
klog.Fatal(err)
}
}()
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := h.Shutdown(ctx); err != nil {
klog.Errorf("shutdown error: %v", err)
}
klog.Info("server stopped")
}完整示例
go
package main
import (
"context"
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/utils"
)
func main() {
h := server.Default(
server.WithHostPorts(":8080"),
)
h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(200, utils.H{
"message": "pong",
"time": time.Now().Format(time.RFC3339),
})
})
h.GET("/users/:id", func(c context.Context, ctx *app.RequestContext) {
id := ctx.Param("id")
ctx.JSON(200, utils.H{
"id": id,
"name": "User " + id,
})
})
h.POST("/users", func(c context.Context, ctx *app.RequestContext) {
var req struct {
Name string `json:"name" binding:"required"`
Email string `json:"email" binding:"required,email"`
}
if err := ctx.BindJSON(&req); err != nil {
ctx.JSON(400, utils.H{
"error": err.Error(),
})
return
}
ctx.JSON(201, utils.H{
"id": 1,
"name": req.Name,
"email": req.Email,
})
})
go func() {
log.Println("server starting on :8080")
if err := h.Run(); err != nil {
log.Fatalf("server error: %v", err)
}
}()
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
log.Println("shutting down...")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := h.Shutdown(ctx); err != nil {
log.Printf("shutdown error: %v", err)
}
log.Println("server stopped")
}小结
本章介绍了 Hertz 的基础概念:
- 架构设计:Engine、RequestContext、Router、Middleware
- 创建应用:默认配置、自定义配置
- 请求处理:获取请求信息、查询参数、路径参数
- 响应处理:文本、JSON、文件、重定向
- 静态文件服务:静态目录、静态文件
- 启动与关闭:基本启动、优雅关闭
在下一章中,我们将深入学习 Hertz 的路由与中间件。