Bazel Remote Cache 緩存問題
簡介
公司 iOS 項目使用 bazel
使用編譯,同時 bazel
支持遠程緩存。 使用遠程緩存,可以加速編譯速度,節省編譯時間。
緩存服務器很簡單,支持 GET、 PUT 操作,分別爲獲取和上傳,官網有說明 Bazel Remote Cache。
build --remote_cache=http://mycache.com
但是在 CI 服務器上,偶爾會出現連接出現異常問題,如連接重置、斷開、超時、dns無法解析等。大概有5%的概率。分析 bazel 源碼,發現 bazel 底層使用 netty 進行http通信。遠程服務器日誌也沒有發現問題。
和常規解決思路不一樣,bazel 運行的時候無法進行抓包,因爲是概率性的,並且 CI 機器有多臺,無法統一進行排查。
解決
從錯誤信息來看,可能是因爲 CPU 使用率過高,導致影響網絡請求,出現連接超時、斷開、重置、dns無法解析等。
調優連接
# 降低連接數,默認爲100
--remote_max_connections=10
# 增加超時時長,默認爲60s
--remote_timeout=100s
調優CPU
# 降低CPU與線程數
--loading_phase_threads=HOST_CPUS*.9
使用本地緩存降低請求量
bazel 自己是支持本地緩存的,並且同時支持本地和遠程。但是不夠智能,不能及時清理老的緩存文件,同時本地和遠程是串行的。如上傳,先寫本地,再上傳。還是無法降低網絡請求的數量。
可參考官方源碼 DiskAndRemoteCacheClient.java
結合微服務中的 SideCar 和 cdn 思路,可以在本地啓動一個緩存服務。
上傳的時候,直接先把緩存放在磁盤中,同時再異步上傳到遠程,同時控制異步上傳的數據量,類似有個MQ進行異步處理,進行消峯操作。
下載的時候,如果本地有緩存,則直接返回。如果沒有則先下載,再進行返回。控制下載線程數,合理規避超時與異常情況。
同時 bazel 有同一個時刻多次請求相同的緩存資源的情況,使用本地緩存,可以避免這樣的問題。
build --remote_cache=http://127.0.0.1:8080/
總結
通過以上三種方式,暫時有效的解決編譯時候出現 netty 異常問題。
第三種方式,使用本地緩存進行代理中間也是踩過許多坑。
第一次使用Spring Boot 啓動一個服務,然後再使用 okhttp 進行遠程服務的下載與上傳,確實可以降低異常出現的概率,但是不夠完美。
第二次換成了Spring Cloud Gateway,想採用nginx的那種方式,不過因爲 Gateway 使用的是完全異步的方式,進行緩存本地化的時候有點喫力,可能是自己學藝不精。
第三次又回退到第一次的方式,並進行了一些列的優化,但是還是有小範圍的概率。因爲 Spring Boot 是基於 java 的,整個服務自己消耗有點高。
第四次,參照第三次的邏輯,使用go語言重新寫了一遍,本地緩存服務自己消耗的資源確實有很大程度降低,JAVA 版本最高的時候,自己需要佔2G內存,使用go版本最高時候只要100M,差距很明顯。
第五次,按理說第四次方案已經很完美,但是還是有小概率的出現,通過查找資料。bazel 自己是支持代理的,但是不是普通的http與socks5 代理,是 unix domain socket。
結合第四種,把go的http版本改成 go的 unix domain socket的即可,修改成本比較低。已上線使用,目前效果良好
# 走自己的代理,代理會進行磁盤緩存。
build --remote_proxy=unix:/tmp/bazel-car-go-unix.socket
以上就是自己排查 bazel 打包問題的歷程。經歷3個月,收穫頗多,尤其是go語言在網絡方面的使用,設計真的是太好了。