色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
电子发烧友
开通电子发烧友VIP会员 尊享10大特权
海量资料免费下载
精品直播免费看
优质内容免费畅学
课程9折专享价
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

抖音內部使用的Go基礎庫開源,高性能動態處理RPC數據

OSC開源社區 ? 來源:OSC開源社區 ? 2023-03-23 10:12 ? 次閱讀

01

背景

當前,Thrift 是字節內部主要使用的 RPC 序列化協議,在 CloudWeGo/Kitex 項目中優化和使用后,性能相比使用支持泛型編解碼的協議如 JSON 有較大優勢。但是在和業務團隊進行深入合作優化的過程中,我們發現一些特殊業務場景并不能享受靜態化代碼生成所帶來的高性能:
  1. 動態反射:動態地 讀取、修改、裁剪 數據包中某些字段,如隱私合規場景中字段屏蔽;
  2. 數據編排:組合多個子數據包進行 排序、過濾、位移、歸并 等操作,如某些 BFF (Backend For Frontent) 服務;
  3. 協議轉換:作為代理將某種協議的數據轉換另一種協議,如 http-rpc 協議轉換網關。
  4. 泛化調用:需要秒級熱更新或迭代非常頻繁的 RPC 服務,如大量 Kitex 泛化調用(generic-call)用戶

不難發現,這些業務場景都具有難以統一定義靜態IDL的特點。即使可以通過分布式 sidecar 技術規避這個問題,也往往因為業務需要動態更新而放棄傳統代碼生成方式,訴諸某些自研或開源的 Thrift 泛型編解碼庫進行泛化 RPC 調用。我們經過性能分析發現,目前這些庫相比代碼生成方式有巨大的性能下降。以字節某 BFF 服務為例,僅僅 Thrift 泛化調用產生的 CPU 開銷占比就將近 40%,這幾乎是正常 Thrift RPC 服務的4到8倍。因此,我們自研了一套能動態處理 RPC 數據(不需要代碼生成)同時保證高性能的 Go 基礎庫 —— dynamicgo。

02

設計與實現

首先要搞清楚當前這些泛化調用庫性能為什么差呢?其核心原因是:采用了某種低效泛型容器來承載中間處理過程中的數據(典型如 thrift-iterator 中的 map[string]interface{})。眾所周知,Go 的堆內存管理代價是極高的 (GC +heap bitmap),而采用 interface 不可避免會帶來大量的內存分配。但實際上相當多的業務場景并不真正需要這些中間表示。比如 http-thrift API 網關中的純協議轉換場景,其本質訴求只是將 JSON(或其它協議)數據依據用戶 IDL 轉換為 Thrift 編碼(反之亦然),完全可以基于輸入的數據流逐字進行翻譯。同樣,我們也統計了抖音某 BFF 服務中泛化調用的具體代碼,發現真正需要進行讀(Get)和寫(Set)操作的字段占整個數據包字段不到5%,這種場景下完全可以對不需要的字段進行跳過(Skip)處理而不是反序列化。而 dynamicgo 的核心設計思想是:基于 原始字節流 和 動態類型描述 原地(in-place) 進行數據處理與轉換。為此,我們針對不同的場景設計了不同的 API 去實現這個目標。動態反射

對于 thrift 反射代理的使用場景,歸納起來有如下使用需求:

  1. 有一套完整結構自描述能力,可表達 scalar 數據類型, 也可表達嵌套結構的映射、序列等關系;
  2. 支持增刪查改(Get/Set/Index/Delete/Add)與遍歷(ForEach);
  3. 保證數據可并發讀,但是不需要支持并發寫。等價于 map[string]interface{} 或 []interface{}

這里我們參考了 Go reflect 的設計思想,把通過IDL解析得到的準靜態類型描述(只需跟隨 IDL 更新一次)TypeDescriptor 和 原始數據單元 Node 打包成一個完全自描述的結構——Value,提供一套完整的反射 API。

//IDL類型描述
typeTypeDescriptorinterface{
Type()Type//數據類型
Name()string//類型名稱
Key()*TypeDescriptor//formapkey
Elem()*TypeDescriptor//forsliceormapelement
Struct()*StructDescriptor//forstruct
}
//純TLV數據單元
typeNodestruct{
tType//數據類型
vunsafe.Pointer//buffer起始位置
lint//數據單元長度
}
//Node+類型描述descriptor
typeValuestruct{
Node
Descthrift.TypeDescriptor
}

