HTTP 框架 Hertz 入门 | 青训营笔记

340 阅读6分钟

这是我参与「第五届青训营 」笔记创作活动的第 12 天

前言

从本篇笔记开始将介绍 Go 框架三件套(Web / RPC / ORM),框架的学习有助于后续课程的学习以及大项目的完成。本文主要介绍字节跳动的开源 Golang 微服务 HTTP 框架 Hertz。

重点内容

  • Hertz 简介
  • 快速开始
  • 路由
  • Hertz 代码生成工具 hz

知识点介绍

Hertz 简介

Hertz 是一个 Golang 微服务 HTTP 框架,在设计之初参考了其他开源框架 fasthttpginecho 的优势, 并结合字节跳动内部的需求,使其具有高易用性、高性能、高扩展性等特点,目前在字节跳动内部已广泛使用。 如今越来越多的微服务选择使用 Golang,如果对微服务性能有要求,又希望框架能够充分满足内部的可定制化需求,Hertz 会是一个不错的选择。

架构设计:Hertz采用了4层分层设计(应用层、路由层、协议层、传输层),保证各个层级功能内聚,同时通过层级之间的接口达到灵活扩展的目标。

hertz.png

框架特点:

  • 高易用性:在开发过程中,快速写出来正确的代码往往是更重要的。因此,在 Hertz 在迭代过程中,积极听取用户意见,持续打磨框架,希望为用户提供一个更好的使用体验,帮助用户更快的写出正确的代码。
  • 高性能:Hertz 默认使用自研的高性能网络库 Netpoll,在一些特殊场景相较于 go net,Hertz 在 QPS、时延上均具有一定优势。
  • 高扩展性:Hertz 采用了分层设计,提供了较多的接口以及默认的扩展实现,用户也可以自行扩展。同时得益于框架的分层设计,框架的扩展性也会大很多。
  • 多协议支持:Hertz 框架原生提供 HTTP1.1、ALPN 协议支持。除此之外,由于分层设计,Hertz 甚至支持自定义构建协议解析逻辑,以满足协议层扩展的任意需求。
  • 网络层切换能力:Hertz 实现了 Netpoll 和 Golang 原生网络库 间按需切换能力,用户可以针对不同的场景选择合适的网络库,同时也支持以插件的方式为 Hertz 扩展网络库实现。

快速开始

准备 Golang 开发环境

首先需要安装 Golang,推荐安装最新版本。可参考 golang.org/doc/install

目前,Hertz 支持 Linux、macOS、Windows 系统。

快速上手

  • 安装命令行工具 hz

    • 确保环境变量已经正确定义

    • 安装 hz

      go install github.com/cloudwego/hertz/cmd/hz@latest
      
  • 生成/编写示例代码

    • 在当前目录下创建 hertz_demo 文件夹,进入该目录中

    • 创建 main.go 文件

    • main.go 文件中添加以下代码

      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/cloudwego/hertz/pkg/protocol/consts"
      )
      
      func main() {
          h := server.Default()
      
          h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
                  ctx.JSON(consts.StatusOK, utils.H{"message": "pong"})
          })
      
          h.Spin()
      }
      
    • 生成 go.mod 文件

      go mod init hertz_demo
      
    • 整理 & 拉取依赖

      go mod tidy
      
  • 运行示例代码

    go build -o hertz_demo
    go run hertz_demo
    

    如果成功,将看到

    2023/01/30 11:26:27.824241 engine.go:617: [Debug] HERTZ: Method=GET    absolutePath=/ping                     --> handlerName=main.main.func1 (num=2 handlers)
    2023/01/30 11:26:27.836518 engine.go:389: [Info] HERTZ: Using network library=standard
    2023/01/30 11:26:27.837518 transport.go:65: [Info] HERTZ: HERTZ: HTTP server listening on address=[::]:8888
    

    之后对接口进行测试:

    curl http://127.0.0.1:8888/ping
    

    如果成功就可以看到以下输出

    StatusCode        : 200
    StatusDescription : OK
    Content           : {"message":"pong"}
    RawContent        : HTTP/1.1 200 OK
                        Content-Length: 18
                        Content-Type: application/json; charset=utf-8
                        Date: Mon, 30 Jan 2023 03:26:53 GMT
                        Server: hertz
    
                        {"message":"pong"}
    Forms             : {}
    Headers           : {[Content-Length, 18], [Content-Type, application/json; charset=utf-8], [Date, Mon, 30 Jan 2023 03:26:53 GMT], [Server,  
                        hertz]}
    Images            : {}
    InputFields       : {}
    Links             : {}
    ParsedHtml        : mshtml.HTMLDocumentClass
    RawContentLength  : 18
    

到此就已经成功启动了 Hertz Server,并完成了一次调用。

路由

路由注册

Hertz 提供了 GETPOSTPUTDELETEANY 等方法用于注册路由。

