日本电影一区二区_日本va欧美va精品发布_日本黄h兄妹h动漫一区二区三区_日本欧美黄色

字節(jié)跳動開源 Go HTTP 框架 Hertz 設(shè)計(jì)實(shí)踐

前言

Hertz 是字節(jié)跳動服務(wù)框架團(tuán)隊(duì)研發(fā)的超大規(guī)模的企業(yè)級微服務(wù) HTTP 框架,具有高易用性、易擴(kuò)展、低時(shí)延等特點(diǎn)。在經(jīng)過了字節(jié)跳動內(nèi)部一年多的使用和迭代,如今已在 CloudWeGo 正式開源。目前,Hertz 已經(jīng)成為了字節(jié)跳動內(nèi)部最大的 HTTP 框架,線上接入的服務(wù)數(shù)量超過 1 萬,峰值 QPS 超過 4 千萬。除了各個(gè)業(yè)務(wù)線的同學(xué)使用外,也服務(wù)于內(nèi)部很多基礎(chǔ)組件,如:函數(shù)計(jì)算平臺 FaaS、壓測平臺、各類網(wǎng)關(guān)、Service Mesh 控制面等,均收到不錯(cuò)的使用反饋。在如此大規(guī)模的場景下,Hertz 擁有極強(qiáng)的穩(wěn)定性和性能,在內(nèi)部實(shí)踐中某些典型服務(wù),如框架占比較高的服務(wù)、網(wǎng)關(guān)等服務(wù),遷移 Hertz 后相比 Gin 框架,資源使用顯著減少,CPU 使用率隨流量大小降低 30%-60%,時(shí)延也有明顯降低。

Hertz 堅(jiān)持內(nèi)外維護(hù)一套代碼,為開源使用提供了強(qiáng)有力的保障。通過開源, Hertz 也將豐富云原生的 Golang 中間件體系,完善 CloudWeGo 生態(tài)矩陣,為更多開發(fā)者和企業(yè)搭建云原生化的大規(guī)模分布式系統(tǒng),提供一種現(xiàn)代的、資源高效的的技術(shù)方案。

本文將重點(diǎn)關(guān)注 Hertz 的架構(gòu)設(shè)計(jì)與功能特性。

項(xiàng)目緣起

最初,字節(jié)跳動內(nèi)部的 HTTP 框架是對 Gin 框架的封裝,具備不錯(cuò)的易用性、生態(tài)完善等優(yōu)點(diǎn)。隨著內(nèi)部業(yè)務(wù)的不斷發(fā)展,高性能、多場景的需求日漸強(qiáng)烈。而 Gin 是對 Golang 原生 net/http 進(jìn)行的二次開發(fā),在按需擴(kuò)展和性能優(yōu)化上受到很大局限。因此,為了滿足業(yè)務(wù)需求,更好的服務(wù)各大業(yè)務(wù)線,2020 年初,字節(jié)跳動服務(wù)框架團(tuán)隊(duì)經(jīng)過內(nèi)部使用場景和外部主流開源 HTTP 框架 Fasthttp、Gin、Echo 的調(diào)研后,開始基于自研網(wǎng)絡(luò)庫 Netpoll 開發(fā)內(nèi)部框架 Hertz,讓 Hertz 在面對企業(yè)級需求時(shí),有更好的性能及穩(wěn)定性表現(xiàn),也能夠滿足業(yè)務(wù)發(fā)展和應(yīng)對不斷演進(jìn)的技術(shù)需求。

架構(gòu)設(shè)計(jì)

Hertz 設(shè)計(jì)之初調(diào)研了大量業(yè)界優(yōu)秀的 HTTP 框架,同時(shí)參考了近年來內(nèi)部實(shí)踐中積累的經(jīng)驗(yàn)。為了保證框架整體上滿足:1. 極致性能優(yōu)化的可能性;2. 面對未來不可控需求的擴(kuò)展能力, Hertz 采用了 4 層分層設(shè)計(jì),保證各個(gè)層級功能內(nèi)聚,同時(shí)通過層級之間的接口達(dá)到靈活擴(kuò)展的目標(biāo)。整體架構(gòu)圖如圖 1 所示。

字節(jié)跳動開源 Go HTTP 框架 Hertz 設(shè)計(jì)實(shí)踐