這樣,只要保證 TypeDescriptor 包含的類型信息足夠豐富,以及對應的 thrift 原始字節流處理邏輯足夠健壯,甚至可以實現數據裁剪、聚合等各種復雜的業務場景。

協議轉換

協議轉換的過程可以通過有限狀態機(FSM)來表達。以 JSON->Thrift 流程為例,其轉換過程大致為:

  1. 預加載用戶 IDL,轉換為運行時的動態類型描述 TypeDescriptor;
  2. 從輸入字節流中讀取一個 json 值,并判斷其具體類型(object/array/string/number/bool/null):
  3. 如果是 object 類型,繼續讀取一個 key,再通過對應的 STRUCT 類型描述找到匹配字段的子類型描述;
  4. 如果是 array 類型,遞歸查找類型描述的子元素類型描述;
  5. 其它類型,直接使用當前類型描述。
  6. 基于得到的動態類型描述信息,將該值轉換為等價的 Thrift 字節,寫入到輸出字節流中 ;
  7. 更新輸入和輸出字節流位置,跳回2進行循環處理,直到輸入終止(EOF)。

95a3aaf0-c8f0-11ed-bfe3-dac502259ad0.png

圖1 JSON2Thrift 數據轉換流程

整個過程可以完全做到 in-place 進行,僅需為輸出字節流分配一次內存即可。

數據編排

與前面兩個場景稍微有所不同,數據編排場景下可能涉及數據位置的改變(異構轉換),并且往往會訪問大量數據節點(最壞復雜度O(N) )。在與抖音隱私合規團隊的合作研發中我們就發現了類似問題。它們的一個重要業務場景:要橫向遍歷某一個 array 的子節點,查找是否有違規數據并進行整行擦除。這種場景下,直接基于原始字節流進行查找和插入可能會帶來大量重復的skip 定位、數據拷貝開銷,最終導致性能劣化。因此我們需要一種高效的反序列化(帶有指針)結構表示來處理數據。根據以往經驗,我們想到了DOM(Document Object Model),這種結構被廣泛運用在 JSON 的泛型解析場景中(如 rappidJSON、sonic/ast),并且性能相比 map+interface 泛型要好很多。

要用 DOM 來描述一個 Thrift 結構體,首先需要一個能準確描述數據節點之間的關系的定位方式—— Path。其類型應該包括 list index、map key 以及 struct field id等。

typePathTypeuint8

const(
PathFieldIdPathType=1+iota//STRUCT下字段ID
PathFieldName//STRUCT下字段名稱
PathIndex//SET/LIST下的序列號
PathStrKey//MAP下的stringkey
PathIntkey//MAP下的integerkey
PathObjKey//MAP下的objectkey
)

typePathNodestruct{
Path//相對父節點路徑
Node//原始數據單元
Next[]PathNode//存儲子節點
}

在 Path 的基礎上,我們組合對應的數據單元Node,然后再通過一個Next 數組動態存儲子節點,便可以組裝成一個類似于BTree的泛型結構。

9604321c-c8f0-11ed-bfe3-dac502259ad0.png

圖2 thrift DOM 數據結構

這種泛型結構比 map+interface 要好在哪呢?首先,底層的數據單元 Node 都是對原始 thrift data 的引用,沒有轉換 interface 帶來的二進制編解碼開銷;其次,我們的設計保證所有樹節點 PathNode 的內存結構是完全一樣,并且由于父子關系的底層核心容器是 slice, 我們又可以更進一步采用內存池技術,將整個 DOM 樹的子節點內存分配與釋放都進行池化從而避免調用 go 堆內存管理。測試結果表明,在理想場景下(后續反序列化的DOM樹節點數量小于等于之前反序列化節點數量的最大值——這由于內存池本身的緩沖效應基本可以保證),內存分配次數可為0,性能提升200%!(見【性能測試-全量序列化/反序列化】部分)。

03

性能測試