方法介绍
Hertz.GET用于注册 HTTP Method 为 GET 的方法
Hertz.POST用于注册 HTTP Method 为 POST 的方法
Hertz.DELETE用于注册 HTTP Method 为 DELETE 的方法
Hertz.PUT用于注册 HTTP Method 为 PUT 的方法
Hertz.PATCH用于注册 HTTP Method 为 PATCH 的方法
Hertz.HEAD用于注册 HTTP Method 为 HEAD 的方法
Hertz.OPTIONS用于注册 HTTP Method 为 OPTIONS 的方法
Hertz.Handle这个方法支持用户手动传入 HTTP Method 用来注册方法,当用于注册普通的 HTTP Method 方法时和上述的方法作用是一致的,并且这个方法同时也支持用于注册自定义 HTTP Method 方法
Hertz.Any用于注册所有 HTTP Method 方法
Hertz.StaticFile/Static/StaticFS用于注册静态文件

示例代码:

package main

import (
	"context"
	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func main(){
	h := server.Default(server.WithHostPorts("127.0.0.1:8080"))

	h.StaticFS("/", &app.FS{Root: "./", GenerateIndexPages: true})

	h.GET("/get", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "get")
	})
	h.POST("/post", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "post")
	})
	h.PUT("/put", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "put")
	})
	h.DELETE("/delete", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "delete")
	})
	h.PATCH("/patch", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "patch")
	})
	h.HEAD("/head", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "head")
	})
	h.OPTIONS("/options", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "options")
	})
	h.Any("/ping_any", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "any")
	})
	h.Handle("LOAD","/load", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "load")
	})
	h.Spin()
}

路由组

Hertz 提供了路由组( Group )的能力,用于支持路由分组的功能,同时中间件也可以注册到路由组上。

示例代码:

package main

import (
	"context"
	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func main(){
	h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
	v1 := h.Group("/v1")
	v1.GET("/get", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "get")
	})
	v1.POST("/post", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "post")
	})
	v2 := h.Group("/v2")
	v2.PUT("/put", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "put")
	})
	v2.DELETE("/delete", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "delete")
	})
	h.Spin()
}

路由类型

Hertz 支持丰富的路由类型用于实现复杂的功能,包括静态路由、参数路由、通配路由。

路由的优先级:静态路由 > 命名路由 > 通配路由

  • 静态路由如上文

  • 参数路由:

    • Hertz 支持使用 :name 这样的命名参数设置路由,并且命名参数只匹配单个路径段。

    • 如果我们设置/user/:name路由,匹配情况如下

      路径是否匹配
      /user/gordon匹配
      /user/you匹配
      /user/gordon/profile不匹配
      /user/不匹配
    • 通过使用 RequestContext.Param 方法,我们可以获取路由中携带的参数。

      package main
      
      import (
      	"context"
      	"github.com/cloudwego/hertz/pkg/app"
      	"github.com/cloudwego/hertz/pkg/app/server"
      	"github.com/cloudwego/hertz/pkg/protocol/consts"
      )
      
      func main(){
      	h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
      	// This handler will match: "/hertz/version", but will not match : "/hertz/" or "/hertz"
      	h.GET("/hertz/:version", func(ctx context.Context, c *app.RequestContext) {
      		version := c.Param("version")
      		c.String(consts.StatusOK, "Hello %s", version)
      	})
      	h.Spin()
      }
      
  • 通配路由

    • Hertz 支持使用 *path 这样的通配参数设置路由,并且通配参数会匹配所有内容。

    • 如果我们设置/src/*path路由,匹配情况如下

      路径是否匹配
      /src/匹配
      /src/somefile.go匹配
      /src/subdir/somefile.go匹配
    • 通过使用 RequestContext.Param 方法,我们可以获取路由中携带的参数。

      package main
      
      import (
      	"context"
      	"github.com/cloudwego/hertz/pkg/app"
      	"github.com/cloudwego/hertz/pkg/app/server"
      	"github.com/cloudwego/hertz/pkg/protocol/consts"
      )
      
      func main(){
      	h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
      	// However, this one will match "/hertz/v1/" and "/hertz/v2/send"
      	h.GET("/hertz/:version/*action", func(ctx context.Context, c *app.RequestContext) {
      		version := c.Param("version")
      		action := c.Param("action")
      		message := version + " is " + action
      		c.String(consts.StatusOK, message)
      	})
      	h.Spin()
      }
      

Hertz 代码生成工具 hz

hz 是 Hertz 框架提供的一个用于生成代码的命令行工具。目前,hz 可以基于 thrift 和 protobuf 的 IDL 生成 Hertz 项目的脚手架。

基本使用:

  • 创建一个 Hertz 新项目

    // GOPATH 下执行,go mod 名字默认为当前路径相对GOPATH的路径,也可自己指定
    hz new
    
    // 非GOPATH 下执行,需要指定 go mod 名
    hz new -mod hertz/demo
    
    // 整理 & 拉取依赖
    go mod tidy
    
  • 编译项目

    go build
    
  • 运行项目

    ./{{your binary}}
    
  • 测试

    curl 127.0.0.1:8888/ping
    

    如果返回{"message":"pong"},说明接口调通。

总结

本文主要介绍了 Golang 微服务 HTTP 框架 Hertz,只是简单的入门练习,如果想了解更多内容还是需要仔细研究官方文档,官方文档的内容很清晰全面,后续还需要深入学习。

引用

  1. Hertz 官方文档 Hertz | CloudWeGo