圖 1:Hertz 架構(gòu)圖

Hertz 從上到下分為:應(yīng)用層、路由層、協(xié)議層和傳輸層,每一層各司其職,同時(shí)公共能力被統(tǒng)一抽象到公共層(common),做到跨層級復(fù)用。另外,同主庫一同發(fā)布的還有作為子模塊的 Hz 腳手架,它能夠協(xié)助使用者快速搭建出項(xiàng)目核心骨架以及提供實(shí)用的構(gòu)建工具鏈。

應(yīng)用層

應(yīng)用層是和用戶直接交互的一層,提供豐富易用的 API,主要包括 Server、Client 和一些其他通用抽象。Server 提供了注冊 HandlerFunc、Binding、Rendering 等能力;Client 提供了調(diào)用下游和服務(wù)發(fā)現(xiàn)等能力;以及抽象一個(gè) HTTP 請求所必須涉及到的請求(request)、響應(yīng)(Response)、上下文(RequestContext)、中間件(Middleware)等等。Hertz 的 Server 和 Client 都能夠提供中間件這樣的擴(kuò)展能力。

應(yīng)用層中一個(gè)非常重要的抽象就是對 Server HandlerFunc 的抽象。早期,Hertz 路由的處理函數(shù) (HandlerFunc)中并沒有接收標(biāo)準(zhǔn)的 context.Context,我們在大量的實(shí)踐過程中發(fā)現(xiàn),業(yè)務(wù)方通常需要一個(gè)標(biāo)準(zhǔn)的上下文在 RPC Client 或者日志、Tracing 等組件間傳遞,但由于請求上下文(RequestContext)生命周期局限于一次 HTTP 請求之內(nèi),而以上提到的場景往往存在異步的傳遞和處理,導(dǎo)致如果直接傳遞請求上下文,會導(dǎo)致出現(xiàn)一些數(shù)據(jù)不一致的問題。為此我們做了諸多嘗試,但是因?yàn)楹诵脑蛟谟谡埱笊舷挛模≧equestContext)的生命周期無法優(yōu)雅的按需延長,最終在各種設(shè)計(jì)權(quán)衡下,我們在路由的處理函數(shù)簽名中增加一個(gè)標(biāo)準(zhǔn)的上下文入?yún)?,通過分離出生命周期長短各異的兩個(gè)上下文的方式,從根本上解決各種因?yàn)樯舷挛纳芷诓灰恢聦?dǎo)致的異常問題,即:

type HandlerFunc func(c context.Context, ctx *app.RequestContext)

路由層

路由層負(fù)責(zé)根據(jù) URI 匹配對應(yīng)的處理函數(shù)。

起初,Hertz 的路由基于 httprouter 開發(fā),但隨著使用的用戶越來越多,httprouter 漸漸不能夠滿足需求,主要體現(xiàn)在 httprouter 不能夠同時(shí)注冊靜態(tài)路由和參數(shù)路由,即 /a/b/:c/d 這兩個(gè)路由不能夠同時(shí)注冊;甚至有一些更特殊的需求,如/a/b、/:c/b ,當(dāng)匹配 /a/b 路由時(shí),兩個(gè)路由都能夠匹配上。

Hertz 為滿足這些需求重新構(gòu)造了路由樹,用戶在注冊路由時(shí)擁有很高的自由度:支持靜態(tài)路由、參數(shù)路由的注冊;支持按優(yōu)先級匹配,如上述例子會優(yōu)先匹配靜態(tài)路由 /a/b ;支持路由回溯,如注冊 /a/b/:c/d,當(dāng)匹配 /a/d 時(shí)仍然能夠匹配上;支持尾斜線重定向,如注冊 /a/b,當(dāng)匹配 /a/b/ 時(shí)能夠重定向到 /a/b 上。Hertz 提供了豐富的路由能力來滿足用戶的需求,更多的功能可以參考 Hertz 配置文檔。

協(xié)議層

協(xié)議層負(fù)責(zé)不同協(xié)議的實(shí)現(xiàn)和擴(kuò)展。

