4月22日, B站部分后臺(tái)源代碼因?yàn)槟硲嵟膯T工, 被上傳至Github. 本文我們不討論安全, 法律 (根據(jù)代碼漏洞, 去惡意攻擊或者獲利是違法的! 我們工作時(shí)也要注意代碼安全), 我僅從開(kāi)發(fā)者的角度談?wù)? 這份代碼我們能學(xué)到什么? B站Golang生態(tài)建設(shè), 代碼規(guī)范, 工具建設(shè), 技術(shù)棧選擇, 對(duì)于Go在部門(mén)或公司的推廣又有哪些值得借鑒?
首先必須得說(shuō), B站這份代碼整體還是不錯(cuò)的, 不是說(shuō)組件或者基礎(chǔ)庫(kù)多么的厲害, 而是從整體目錄分布, 業(yè)務(wù)代碼分布, API易用性, 業(yè)務(wù)代碼風(fēng)格, 工具的統(tǒng)一, 上手難度上來(lái)評(píng)價(jià).
這里是一個(gè)小小的總結(jié).
- 約329個(gè)Go服務(wù), 歷史約170人左右貢獻(xiàn)過(guò)Go代碼.
- 代碼和目錄規(guī)范性比較好, 代碼生成工具建設(shè)比較好, 大家可以借鑒一下.
- 對(duì)于一個(gè)Golang開(kāi)發(fā)者來(lái)說(shuō), 入職B站, 我覺(jué)得大概2-3天就可以copy&&paste開(kāi)始貢獻(xiàn)業(yè)務(wù)代碼了. 其他語(yǔ)言開(kāi)發(fā)者, 3-4天吧, 因?yàn)閷W(xué)習(xí)Golang花一天.
- B站Go不依賴CGO, 業(yè)務(wù)代碼可以在windows編譯通過(guò)! 啟動(dòng)!
- 組件基本是基于開(kāi)源組件封裝.
- RPC基于grpc封裝, 協(xié)議編碼為proto, 沒(méi)有我們通常那樣的包頭.
- 服務(wù)注冊(cè)與發(fā)現(xiàn)已經(jīng)包裝在RPC中. 注冊(cè)使用自研的discovery, 基于類似url的方式去注冊(cè)和尋址.
- 數(shù)據(jù)存儲(chǔ)多使用memcache, redis和DB.
- hbase也使用比較多. 用于鑒權(quán), 用戶數(shù)據(jù)存儲(chǔ). 對(duì)于一些kv數(shù)據(jù), 外部沒(méi)有支持冷熱分離的kv存儲(chǔ), hbase是一個(gè)非常好的選擇: 基于HDFS, 熱數(shù)據(jù)加載到內(nèi)存, 列式存儲(chǔ), 強(qiáng)一致, 可配置副本數(shù).
- 消息隊(duì)列為使用基于kakfa, 實(shí)現(xiàn)了redis協(xié)議的databus.
- 小文件存儲(chǔ): B站自己實(shí)現(xiàn)的bfs
- 監(jiān)控上報(bào)使用的是prometheus, 對(duì)于中小公司, 沒(méi)法建設(shè)自己的監(jiān)控組件, prometheus是很不錯(cuò)的選擇.
- 簡(jiǎn)單瀏覽了下, 這份代碼在SQL上沒(méi)有注入風(fēng)險(xiǎn). 生產(chǎn)環(huán)境的配置并沒(méi)有在這份代碼中. 一個(gè)合格的開(kāi)發(fā)者, 即使所有源碼流出去, 也不會(huì)對(duì)系統(tǒng)造成任何危害.
- 不過(guò)B站的代碼似乎打點(diǎn)監(jiān)控做的不是很多(可能沒(méi)有太多的去強(qiáng)調(diào)?)
可以看出B站有一定的技術(shù)建設(shè)能力, 能夠基于開(kāi)源技術(shù)棧做封裝和改進(jìn), 所選技術(shù)棧適合中小型公司業(yè)務(wù). 技術(shù)總監(jiān)毛劍水平的確挺不錯(cuò), 下面會(huì)給出兩篇B站在Gopher China上的分享.
詳細(xì)請(qǐng)看下文
PS: 學(xué)習(xí)完代碼已刪除, 不要問(wèn)我要代碼哈.
[TOC]
嗶哩嗶哩的Go微服務(wù)實(shí)戰(zhàn)
文章上: https://mp.weixin.qq.com/s/bPFUGQDZCnt2aeIf7JI2cQ (主要講B站從PHP, Java轉(zhuǎn)為Go微服務(wù)之路)
文章下: https://mp.weixin.qq.com/s/4uA6iE7HC_SAfdIATAdrrA (主要講B站中間件建設(shè)情況)
視頻: https://www.bilibili.com/video/av29079011
以上兩篇(上面后兩篇)是B站公開(kāi)分享的Go微服務(wù)實(shí)戰(zhàn), 上文講B站的微服務(wù)演進(jìn), 下文講B站的中間件建設(shè).
本文中很多情況的確和本文中一致. 詳細(xì)可參考這兩篇文章.
目錄結(jié)構(gòu)及整體情況
通過(guò)main.go啟動(dòng)文件統(tǒng)計(jì), 整體約329個(gè)Go服務(wù). 有170人左右貢獻(xiàn)過(guò)Go代碼.
整體目錄結(jié)構(gòu)
admin是管理后臺(tái)的服務(wù), service是提供RPC內(nèi)部服務(wù), job是處理消息隊(duì)列的服務(wù), interface目錄是對(duì)外http的服務(wù).
其中admin目錄下54個(gè), infra目錄下5個(gè)基礎(chǔ)組件服務(wù), interface下77個(gè), job目錄下80個(gè), service目錄下113個(gè).
可以看出來(lái)B站Go后臺(tái)代碼管理使用的是一個(gè)大倉(cāng)庫(kù)的方式(從源碼目錄整齊度以及大倉(cāng)庫(kù)的README來(lái)看, 應(yīng)該是這樣的, 當(dāng)然還有個(gè)東西叫g(shù)it submodule). 這種方式有好處也有壞處.
- 上手很快, 所有的庫(kù), 依賴, 業(yè)務(wù)都在一個(gè)倉(cāng)庫(kù)中, 下載了就有提示, 馬上開(kāi)始擼代碼.
- 后期版本管理變得很混亂. 分支開(kāi)發(fā)也很重.
我在前公司也有這種情況, 前人把大概某20個(gè)服務(wù)放在一個(gè)倉(cāng)庫(kù)中, 后續(xù)的git log簡(jiǎn)直沒(méi)法看. 幸好后面大家沒(méi)這么做了.
服務(wù)目錄結(jié)構(gòu)
cmd: 放main.go和配置文件, 作為啟動(dòng)入口
conf: 放配置文件對(duì)應(yīng)的golang struct, 使用的是toml
model: 放結(jié)構(gòu)體, 比如Http參數(shù)轉(zhuǎn)換用的struct, DB存儲(chǔ)對(duì)應(yīng)的struct, 各層之間傳遞用的struct
dao: data access object, 數(shù)據(jù)庫(kù)訪問(wèn)方法, redis, memcache訪問(wèn)方法, 還有一些RPC調(diào)用也放在這里面
http: 提供http服務(wù), 主要是提供協(xié)議轉(zhuǎn)換, 聚合. 邏輯還是再service層做.
service: 對(duì)于后端服務(wù)來(lái)說(shuō), 該目錄提供服務(wù)的實(shí)現(xiàn), 對(duì)于http服務(wù), 該目錄提供http服務(wù)的實(shí)現(xiàn).
目錄規(guī)范性
所有的服務(wù)均遵守該目錄結(jié)構(gòu). model層放VO, DO等, dao層用于數(shù)據(jù)層封裝, 隔離本服務(wù)的領(lǐng)域邏輯與外部數(shù)據(jù). http層提供協(xié)議轉(zhuǎn)換. service實(shí)現(xiàn)具體邏輯.
比較像Java開(kāi)發(fā)的模式, 可能在公司很多人不是很喜歡這樣復(fù)雜的目錄, 喜歡什么都放在一個(gè)目錄下.
不過(guò)這樣的分目錄是一種比較好的實(shí)踐. 各層分的清清楚楚, 一個(gè)服務(wù)從1個(gè)接口到10個(gè)接口, 都比較清晰. 對(duì)于服務(wù)改動(dòng)來(lái)說(shuō),也比較好聚焦于某一層.
生成工具的重要性
目錄做到規(guī)范性, 服務(wù)維護(hù), 其他人接手也容易多了. 然而大家都是有各自習(xí)慣, 每個(gè)人都喜歡偷懶, 靠規(guī)范, 靠說(shuō)教來(lái)使得程序員保持目錄規(guī)范, 實(shí)踐證明是不可能的. 所以得靠生成工具.你給程序員生成好的代碼目錄和模式, 99%的人是不會(huì)去改的...能把業(yè)務(wù)邏輯實(shí)現(xiàn)了, 還管其他的干啥?
此份代碼的300多個(gè)服務(wù), 目錄都是一致的, 不管是http服務(wù), 還是接收消息隊(duì)列的服務(wù), 還是后臺(tái)service. 同時(shí)service包下, http包下的代碼流程都基本一致, rpc調(diào)用方式一致, 都是靠生成工具來(lái)實(shí)現(xiàn).
B站Golang技術(shù)棧分析
技術(shù)棧 | 技術(shù)選型 | 參考鏈接 |
---|---|---|
RPC | 基于grpc封裝的warden框架, 已開(kāi)源 | https://github.com/bilibili/kratos |
HTTP框架 | 基于gin封裝的blade master框架, 已開(kāi)源 | 同上 |
服務(wù)注冊(cè)與發(fā)現(xiàn) | 初期為zk, 后面逐步改為參考Spring Cloud體系Eureka自研的discovery | 已開(kāi)源 https://github.com/bilibili/discovery |
存儲(chǔ) | DB, redis, memcache, hbase存儲(chǔ)一些用戶kv信息和歷史流水, 已封裝好庫(kù) library/database/ | client庫(kù)已開(kāi)源 https://github.com/bilibili/kratos |
搜索 | B站視頻, 用戶, 歷史記錄等使用es搜索, 客戶端已封裝在基礎(chǔ)庫(kù)中 library/database/elastic | |
小文件存儲(chǔ) | 毛劍個(gè)人研發(fā)的bfs, 已開(kāi)源. | https://www.toutiao.com/i6272104949560115714/ https://github.com/Terry-Mao/bfs |
消息隊(duì)列 | 基于kafka封裝的databus | |
log | 基于uber的zap封裝的日志框架 | |
配置及配置中心 | 支持從環(huán)境變量讀取配置, 從toml中解析配置, 支持遠(yuǎn)程配置中心(自研, mysql存儲(chǔ), 本地落地,http協(xié)議, long poll, 客戶端有更新事件, 類似于攜程開(kāi)源的Apollo) | |
監(jiān)控 | 使用開(kāi)源的prometheus, 框架和庫(kù)(sql, redis, hbase等)中已預(yù)埋計(jì)數(shù)點(diǎn)和時(shí)間統(tǒng)計(jì)點(diǎn), 同時(shí)也可以在業(yè)務(wù)邏輯中打點(diǎn). library/stat/stat.go | |
trace | trace似乎是基于agent的方式, 使用unix domain socket進(jìn)行傳送, 框架和庫(kù)已預(yù)埋點(diǎn). library/net/trace.go | |
研發(fā)流程管理 | TAPD, 哈哈, 有相關(guān)的tapd struct信息 |
其中RPC, HTTP框架, 數(shù)據(jù)訪問(wèn)的一些庫(kù)封裝, 包括生成工具, 均以kratos項(xiàng)目在github開(kāi)源了(https://github.com/bilibili/kratos Kratos是bilibili開(kāi)源的一套Go微服務(wù)框架镣屹,包含大量微服務(wù)相關(guān)框架及工具)
B站目前使用及封裝的中間件的詳細(xì)介紹在Gopher China 2017 B站的分享有提到原理和使用情況.
https://mp.weixin.qq.com/s/4uA6iE7HC_SAfdIATAdrrA
bfs介紹
http://www.reibang.com/p/923917220d23
B站運(yùn)維體系發(fā)展
https://myslide.cn/slides/3840
總結(jié)
簡(jiǎn)單分析了下B站的代碼風(fēng)格和后臺(tái)架構(gòu), 目錄規(guī)范性, 工具建設(shè)均做的不錯(cuò), 其中代碼生成工具做的很好, http服務(wù), 后臺(tái)grpc服務(wù),均可通過(guò)proto生成(目前now也是這樣做的), 也有使用go ast進(jìn)行生成的工具(大家可以參考一下).
中間件方面, 技術(shù)選型大部分為一些目前業(yè)界比較實(shí)用和流行的開(kāi)源組件, 進(jìn)行了一些封裝, 比較適合員工上手. 同時(shí)像配置中心, 小文件存儲(chǔ), 為自研. 全套解決方案比較關(guān)鍵路徑均開(kāi)源.
可以看出B站有一定的技術(shù)建設(shè)能力. 技術(shù)選型比較符合中小型公司的實(shí)際情況.
對(duì)于我們而言, 可以借鑒一下, 完善生成工具, 提高API的易用性, 降低入門(mén)門(mén)檻, 根據(jù)業(yè)務(wù)選用適合組件.