這里我們分別定義簡單(Small)、復雜(Medium) 兩個基準結構體分別在比較 不同數據量級 下的性能,同時添加簡單部分(SmallPartial)、復雜部分(MediumPartial) 兩個對應子集,用于【反射-裁剪】場景的性能比較:

  • Small:114B,6個有效字段
  • SmallPartial:small 的子集,55B,3個有效字段
  • Medium: 6455B,284個有效字段
  • MediumPartial: medium 的子集,1922B,132個有效字段

Small:https://github.com/cloudwego/dynamicgo/blob/main/testdata/idl/baseline.thrift#L3

Medium:https://github.com/cloudwego/dynamicgo/blob/main/testdata/idl/baseline.thrift#L12

SmallPartial:https://github.com/cloudwego/dynamicgo/blob/main/testdata/idl/baseline.thrift#L12

MediumPartial:https://github.com/cloudwego/dynamicgo/blob/main/testdata/idl/baseline.thrift#L36

其次,我們依據上述業務場景劃分為 反射、協議轉換、全量序列化/反序列化 三套 API,并以代碼生成庫kitex/FastAPI、泛化調用庫kitex/generic、JSON 庫sonic為基準進行性能測試。其它測試環境均保持一致:

  • Go 1.18.1
  • CPU intel i9-9880H 2.3GHZ
  • OS macOS Monterey 12.6

kitex/FastAPI:https://github.com/cloudwego/kitex/blob/aed28371eb88b2668854759ce9f4666595ebc8de/pkg/remote/codec/thrift/thrift.go

kitex/generic:https://github.com/cloudwego/kitex/tree/develop/pkg/generic

sonic:https://github.com/bytedance/sonic

反射

1. 代碼

dynamicgo/testdata/baseline_tg_test.go

2. 用例

  • GetOne:查找字節流中最后1個數據字段
  • GetMany:查找前中后5個數據字段
  • MarshalMany:將 GetMany 中的結果進行二次序列化
  • SetOne:設置最后一個數據字段
  • SetMany:設置前中后3個節點數據
  • MarshalTo:將大 Thrift 數據包裁剪為小 thrift 數據包 (Small -> SmallPartial 或 Medium -> MediumParital)
  • UnmarshalAll+MarshalPartial:代碼生成/泛化調用方式裁剪——先反序列化全量數據再序列化部分數據。效果等同于 MarshalTo。

3. 結果

  • 簡單(ns/OP)
962bbf62-c8f0-11ed-bfe3-dac502259ad0.png
  • 復雜(ns/OP)
965b93d6-c8f0-11ed-bfe3-dac502259ad0.png

4. 結論

  • dynamicgo 一次查找+寫入 開銷大約為代碼生成方式的 2 ~ 1/3、為泛化調用方式的 1/12 ~ 1/15,并隨著數據量級增大優勢加大;
  • dynamicgo thrift 裁剪 開銷接近于代碼生成方式、約為泛化調用方式的 1/10~1/6,并且隨著數據量級增大優勢減弱。

協議轉換

1. 代碼

  • JSON2Thrift:dynamicgo/testdata/baseline_j2t_test.go
  • ThriftToJSON:dynamicgo/testdata/baseline_t2j_test.go

2. 用例

  • JSON2thrift:JSON 數據轉換為等價結構的 thrift 數據
  • thrift2JSON:將 thrift 數據轉換為等價結構的 JSON 數據
  • sonic + kitex-fast:表示通過 sonic 處理 json 數據(有結構體),通過kitex代碼生成處理thrift數據

3. 結果

  • 簡單(ns/OP)
96762c8c-c8f0-11ed-bfe3-dac502259ad0.png
  • 復雜(ns/OP)
969ea66c-c8f0-11ed-bfe3-dac502259ad0.png

4. 結論

  • dynamicgo 協議轉換開銷約為代碼生成方式的 1~2/3、泛化調用方式的 1/4~1/9,并且隨著數據量級增大優勢加大;

全量序列化/反序列化

1. 代碼

dynamicgo/testdata/baseline_tg_test.go#BenchmarkThriftGetAll

2. 用例

  • UnmarshalAll:反序列化所有字段。其中對于 dynamicgo 有兩種模式:

    • new:每次重新分配 DOM 內存;
    • reuse:使用內存池復用 DOM 內存。
  • MarshalAll:序列化所有字段。

3. 結果

  • 簡單(ns/OP)