Hertz 支持協(xié)議的擴(kuò)展,用戶只需要實(shí)現(xiàn)下面的接口便可以按照自己的需求在引擎(Engine) 上擴(kuò)展協(xié)議,同時(shí)也支持通過 ALPN 協(xié)議協(xié)商的方式注冊。Hertz 首批只開源了 HTTP1 實(shí)現(xiàn),未來會陸續(xù)開源 HTTP2、QUIC 等實(shí)現(xiàn)。協(xié)議層擴(kuò)展提供的靈活性甚至可以超越 HTTP 協(xié)議的范疇,用戶完全可以按需注冊任意符合自身需求的協(xié)議層實(shí)現(xiàn),并且加入到 Hertz 的引擎中來,同時(shí),也能夠無縫享受到傳輸層帶來的極致性能。

type ServerFactory interface { New(core Core) (server protocol.Server, err error)}type Server interface { Serve(c context.Context, conn network.Conn) error}

傳輸層

傳輸層負(fù)責(zé)底層的網(wǎng)絡(luò)庫的抽象和實(shí)現(xiàn)。

Hertz 支持底層網(wǎng)絡(luò)庫的擴(kuò)展。Hertz 原生完美適配 Netpoll,在時(shí)延方面有很多深度的優(yōu)化,非常適合時(shí)延敏感的業(yè)務(wù)接入。Netpoll 對 TLS 能力的支持有待完善,而 TLS 能力又是 HTTP 框架必備能力,為此 Hertz 底層同時(shí)支持基于 Golang 標(biāo)準(zhǔn)網(wǎng)絡(luò)庫的實(shí)現(xiàn)適配,支持網(wǎng)絡(luò)庫的一鍵切換,用戶可根據(jù)自己的需求選擇合適的網(wǎng)絡(luò)庫進(jìn)行替換。如果用戶有更加高效的網(wǎng)絡(luò)庫或其他網(wǎng)絡(luò)庫需求,也完全可以根據(jù)需求自行擴(kuò)展。

Hz 腳手架

與 Hertz 一并開源的還有一個(gè)易用的命令行工具 Hz,用戶只需提供一個(gè) IDL,根據(jù)定義好的接口信息,Hz 便可以一鍵生成項(xiàng)目腳手架,讓 Hertz 達(dá)到開箱即用的狀態(tài);Hz 也支持基于 IDL 的更新能力,能夠基于 IDL 變動智能地更新項(xiàng)目代碼。目前 Hz 支持了 Thrift 和 Protobuf 兩種 IDL 定義。命令行工具內(nèi)置豐富的選項(xiàng),可以根據(jù)自己的需求使用。同時(shí)它底層依賴 Protobuf 官方的編譯器和自研的 Thriftgo 的編譯器,兩者都支持自定義的生成代碼插件。如果默認(rèn)模板不能夠滿足需求,完全能夠按需定義。

未來,我們將繼續(xù)迭代 Hz,持續(xù)集成各種常用的中間件,提供更高層面的模塊化構(gòu)建能力。給 Hertz 的用戶提供按需調(diào)整的能力,通過靈活的自定義配置打造一套滿足自身開發(fā)需求的腳手架。

Common 組件

Common 組件主要存放一些公共的能力,比如錯(cuò)誤處理、單元測試能力、可觀測性相關(guān)能力(Log、Trace、Metrics 等)。對于服務(wù)可觀測性的能力,Hertz 提供了默認(rèn)的實(shí)現(xiàn),用戶可以按需裝配;如果用戶有特殊的需求,也可以通過 Hertz 提供的接口注入。比如對于 Trace 能力,Hertz 提供了默認(rèn)的實(shí)現(xiàn),也提供了將 Hertz 和 Kitex 串起來的 Example。如果想注入自己的實(shí)現(xiàn),也可以實(shí)現(xiàn)下面的接口:

// Tracer is executed at the start and finish of an HTTP.type Tracer interface { Start(ctx context.Context, c *app.RequestContext) context.Context Finish(ctx context.Context, c *app.RequestContext)}

功能特性

中間件

Hertz 除了提供 Server 的中間件能力,還提供了 Client 中間件能力。用戶可以使用中間件能力將通用邏輯(如:日志記錄、性能統(tǒng)計(jì)、異常處理、鑒權(quán)邏輯等等)和業(yè)務(wù)邏輯區(qū)分開,讓用戶更加專注于業(yè)務(wù)代碼。Server 和 Client 中間件使用方式相同,使用 Use 方法注冊中間件,中間件執(zhí)行順序和注冊順序相同,同時(shí)支持預(yù)處理和后處理邏輯。

