golang入门笔记——Hertz

Hertz介绍

Hertz是字节跳动研发的企业级微服务HTTP框架具有高易用性、易扩展、低时延等特点。是基于自研网络库Netpoll开发的内部框架Hertz。Hertz框架整体上满足
1.极致性能优化的问题性
2.面对未来不可控需求的扩展能力Hertz采用了4层分层设计应用层、路由层、协议层、传输层保证各个层级功能内聚同时通过层级之间的接口达到灵活扩展的目标。整体框架如下图

image.png

应用层

主要包括与用户直接交互的易用的API主要包括Server、Client和一些其他通用抽象。Hertz框架最具特点的是处理函数HandlerFunc多了一个context上下文这是在大量的实践过程中发现的业务方通常需要一个标准的上下文在RPC Client或者日志、Tracing等组件间传递但由于请求上下文RequestContext生命周期局限于一次HTTP请求之内而以上提到的场景往往存在异步的传递和处理 导致如果直接传递请求上下文会导致出现一些数据不一致的问题。因此最终增加了一个标准的上下文入参从根本上解决各种因为上下文生命周期不一致的异常问题处理函数的格式为
type HandlerFunc func(c context.Context,ctx *app.RequestContext)

路由层

Hertz在设计路由时给了用户极高的自由度去注册路由支持的路由有支持静态路由、参数路由的注册支持按优先级匹配支持路由回溯支持尾斜线重定向。例如
1.优先级匹配如/a/b和/:c/b同时满足时优先匹配/a/b
2.路由回溯如注册/a/b和/:c/d当匹配/a/d时仍然能够匹配上/:c/d
3.支持尾斜线重定向如注册/a/b当匹配/a/b/时重定向到/a/b上

协议层

协议层负责不同协议的实现和扩展。Hertz支持协议的扩展用户只需要实现下面的接口便可以按照自己的需求在引擎上扩展协议同时也支持通过ALPN协议协商的方式注册。Hertz首批只开源了 HTTP1 实现未来会陆续开源 HTTP2、QUIC 等实现。协议层扩展提供的灵活性甚至可以超越 HTTP 协议的范畴用户完全可以按需注册任意符合自身需求的协议层实现并且加入到Hertz的引擎中来同时也能够无缝享受到传输层带来的极致性能。

传输层

传输层负责底层的网络库的抽象和实现
Hertz支持底层网络库的扩展。Hertz原生完美匹配Netpoll在时延方面有很多深度的优化Netpoll对TLS能力的支持有待完善为此Hertz底层同时支持基于Golang标准网络库的实现适配同时支持网络库的一键切换用户可根据自己的需求选择合适的网络库进行替换。如果用户有更加高效的网络库或其他网络库需求也完全可以根据需求自行扩展。

HZ脚手架

用户可以提供一个IDL利用命令行工具Hz一键生成项目脚手架。Hz也支持基于IDL的更新能力能够基于IDL变动智能地更新项目代码。Hz支持Thrift和Protobuf两种IDL定义。

Hertz的使用

一个简单的案例利用Hertz监听8080端口并编写/ping的get处理函数

package main

import (
   "context"
   "github.com/cloudwego/hertz/pkg/app"
   "github.com/cloudwego/hertz/pkg/app/server"
   "github.com/cloudwego/hertz/pkg/common/utils"
   "net/http"
)

func main() {
   h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
   h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
      ctx.JSON(http.StatusOK, utils.H{"ping": "pong"})
   })
   h.Spin()
}

Hertz和gin一样提供了分组路由的功能

v1 := h.Group("/v1")
{
   v1.GET("/login", func(c context.Context, ctx *app.RequestContext) {
   })

}
v2 := h.Group("/v2")
{
   v2.GET("/login", func(c context.Context, ctx *app.RequestContext) {

   })
}

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

h.GET("/herz/:version", func(c context.Context, ctx *app.RequestContext) {

})
h.GET("/herz/*action", func(c context.Context, ctx *app.RequestContext) {

})

参数绑定Bind、Validate、BindAndValidate用于参数绑定和校验

image.png

参数校验

