Go语言学习指南——HTTP框架Hertz

HTTP协议

超文本传输协议(Hypertext Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在 TCP 之上。它指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应。请求和响应消息的头以 ASCII 形式给出;而消息内容则具有一个类似 MIME 的格式。

HTTP是基于客户/服务器模式,且面向连接的。典型的HTTP事务处理有如下的过程: 

  1. 客户与服务器建立连接;
  2. 客户向服务器提出请求;
  3. 服务器接受请求,并根据请求返回相应的文件作为应答;
  4. 客户与服务器关闭连接。

HTTP框架

HTTP框架采用分层设计,各层之间通过 API 通信,这样提高了代码的复用性和拓展性。

  • 应用层——提供给用户使用的接口,需要保证可理解性和简单性
  • 中间件层——对请求进行预处理和后处理
  • 路由层——为 url 请求匹配相应的 Handler 处理函数
  • 协议层——抽象出合适的接口,在连接上读取数据
  • 传输层——为网络I/O提供了一个可移植的接口,包括TCP/IP、UDP、域名解析和Unix域套接字

Hertz介绍

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

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

image.png

Hertz 从上到下分为:应用层、路由层、协议层和传输层,每一层各司其职,同时公共能力被统一抽象到公共层(Common),做到跨层级复用。另外,同主库一同发布的还有作为子模块的 Hz 脚手架,它能够协助使用者快速搭建出项目核心骨架以及提供实用的构建工具链。

框架特点

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

框架结构

应用层

应用层是和用户直接交互的一层,提供丰富易用的 API,主要包括Server、Client 和一些其他通用抽象。Server 提供了注册 HandlerFunc、Binding、Rendering 等能力;Client 提供了调用下游和服务发现等能力;以及抽象一个 HTTP 请求所必须涉及到的请求(Request)、响应(Response)、上下文(RequestContext)、中间件(Middleware)等等。Hertz 的 Server 和 Client 都能够提供中间件这样的扩展能力。

路由层

路由层负责根据 URI 匹配对应的处理函数。

起初,Hertz 的路由基于 httprouter开发,但随着使用的用户越来越多,httprouter 渐渐不能够满足需求,主要体现在 httprouter 不能够同时注册静态路由和参数路由,即 /a/b/:c/d 这两个路由不能够同时注册;甚至有一些更特殊的需求,如 /a/b/:c/b ,当匹配 /a/b 路由时,两个路由都能够匹配上。

Hertz 为满足这些需求重新构造了路由树,用户在注册路由时拥有很高的自由度:支持静态路由、参数路由的注册;支持按优先级匹配,如上述例子会优先匹配静态路由 /a/b;支持路由回溯,如注册 /a/b/:c/d,当匹配 /a/d 时仍然能够匹配上;支持尾斜线重定向,如注册 /a/b,当匹配 /a/b/ 时能够重定向到 /a/b 上。Hertz 提供了丰富的路由能力来满足用户的需求,更多的功能可以参考 Hertz 配置文档。

协议层

协议层负责不同协议的实现和扩展。

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

传输层

传输层负责底层的网络库的抽象和实现。

Hertz 支持底层网络库的扩展。Hertz 原生完美适配 Netpoll,在时延方面有很多深度的优化,非常适合时延敏感的业务接入。Netpoll 对 TLS 能力的支持有待完善,而 TLS 能力又是 HTTP 框架必备能力,为此 Hertz 底层同时支持基于 Golang 标准网络库的实现适配,同时支持网络库的一键切换,用户可根据自己的需求选择合适的网络库进行替换。如果用户有更加高效的网络库或其他网络库需求,也完全可以根据需求自行扩展。

Hz 脚手架

与 Hertz 一并开源的还有一个易用的命令行工具 Hz,用户只需提供一个 IDL,根据定义好的接口信息,Hz 便可以一键生成项目脚手架,让 Hertz 达到开箱即用的状态;Hz 也支持基于 IDL 的更新能力,能够基于 IDL 变动智能地更新项目代码。目前 Hz 支持了 Thrift 和 Protobuf 两种 IDL 定义。命令行工具内置丰富的选项,可以根据自己的需求使用。同时它底层依赖 Protobuf 官方的编译器和自研的 Thriftgo 的编译器,两者都支持自定义的生成代码插件。如果默认模板不能够满足需求,完全能够按需定义。

Common 组件

Common 组件主要存放一些公共的能力,比如错误处理、单元测试能力、可观测性相关能力(Log、Trace、Metrics 等)。对于服务可观测性的能力,Hertz 提供了默认的实现,用户可以按需装配;如果用户有特殊的需求,也可以通过 Hertz 提供的接口注入。比如对于 Trace 能力,Hertz 提供了默认的实现,也提供了将 Hertz 和 Kitex 串起来的 Example。

功能特性

中间件

Hertz 除了提供 Server 的中间件能力,还提供了 Client 中间件能力。用户可以使用中间件能力将通用逻辑(如:日志记录、性能统计、异常处理、鉴权逻辑等等)和业务逻辑区分开,让用户更加专注于业务代码。Server 和 Client 中间件使用方式相同,使用 Use 方法注册中间件,中间件执行顺序和注册顺序相同,同时支持预处理和后处理逻辑。

Server 和 Client 的中间件实现方式并不相同。对于 Server 来说,我们希望减少栈的深度,同时也希望中间件能够默认的执行下一个,用户需要手动终止中间件的执行。因此,我们将 Server 的中间件分成了两种类型,即不在同一个函数调用栈(该中间件调用完后返回,由上一个中间件调用下一个中间件)和在同一个函数调用栈的中间件(该中间件调用完后由该中间件继续调用下一个中间件)。其核心是需要一个地方存下当前的调用位置 index,并始终保持其递增。恰好 RequestContext 就是一个存储 index 合适的位置。但是对于 Client,由于没有合适的地方存储 index,我们只能退而求其次,抛弃 index 的实现,将所有的中间件构造在同一调用链上,需要用户手动调用下一个中间件。

框架路由

根据请求的 URI 选择对应的处理函数。

  • 首先匹配 HTTP 方法
  • 静态路由: 精确匹配注册的路由,如:/a/b/c、/a/b/d
  • 参数路由:
    ** 命名参数:形如:name这类叫做命名参数,命名参数只匹配单个路径段:
Pattern: /user/:user  

 /user/gordon              match(user = gordon)  
 /user/you                 match(user = you)  
 /user/gordon/profile      no match  
 /user/                    no match 

通配参数:形如 action这类叫做通配参数,就像名字所暗示的那样,它们匹配所有内容。因此,它们必须始终位于模式的末尾:

Pattern: /src/*filepath  
  
 /src/                     match(filepath = "")  
 /src/somefile.go          match(filepath = somefile.go)  
 /src/subdir/somefile.go   match(filepath = subdie/somefile.go)  
  • 路由修复: 如果只注册了 /a/b,但是访问的 URI 是 /a/b/,那可以提供自动重定向到 /a/b 能力;同样,如果只注册了 /a/b/,但是访问的 URI 是 /a/b,那可以提供自动重定向到 /a/b/ 能力
  • 冲突路由:同时注册 /a/b 和 /:id/b,并设定优先级。比如:当请求 URI 为 /a/b 时,优先匹配静态路由 /a/b
流式处理

Hertz 提供 Server 和 Client 的流式处理能力。HTTP 的文件场景是十分常见的场景,除了 Server 侧的上传场景之外,Client 的下载场景也十分常见。为此,Hertz 支持了 Server 和 Client 的流式处理。在内部网关场景中,从 Gin 迁移到 Hertz 后,CPU 使用量随流量大小不同可节省 30%—60% 不等,服务压力越大,收益越大。Hertz 开启流式功能的方式也很容易,只需要在 Server 上或 Client 上添加一个配置即可,可参考 CloudWeGo 官网 Hertz 文档的流式处理部分。

Hertz框架使用

首先,定义 IDL,这里使用 Thrift 作为 IDL 的定义(也支持使用 Protobuf 定义的 IDL),编写一个名为 Demo 的 service。这个服务有一个 API: Hello,它的请求参数是一个 query,响应是一个包含一个 RespBody 字段的 Json。

namespace go hello.example

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

struct HelloRes0 {
    1: string RespBody;
}

service HelloService {
    HelloResp Hello(1: HelloReq request) (qpi.get="/hello");
}

接下来我们使用 Hz 生成代码,并整理和拉取依赖。 

hz new -idl idl/hello.thrift -mod Demo
go mod tidy && go mod verify

 填充业务逻辑,比如我们返回 hello,${Name} ,那我们在 biz/handler/example/ hello_service.go 中添加以下代码即可。

//Hello .
func Hello(ctx context.Context, c *app.RequestContext) {
    var err error
    var req example.HelloReq
    err = c.BindAndValidate(&req)
    if err != nil {
        c.String(400, err.Error())
        return
    }
    
    resp := new(example.HelloResp)
    resp.RespBody = "hello, " + req.Name
    c.JSON(200, resp)
}

编译并运行项目。

go build
./Demo

 到现在一个简单的 Hertz 项目已经生成,下面我们来测试一下。

$ curl http:
$ {"RespBody":"hello, Xiaoming"}

以上 Demo 可以在 Hertz-Examples 中查看,之后就可以愉快地构建自己的项目了。

小结

本文希望以上的分享能够让大家对 Hertz 有一个整体上的认识。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值