Server 和 Client 的中間件實(shí)現(xiàn)方式并不相同。對于 Server 來說,我們希望減少棧的深度,同時(shí)也希望中間件能夠默認(rèn)的執(zhí)行下一個(gè),用戶需要手動終止中間件的執(zhí)行。因此,我們將 Server 的中間件分成了兩種類型,即不在同一個(gè)函數(shù)調(diào)用棧(該中間件調(diào)用完后返回,由上一個(gè)中間件調(diào)用下一個(gè)中間件,如圖 2 中 B 和 C)和在同一個(gè)函數(shù)調(diào)用棧的中間件(該中間件調(diào)用完后由該中間件繼續(xù)調(diào)用下一個(gè)中間件,如圖 2 中 C 和 Business Handler)。

字節(jié)跳動開源 Go HTTP 框架 Hertz 設(shè)計(jì)實(shí)踐

圖 2: 中間件鏈路

其核心是需要一個(gè)地方存下當(dāng)前的調(diào)用位置 index,并始終保持其遞增。恰好 RequestContext 就是一個(gè)存儲 index 合適的位置。但是對于 Client,由于沒有合適的地方存儲 index,我們只能退而求其次,拋棄 index 的實(shí)現(xiàn),將所有的中間件構(gòu)造在同一調(diào)用鏈上,需要用戶手動調(diào)用下一個(gè)中間件。

流式處理

Hertz 提供 Server 和 Client 的流式處理能力。HTTP 的文件場景是十分常見的場景,除了 Server 側(cè)的上傳場景之外,Client 的下載場景也十分常見。為此,Hertz 支持了 Server 和 Client 的流式處理。在內(nèi)部網(wǎng)關(guān)場景中,從 Gin 遷移到 Hertz 后,cpu 使用量隨流量大小不同可節(jié)省 30%-60% 不等,服務(wù)壓力越大,收益越大。Hertz 開啟流式功能的方式也很容易,只需要在 Server 上或 Client 上添加一個(gè)配置即可,可參考 CloudWeGo 官網(wǎng) Hertz 文檔的流式處理部分。

由于 Netpoll 采用 LT 的觸發(fā)模式,由網(wǎng)絡(luò)庫主動將將數(shù)據(jù)從 TCP 緩沖區(qū)讀到用戶態(tài),并存儲到 buffer 中,否則 epoll 事件會持續(xù)觸發(fā)。因此 Server 在超大請求的場景下,由于 Netpoll 持續(xù)將數(shù)據(jù)讀到用戶態(tài)內(nèi)存中,可能會有 OOM 的風(fēng)險(xiǎn)。HTTP 文件上傳場景就是一個(gè)典型的場景,但 HTTP 上傳服務(wù)又是很常見的場景,因此我們支持標(biāo)準(zhǔn)網(wǎng)絡(luò)庫 go net,并針對 Hertz 做了特殊優(yōu)化,暴露出 Read() 接口,防止 OOM 發(fā)生。

對于 Client,情況并不相同。流式場景下會將連接封裝成 Reader 暴露給用戶,而 Client 有連接池管理,那這樣連接就多了一種狀態(tài),何時(shí)關(guān)連接,何時(shí)復(fù)用連接成了一個(gè)問題。由于框架側(cè)并不知道該連接何時(shí)會用完,框架側(cè)復(fù)用該連接不現(xiàn)實(shí),會導(dǎo)致串包問題。由于 GC 會關(guān)閉連接,因此我們起初設(shè)想流式場景下的連接交由用戶后,由 GC 負(fù)責(zé)關(guān)閉,這樣也不會導(dǎo)致資源泄漏。但是在測試后發(fā)現(xiàn),由于 GC 存在一定時(shí)間間隔,另外 TCP 中主動關(guān)閉連接的一方需要等待 2RTT,在高并發(fā)場景下會導(dǎo)致 fd 被打滿的情況。最終我們提供了復(fù)用連接的接口,對于性能有場要求用戶,在使用完連接后可以將連接重新放入連接池中復(fù)用。

性能表現(xiàn)