type InfoRequest struct {
		Name         string   `vd:"($!='Alice'||(Age)$==18) && regexp('\w')"`
		Age          int      `vd:"$>0"`
		Email        string   `vd:"email($)"`
		Phone1       string   `vd:"phone($)"`
		OtherPhones  []string `vd:"range($, phone(#v,'CN'))"`
		*InfoRequest `vd:"?"`
		Info1        *InfoRequest `vd:"?"`
		Info2        *InfoRequest `vd:"-"`
	}

自定义验证函数

import "github.com/cloudwego/hertz/pkg/app/server/binding"

func init() {
    binding.MustRegValidateFunc("test", func(args ...interface{}) error {
       if len(args) != 1 {
          return fmt.Errorf("the args must be one")
       }
       s, _ := args[0].(string)
       if s == "123" {
          return fmt.Errorf("the args can not be 123")
       }
       return nil
    })
}

参数绑定优先级
path > form > query > cookie > header > json > raw_body

type Args struct {
   Query      string   `query:"query"vd:"$!='Hertz'"`
   QuerySlice []string `query:"queryslice"`
   Path       string   `path:"path"`
   Header     string   `header:"header"`
   Form       string   `form:"form"`
   Json       string   `json:"json"`
}

func main(){
h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
h.POST("v:path/bind", func(c context.Context, ctx *app.RequestContext) {
   var arg Args
   err := ctx.BindAndValidate(&arg)
   if err != nil {
      panic(err)
   }
})
}

中间件

func MyMiddleware() app.HandlerFunc {
   return func(c context.Context, ctx *app.RequestContext) {
      fmt.Println("pre-handle")
      ctx.Next(c)
      fmt.Println("post-handle")
   }
}
func main() {
   h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
   h.Use(MyMiddleware())
}

HTTP Client

Hertz提供了HTTP Client来模拟用户的请求

package main

import (
   "context"
   "fmt"
   "github.com/cloudwego/hertz/pkg/app/client"
   "github.com/cloudwego/hertz/pkg/protocol"
)

func main() {
   c, err := client.NewClient()
   if err != nil {
      return
   }
   status, body, _ := c.Get(context.Background(), nil, "http://baidu.com")
   fmt.Printf("status:%v body:%v", status, string(body))
   var postArgs protocol.Args
   postArgs.Set("arg", "a")
   status, body, _ = c.Post(context.Background(), nil, "http://baidu.com", &postArgs)
   fmt.Printf("status:%v body:%v", status, string(body))
}

Hertz代码生成工具

编写IDL

namespace go hello.example

struct HelloReq{
    1:string Name (api.query="name");
}

struct HelloResp{
    1:string RespBody;
}

service HelloService{
    HelloResp HelloMethod(1:HelloReq request)(api.get="/hello")
}

利用hz工具生成代码

hz new -module mod_name -idl idl/hello.thrift

生成的代码目录结构

image.png

生成的主要代码
biz/handler/hello/example/hello_service.go

// Code generated by hertz generator.

package example

import (
   "context"

   "github.com/cloudwego/hertz/pkg/app"
   "github.com/cloudwego/hertz/pkg/protocol/consts"
   example "hertz-demo/biz/model/hello/example"
)

// HelloMethod .
// @router /hello [GET]
func HelloMethod(ctx context.Context, c *app.RequestContext) {
   var err error
   var req example.HelloReq
   err = c.BindAndValidate(&req)
   if err != nil {
      c.String(consts.StatusBadRequest, err.Error())
      return
   }

   resp := new(example.HelloResp)

   c.JSON(consts.StatusOK, resp)
}

biz/router/hello/example/hello.go

// Code generated by hertz generator. DO NOT EDIT.

package Example

import (
   "github.com/cloudwego/hertz/pkg/app/server"
   example "hertz-demo/biz/handler/hello/example"
)

/*
 This file will register all the routes of the services in the master idl.
 And it will update automatically when you use the "update" command for the idl.
 So don't modify the contents of the file, or your code will be deleted when it is updated.
*/

// Register register routes based on the IDL 'api.${HTTP Method}' annotation.
func Register(r *server.Hertz) {

   root := r.Group("/", rootMw()...)
   root.GET("/hello", append(_hellomethodMw(), example.HelloMethod)...)
}
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: go