96b50a74-c8f0-11ed-bfe3-dac502259ad0.png
  • 復雜(ns/OP)
96cd3108-c8f0-11ed-bfe3-dac502259ad0.png

4. 結論

  • dynamicgo 全量序列化 開銷約為代碼生成方式的 6~3倍、泛化調用方式的 1/4~1/2,并且隨著數據量級增大優勢減弱;
  • Dynamigo 全量反序列化+內存復用 場景下開銷約為代碼生成方式的 1.8~0.7、泛化調用方式的 1/13~1/8,并且隨著數據量級增大優勢加大。

04

應用與展望

當前,dynamicgo 已經應用到許多重要業務場景中,包括:

  1. 業務隱私合規 中間件(thrift 反射);
  2. 抖音某 BFF 服務下游數據按需下發(thrift 裁剪);
  3. 字節跳動某 API 網關協議轉換(JSON<>thrift 協議轉換)。

并且逐步上線并取得收益。目前 dynamic 還在迭代中,接下來的工作包括:

  1. 集成到 Kitex 泛化調用模塊中,為更多用戶提供高性能的 thrift 泛化調用模塊;
  2. Thrift DOM 接入 DSL(GraphQL)組件,進一步提升 BFF 動態網關性能;
  3. 支持 Protobuf 協議。

也歡迎感興趣的個人或團隊參與進來,共同開發!

項目地址

GitHub:https://github.com/cloudwego

官網:www.cloudwego.io


審核編輯 :李倩


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 編解碼
    +關注

    關注

    1

    文章

    140

    瀏覽量

    19741
  • 開源
    +關注

    關注

    3

    文章

    3468

    瀏覽量

    42928
  • 數據包
    +關注

    關注

    0

    文章

    268

    瀏覽量

    24617

原文標題:抖音內部使用的Go基礎庫開源,高性能動態處理RPC數據

文章出處:【微信號:OSC開源社區,微信公眾號:OSC開源社區】歡迎添加關注!文章轉載請注明出處。