Hertz 使用字節(jié)跳動自研高性能網(wǎng)絡(luò)庫 Netpoll,在提高網(wǎng)絡(luò)庫效率方面有諸多實(shí)踐,參考已發(fā)布文章字節(jié)跳動在 Go 網(wǎng)絡(luò)庫上的實(shí)踐。除此之外,Netpoll 還針對 HTTP 場景進(jìn)行優(yōu)化,通過減少拷貝和系統(tǒng)調(diào)用次數(shù)提高吞吐以及降低時(shí)延。為了衡量 Hertz 性能指標(biāo),我們選取了社區(qū)中有代表性的框架 Gin(net/http)和 Fasthttp 作為對比,如圖 3 所示??梢钥吹?,Hertz 的極限吞吐、TP99 等指標(biāo)均處于業(yè)界領(lǐng)先水平。未來,Hertz 還將繼續(xù)和 Netpoll 深度配合,探索 HTTP 框架性能的極限。

字節(jié)跳動開源 Go HTTP 框架 Hertz 設(shè)計(jì)實(shí)踐

圖 3:Hertz 和其他框架性能對比

一個(gè) Demo

下面簡單演示一下 Hertz 是如何開發(fā)一個(gè)服務(wù)的。

  1. 首先,定義 IDL,這里使用 Thrift 作為 IDL 的定義(也支持使用 Protobuf 定義的 IDL),編寫一個(gè)名為 Demo 的 service。這個(gè)服務(wù)有一個(gè) API: Hello,它的請求參數(shù)是一個(gè) query,響應(yīng)是一個(gè)包含一個(gè) RespBody 字段的 Json

// idl/hello.thriftnamespace go hello.examplestruct HelloReq { 1: string Name (api.query="name");}struct HelloResp { 1: string RespBody;}service HelloService { HelloResp Hello(1: HelloReq request) (api.get="/hello");}

  1. 接下來我們使用 hz 生成代碼,并整理和拉取依賴

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

  1. 填充業(yè)務(wù)邏輯,比如我們返回 hello, ${Name},那我們在biz/handler/example/hello_service.go 中添加以下代碼即可

// Hello .// @router /hello [GET]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)}

  1. 編譯并運(yùn)行項(xiàng)目

$ go build$ ./Demo

到現(xiàn)在一個(gè)簡單的 Hertz 項(xiàng)目已經(jīng)生成,下面我們來測試一下

$ curl http://localhost:8888/hello?name=Xiaoming// 如果看到以下返回說明服務(wù)已經(jīng)正常啟動起來啦$ {"RespBody":"hello, Xiaoming"}

(以上 demo 可以在 hertz-examples 中查看) 之后就可以愉快的構(gòu)建自己的項(xiàng)目了。

后記

希望以上的分享能夠讓大家對 Hertz 有一個(gè)整體上的認(rèn)識。同時(shí),我們也在不斷地迭代 Hertz、完善 CloudWeGo 整體生態(tài)。歡迎各位感興趣的同學(xué)們加入我們,共同建設(shè) CloudWeGo。

參考資料

  • Hertz: https://github.com/cloudwego/hertz
  • Hertz Doc: https://www.cloudwego.io/zh/docs/hertz/
  • 字節(jié)跳動在 Go 網(wǎng)絡(luò)庫上的實(shí)踐: https://www.cloudwego.io/zh/blog/2021/10/09/字節(jié)跳動在-go-網(wǎng)絡(luò)庫上的實(shí)踐/

相關(guān)新聞

聯(lián)系我們
聯(lián)系我們
公眾號
公眾號
在線咨詢
分享本頁
返回頂部
大厂| 都兰县| 万全县| 疏附县| 湖南省| 合肥市| 灵寿县| 响水县| 贡山| 安丘市| 福泉市| 墨江| 浦北县| 上饶县| 浏阳市| 江达县| 沧源| 德兴市| 昔阳县| 应用必备| 昌黎县| 琼结县| 海门市| 迁安市| 湖北省| 苏尼特左旗| 富阳市| 天水市| 潼南县| 四子王旗| 本溪| 龙岩市| 西昌市| 临西县| 邓州市| 丁青县| 务川| 太谷县| 兴安盟| 郯城县| 丹巴县|