Skip to content

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 的基础概念:

  1. 架构设计:Engine、RequestContext、Router、Middleware
  2. 创建应用:默认配置、自定义配置
  3. 请求处理:获取请求信息、查询参数、路径参数
  4. 响应处理:文本、JSON、文件、重定向
  5. 静态文件服务:静态目录、静态文件
  6. 启动与关闭:基本启动、优雅关闭

在下一章中,我们将深入学习 Hertz 的路由与中间件。