收藏 0人收藏

    評論

    相關推薦

    【GoRK3288】1.Rockchip RK3288, GO!GO!!GO!!!

    語言是谷歌2009發布的第二款開源編程語言, 專門針對多處理器系統應用程序的編程進行了優化,使用Go編譯的程序可以媲美C或C++代碼的速度,而且更加安全、支持并行進程,而且可以在不損失應用程序
    發表于 08-14 21:07

    go語言能做什么工作?

    讓程序員更容易地進行維護和修改。它融合了傳統編譯型語言的高效性和腳本語言的易用性和富于表達性。Go語言作為服務器編程語言,很適合處理日志、數據打包、虛擬機處理、文件系統、分布式系統、
    發表于 03-22 15:03

    香山是什么?“香山” 高性能開源 RISC-V 處理器項目介紹

    香山是什么2019 年,在中國科學院支持下,由 中國科學院計算技術研究所 牽頭發起 “香山” 高性能開源 RISC-V 處理器項目,研發出目前國際上性能最高的開源
    發表于 04-07 14:20

    基于LabView平臺的齒輪箱性能動態測試與診斷_李貴明

    基于LabView平臺的齒輪箱性能動態測試與診斷_李貴明
    發表于 03-18 09:41 ?3次下載

    移動卡全新發布免流量刷你會辦理嗎?

    “像一顆海草海草海草海草,隨波飄搖……”每天晚上睡覺前刷刷上班坐在公交車上刷刷閑著沒事總是要刷刷
    的頭像 發表于 07-16 15:14 ?7.5w次閱讀

    到底因為什么火成這樣?

    7月16日,官方正式宣布,全球月活躍用戶數超過5億。這是首次對外公布自己的全球月活躍
    的頭像 發表于 09-01 15:54 ?3122次閱讀

    怎么直播?三種方法教你怎么開直播

    很多朋友不知道怎么直播,找不到直播的入口。 小編為了學習如何在直播,也是在百度搜索了
    發表于 12-10 13:40 ?1.5w次閱讀

    數據造假等各類違規行為進行實時攔截

    截至2020年8月,包含火山版在內,的日活躍用戶已經超過了6億。隨著的火爆,相關第三
    的頭像 發表于 12-01 09:12 ?5686次閱讀

    支付在APP內正式上線

    1月19日 消息:據支付百科消息,支付已在APP內正式上線,在APP內購物結算時,除
    的頭像 發表于 01-19 15:50 ?4706次閱讀

    回應封殺騰訊外鏈傳聞

    據報道,針對近日社交媒體上流傳的“封殺騰訊”傳聞,回應稱為禁止財經、醫療垂類外鏈引流。
    的頭像 發表于 02-02 11:35 ?2319次閱讀

    再回應起訴騰訊

    2月2日,再次以聲明形式回應對騰訊的起訴。聲明中,稱,騰訊所謂的“惡意構陷”沒有任何依據;所謂“違規獲取微信用戶個人信息”不屬實;
    的頭像 發表于 02-03 09:13 ?2588次閱讀

    快手APP大眼特效開源實現,甜美系小姐姐親做效果演示

    短視頻中的大眼特效有很多人玩,這篇就講一下怎么實現。本文為《美顏效果開源實現,從AI到美顏全流程講解》姐妹篇,很多代碼和...
    發表于 01-26 18:45 ?1次下載
    <b class='flag-5'>抖</b><b class='flag-5'>音</b>快手APP大眼特效<b class='flag-5'>開源</b>實現,甜美系小姐姐親做效果演示

    嵌入式Linux應用開發之內置RPC

    標準RPC默認采用Go語言特有的gob編碼,因此從其它語言調用Go語言實現的RPC服務將比較困難。雖然可以通過額外的工作支持跨語言,但是
    發表于 05-13 09:46 ?791次閱讀

    拒絕小屏 躺著大屏刷

    隨著短視頻的興起,刷已經成為大多數人茶余飯后的一種消遣方式,但盯著一塊小小的手機屏幕刷實在是有點辛苦,正確的打開方式應該是躺在沙發上使用大屏電視,這樣刷
    的頭像 發表于 07-14 11:37 ?1054次閱讀
    拒絕小屏 躺著大屏刷<b class='flag-5'>抖</b><b class='flag-5'>音</b>

    Tars框架使用NIO進行網絡編程的源碼分析

    Tars是騰訊開源的支持多語言的高性能RPC框架,起源于騰訊內部2008年至今一直使用的統一應用框架TAF(Total Application Framework),目前支持C++、J
    的頭像 發表于 06-26 17:31 ?759次閱讀
    Tars框架使用NIO進行網絡編程的源碼分析
    主站蜘蛛池模板: 精品福利一区 | 毛片免费在线视频 | 少妇精品无码一区二区三区 | 亚洲AVAV天堂AV在线网爱情 | 国产精品禁18久久久夂久 | 日日摸夜添夜夜夜添高潮 | 九九热这里有精品 | 伦理片飘花免费影院 | 97影院理论午夜伦不卡偷 | 野花日本免费完整版高清版动漫 | 大学生第一次破苞疼哭了 | 5G在线观看免费年龄确认 | 国产激情精品久久久久久碰 | 好好的曰com久久 | 一本久道久久综合狠狠躁AV | 亚洲中文无码AV在线观看 | 国产精品久久人妻无码网站一区L | 亚洲 视频 在线 国产 精品 | 国产在线AV一区二区香蕉 | 免费果冻传媒2021在线观看 | 99精品免费观看 | 亚州日韩精品AV片无码中文 | 亚洲日本乱码中文论理在线电影 | 国产综合自拍 偷拍在线 | 精品久久香蕉国产线看观看麻豆 | 国产在线中文字幕 | 亚洲精品www久久久久久 | 亚洲人美女肛交真人全程 | 99久久免费国产精品特黄 | 中文字幕 人妻熟女 | 兰桂坊人成社区亚洲精品 | 花蝴蝶高清影视视频在线播放 | 草神被爆漫画羞羞漫画 | 欧美另类老女人 | 日韩吃奶摸下AA片免费观看 | 美女张开腿让男生桶动态图 | 夜夜狂射影院欧美极品 | 99久久久久国产精品免费 | 99久久精品毛片免费播放 | 国产高清超清在线播放 | 中文字幕成人免费高清在线 |

    電子發燒友

    中國電子工程師最喜歡的網站

    • 2931785位工程師會員交流學習
    • 獲取您個性化的科技前沿技術信息
    • 參加活動獲取豐厚的禮品