最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用Go重構(gòu)流式日志網(wǎng)關(guān)的實戰(zhàn)分享

 更新時間:2023年06月08日 09:28:11   作者:又拍云  
流式日志網(wǎng)關(guān)的主要功能是提供?HTTP?接口,接收?CDN?邊緣節(jié)點上報的各類日志(訪問日志/報錯日志/計費日志等),將日志作預處理并分流到多個的?Kafka?集群和?Topic?中,本文就給大家分享如何使用?Go?重構(gòu)流式日志網(wǎng)關(guān)

項目背景

分享之前,先來簡單介紹下該項目在流式日志處理鏈路中所處的位置。

流式日志網(wǎng)關(guān)的主要功能是提供 HTTP 接口,接收 CDN 邊緣節(jié)點上報的各類日志(訪問日志/報錯日志/計費日志等),將日志作預處理并分流到多個的 Kafka 集群和 Topic 中。

越來越多的客戶要求提供實時日志支持,業(yè)務量的增加讓機器資源的消耗也與日俱增,最先暴露出了流式日志處理鏈路的一大瓶頸——帶寬資源。

可以通過給集群擴充更多的機器來提升集群總傳輸帶寬,但基于成本考量,重中之重是先優(yōu)化網(wǎng)關(guān)程序。

舊版網(wǎng)關(guān)項目

項目代號 Chopper ,其基于另一個內(nèi)部 OpenResty 項目框架來開發(fā)的。其亮點功能有:支持從 Consul 、Redis 等其他外部系統(tǒng)熱加載配置及動態(tài)生效;能夠加載 Lua 腳本實現(xiàn)靈活的日志預處理能力。

其 Kafka 生產(chǎn)者客戶端基于 doujiang24/lua-resty-kafka 實現(xiàn)。經(jīng)過實踐考驗,Chopper 的吞吐量是滿足現(xiàn)階段需求的。

存在的問題

1. 關(guān)鍵依賴庫的社區(qū)活躍度低

lua-resty-kafka 的社區(qū)活躍度較低,至今仍然處在實驗階段;而且它用作 Kafka 生產(chǎn)者客戶端目前沒有支持消息壓縮功能,而這在其他語言實現(xiàn)的 Kafka 客戶端中都是標準的選項。

2. 內(nèi)存使用不節(jié)制

單實例部署配置 4 核 8 G,僅少量請求訪問后,內(nèi)存占用就穩(wěn)定在 2G 而沒有釋放。

3. 配置文件可維護性差

實際線上用到 Consul 作為配置中心,采用篇幅很長的 JSON 格式配置文件,不利于運維。另外在 Consul 修改配置沒有回退功能,是一個高風險操作。

好在目前日志網(wǎng)關(guān)的功能并不復雜,所以我們決定重構(gòu)它。

新項目啟動

眾所周知, Go 語言擁有獨特的高并發(fā)模型、較低的上手難度和豐富的第三方生態(tài)。而且我們小組成員都有 Go 項目的開發(fā)經(jīng)驗,所以我們選擇使用基于 Go 語言的技術(shù)棧來重新構(gòu)建 Chopper 項目,所以新項目命名為 chopper-go 。

需求梳理及概要設(shè)計

重新構(gòu)建一個線上項目的基本原則是,功能上要完全兼容,最好能夠?qū)崿F(xiàn)線上服務的無縫升級替換。

原版核心模塊的設(shè)計

Chopper 的核心功能是將接收到的 HTTP 請求分流到特定 Kafka 集群及其 Topic 中。

一、HTTP 接口部分

只開放了唯一一個對外的 API ,功能很簡單:

請求方式:POST 請求路徑:/log/repo/{repo_name} 請求體:  多行日志,滿足 JSONL 格式(即每行一條 JSON ,多行按換行符 \n 分隔)。相應狀態(tài)碼:- 200:投遞成功。- 5xx:投遞失敗需要重試。參數(shù)解釋: - repo_name: 對應 repo 配置名稱。

二、業(yè)務配置部分

每一類業(yè)務抽象為一個 repo 配置。Repo 配置由三部分構(gòu)成:constraint、processor、kafka。constraint 是一個對象,可以配置對日志字段的一些約束條件,不滿足條件的日志會被丟棄。processor 是一個列表,可以組合多個處理模塊,程序?qū)错樞蛞来螌φ埱笾械拿織l日志進行處理。實現(xiàn)了如下幾種 processor 類型:

  • decoder , 配置原始數(shù)據(jù)按哪種格式反序列化到 Lua table ,但只實現(xiàn)了 JSON decoder。
  • splitter,配置分隔日志字段的字符。
  • assigner,配置一組字段名映射關(guān)系,需要與 spliter 配合。
  • executer, 配置額外的 lua 腳本名稱,通過動態(tài)加載其他 lua 腳本實現(xiàn)更靈活的處理邏輯。

kafka 是一個對象,可以配置當前業(yè)務相關(guān)聯(lián)的 Kafka 集群名,默認投遞的 Topic ,以及生產(chǎn)者客戶端的工作模式(同步或者異步)。

新版本的改動HTTP

接口沿用原先的設(shè)計,在業(yè)務配置部分做了一些改動:

  • processor 改名為 executers ,實現(xiàn)幾個通用功能的日志處理模塊,方便組合使用。
  • kafka 配置中關(guān)聯(lián)的不再是集群名,而是 Kafka 生產(chǎn)者客戶端的配置標簽。
  • 原先保存 kafka 集群連接配置信息的配置塊,改為保存 kafka 生產(chǎn)者客戶端的配置塊,統(tǒng)一在一個配置塊區(qū)域初始化所有用到的 kafka 生產(chǎn)者客戶端。

一點妥協(xié)(做減法)

為了縮短新項目的開發(fā)周期,對原始項目的一些不太重要的特性我們做了一些取舍。

取消動態(tài)腳本功能

Go 是靜態(tài)語言沒有 Lua 動態(tài)語言那么靈活,要加載執(zhí)行動態(tài)腳本有一定的實現(xiàn)難度,且日志處理性能沒有保障。線上只有極少數(shù)業(yè)務在 processor 中配置了 executor,且這些 executor 的 Lua 腳本實現(xiàn)相近,完全可以抽取出通用的代碼。

不支持外部配置中心

為了讓發(fā)布和回退有記錄可回溯,從 Consul 等配置中心熱加載服務配置的功能我們也去掉了。利用好容器平臺的金絲雀發(fā)布功能,就能將服務更新的影響降到最低。

不支持復雜的路由重寫

OpenResty 項目內(nèi)置 Nginx 可以利用 Nginx 強大的配置實現(xiàn)豐富的路由 rewrite 功能,就具體使用場景而言,我們只需要簡單的路由映射即可。況且更復雜的需求也可以由上一級網(wǎng)關(guān)完成。

選擇合適的開源庫

Web 框架的選擇

使用 Go 開發(fā) Web 應用很快捷。我們參考了如下文章:

  • 《超全的 Go Http 路由框架性能比較》
  • 《iris真的是最快的路由框架嗎?》

下列幾款 Star 較多的 Go Web 框架都能滿足我們需求:

  • kataras/iris
  • gin-gonic/gin
  • go-chi/chi
  • labstack/echo

他們性能都很好,最終我們選擇了 Gin 。原因是用得多比較熟,而且文檔看著舒服。

Kafka 生產(chǎn)者客戶端的選擇

社區(qū)中熱度最高的幾款 Go Kafka 客戶端庫:

  • segmentio/kafka-go
  • Shopify/sarama
  • confluentinc/confluent-kafka-go

實際上三款客戶端庫我們在歷史項目中都使用過,其中 kafka-go 的 API 是三者中最簡潔易用的,我們的多個消費端程序都是基于它實現(xiàn)的。

但是在 chopper-go 中僅需要用到生產(chǎn)者客戶端,我們沒有選擇 kafka-go 。那是因為我們做了一些基準測試kafka-go 的生產(chǎn)者客戶端存在性能風險:啟用 async 模式時盡管消息發(fā)送特別快,但是內(nèi)存占用也增長特別快。通過閱讀源碼我也找到了原因并向官方提了 issue

最終我們選擇 sarama  ,一方面是性能很穩(wěn)定,另一方面是它開放的> API 較多,但是用起來確實有點費勁。

測試框架的選擇

程序的可靠性,一定需要測試來保證。除了編寫小模塊中編寫單元從測試,我們對整個日志網(wǎng)關(guān)服務還要做集成測試。集成測試涉及到一些外部服務依賴,此項目中主要的外部依賴是 Kafka 和 Zookeeper。

利用 Docker 可以很方便的拉起測試環(huán)境,我們注意到了兩款可以用來在 Go test 中編寫集成測試的庫:

  • ory/dockertest
  • testcontainers/testcontainers-go

使用下來,我們最終選擇了 testcontainers-go,簡單介紹下原因:

在編寫集成測試時,我們需要有個等待機制來確保依賴服務的容器是否準備就緒,并以此控制測試流程,以及測試結(jié)束后需要把測試開啟的臨時容器都清理干凈。

testcontainers-go  的設(shè)計要優(yōu)于 dockertest 。testcontainers-go 提供一個 wait 子包,可以配置多種等待策略來確保依賴服務就緒,以及測試結(jié)束時它會調(diào)用一個特殊的名為 Ryuk 的容器來確保測試容器都被關(guān)閉。相對而言,dockertest 要簡陋不少。

需要注意的是,在 CI 環(huán)境運行集成測試都需要確保 ci-runner 支持 DinD ,否則運行 go test 會失敗。

項目開發(fā)

項目開發(fā)過程中基本按照需求來實現(xiàn)沒有太多難點。這里分享踩到的幾個坑。

循環(huán)中變量的引用問題

在測試中發(fā)現(xiàn),Kafka 生產(chǎn)者沒有按期望把消息投遞到指定的 Kafka 集群。

經(jīng)過排查到如下代碼:

func New(cfg Config) (*Manager, error) {
            var newProducers = make(NewProducerFuncs)
            for name, kCfg := range cfg.Mapping {
            newProducers[name] = func() (kafka.Producer, error) { return kafka.New(kCfg) }
            }
            // 略
   }

其作用是將配置每個 Kafka 生產(chǎn)者配置先保存為一個函數(shù)閉包,待后續(xù)初始化 repo 的時候再初始化生產(chǎn)者客戶端。

經(jīng)驗豐富的同學可以發(fā)現(xiàn),for 循環(huán)的 kCfg 變量其實是指向迭代對象的地址,整個循環(huán)下來所有的函數(shù)閉包中用到的 kCfg 都指向 cfg.Mapping 的最后一個迭代值。

解決辦法很簡單,先做一遍變量拷貝即可:

func New(cfg Config) (*Manager, error) {
            var newProducers = make(NewProducerFuncs)
            for name, kCfg := range cfg.Mapping {
            newProducers[name] = func() (kafka.Producer, error) { return kafka.New(kCfg) }
            }
            // 略
   }

這是個挺容易碰到的問題,參考十多年了,這個最容易犯錯的Go語法終于要改了 (colobu.com)

Go 也有可能在未來將循環(huán)變量的語義從 per-loop 改成 per-iteration。

Sarama 客戶端的一點坑

對于重要的日志數(shù)據(jù),我們希望在 HTTP 請求返回時明確反饋是否成功寫入 Kafka 。那么最好將 Kafka 生產(chǎn)者客戶端配置為同步模式。

而同步模式的生產(chǎn)者要提高吞吐量,批量發(fā)送是必不可少的。

批量發(fā)送的配置位于 sarama.Config.Producer.Flush

cfg := sarama.NewConfig()
// 單次請求中消息數(shù)量的絕對上限
cfg.Producer.Flush.MaxMessages = batchMaxMsgs
// 能夠觸發(fā)請求發(fā)出的消息數(shù)量閾值
cfg.Producer.Flush.Messages = batchMsgs
// 能夠觸發(fā)請求發(fā)出的消息字節(jié)大小閾值
cfg.Producer.Flush.Bytes = batchBytes
// 批量請求的觸發(fā)間隔時間
cfg.Producer.Flush.Frequency = batchTimeout

實踐中發(fā)現(xiàn),如果配置了 Flush.Bytes 而沒有配置Flush.Frequency 就存在問題。如果消息大小始終未達閾值就不會觸發(fā)批量請求,故 HTTP 請求就會阻塞直到客戶端請求超時。

所以在配置參數(shù)的讀取上,我們把這兩個配置項做了關(guān)聯(lián),只有配置了 Flush.Frequency 才能讓 Flush.Bytes 的配置生效。

項目上線

容器平臺上的灰度技巧

原本圖方便我們的路由轉(zhuǎn)發(fā)規(guī)則配置的是全部路由直接轉(zhuǎn)給同一組 Chopper 實例。

前面介紹了,每一個業(yè)務對應一個 repo,也就對應一個獨立的請求路徑。如果要灰度新的服務,需要對不同業(yè)務單獨灰度,所以我們需要將不同業(yè)務的流量去分開。

好在容器平臺的 k8s-ingress 使用的是 APISIX 作為接入網(wǎng)關(guān),其路由匹配的優(yōu)先級是:絕對匹配 > 前綴匹配。

只需要針對特定業(yè)務增加一條絕對匹配規(guī)則,就可以分離出特定業(yè)務的流量。

舉個例子:原本的轉(zhuǎn)發(fā)規(guī)則是:/* -> workers-0

我們新建一條轉(zhuǎn)發(fā)規(guī)則:/log/repo/cdn-access -> workers-1

workers-0 和 workers-1 兩組服務的配置完全相同。

然后我們對 workers-1 這組服務灰度發(fā)布新版程序。

逐步擴大

每灰度一條路由,我們可以從監(jiān)控 Dashboard 上觀察 HTTP 請求是否有異常,觀察 Kafka 對應的 topic 的寫入速率是否有異常抖動。

一旦觀測到異常,立即停止灰度,然后檢查程序運行日志,修正問題后重新開始灰度。

如果無異常,則逐步擴大灰度比例,直到完成服務更新。

總結(jié)起來就是灰度、觀測、回退、修改循環(huán)推進,確保升級對每個業(yè)務都無感知。

完成發(fā)布

對比服務端資源占用情況

舊版 chopper (4C8G x 20) 灰度比例

10% -> 50%

chopper-go (4C4G x 20)

10% -> 50%

50% -> 100%

結(jié)論:新版日志網(wǎng)關(guān)的內(nèi)存和 CPU 的資源使用都有顯著降低。

服務端程序的資源占用情況

舊版 chopper 的 Kafka 客戶端不支持消息壓縮,chopper-go 發(fā)布中就配置了 Kafka 生產(chǎn)者消息的功能。壓縮算法選擇 lz4 ,觀察兩組消費服務的資源實用率的變化:消費服務0

  • 內(nèi)存使用率 27% -> 40%
  • 網(wǎng)絡流入 253Mbps -> 180Mbps

消費服務1

  • 內(nèi)存使用率 28% -> 39%
  • 網(wǎng)絡流入 380Mbps -> 267Mbps

結(jié)論:開啟消息壓縮功能后,消費實例的內(nèi)存使用率普遍有增長,但內(nèi)網(wǎng)傳輸帶寬占用降低約 30%

更新計劃

重構(gòu)后的流式日志網(wǎng)關(guān),尚有許多可優(yōu)化空間,例如:

  • 采用更節(jié)省帶寬的日志傳輸格式;
  • 進一步細化 Kafka topic 的分流粒度;
  • 日志消息處理階段多級處理執(zhí)行器之間增加緩存提高字段訪問速度等等。

在豐富開源生態(tài)的加持下,該項目的優(yōu)化迭代也將有條不紊地進行。

以上就是使用Go重構(gòu)流式日志網(wǎng)關(guān)的實戰(zhàn)分享的詳細內(nèi)容,更多關(guān)于Go 重構(gòu)流式日志網(wǎng)關(guān)的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 對Go語言中的context包源碼分析

    對Go語言中的context包源碼分析

    這篇文章主要對Go語言中的context包源碼進行分析,context包析是1.15,context包定義了一個Context類型過這個Context接口類型,?就可以跨api邊界/跨進程傳遞一些值,下面我們就來對context包源碼進行分析,需要的小伙伴可以參考一下
    2022-02-02
  • Go語言學習網(wǎng)絡編程與Http教程示例

    Go語言學習網(wǎng)絡編程與Http教程示例

    這篇文章主要為大家介紹了Go語言學習網(wǎng)絡編程與Http教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • 一文帶你理解Golang中的Time結(jié)構(gòu)

    一文帶你理解Golang中的Time結(jié)構(gòu)

    根據(jù)golang的time包的文檔可以知道,golang的time結(jié)構(gòu)中存儲了兩種時鐘,一種是Wall?Clocks,一種是Monotonic?Clocks,下面我們就來簡單了解一下這兩種結(jié)構(gòu)吧
    2023-09-09
  • Go位集合相關(guān)操作bitset庫安裝使用

    Go位集合相關(guān)操作bitset庫安裝使用

    這篇文章主要為大家介紹了Go位集合相關(guān)操作bitset庫安裝使用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • Golang之模糊測試工具的使用

    Golang之模糊測試工具的使用

    本文主要介紹了Golang之模糊測試工具的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-03-03
  • golang對自定義類型進行排序的解決方法

    golang對自定義類型進行排序的解決方法

    學習一門編程語言,要掌握原子數(shù)據(jù)類型,還需要掌握自定義數(shù)據(jù)類型。下面這篇文章主要給大家介紹了關(guān)于golang如何對自定義類型進行排序的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下。
    2017-12-12
  • Go語言Telnet回音服務器的實現(xiàn)

    Go語言Telnet回音服務器的實現(xiàn)

    這篇文章主要介紹了Go語言Telnet回音服務器的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-01-01
  • GoLang channel底層代碼實現(xiàn)詳解

    GoLang channel底層代碼實現(xiàn)詳解

    Channel和goroutine的結(jié)合是Go并發(fā)編程的大殺器。而Channel的實際應用也經(jīng)常讓人眼前一亮,通過與select,cancel,timer等結(jié)合,它能實現(xiàn)各種各樣的功能。接下來,我們就要梳理一下GoLang channel底層代碼實現(xiàn)
    2022-10-10
  • go 協(xié)程返回值處理操作

    go 協(xié)程返回值處理操作

    這篇文章主要介紹了go 協(xié)程返回值處理操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Golang自旋鎖的相關(guān)介紹

    Golang自旋鎖的相關(guān)介紹

    自旋鎖是指當一個線程在獲取鎖的時候,如果鎖已經(jīng)被其他線程獲取,那么該線程將循環(huán)等待,然后不斷地判斷是否能夠被成功獲取,知直到獲取到鎖才會退出循環(huán)
    2022-10-10

最新評論

和田县| 东城区| 孙吴县| 寿阳县| 白沙| 诸城市| 万安县| 民和| 林甸县| 山阳县| 东海县| 慈溪市| 佛教| 鄱阳县| 清丰县| 玉林市| 卓尼县| 南皮县| 井陉县| 巴林右旗| 安塞县| 红安县| 平武县| 新津县| 万盛区| 锡林浩特市| 多伦县| 余干县| 西昌市| 新泰市| 永年县| 酒泉市| 蕉岭县| 龙游县| 克拉玛依市| 和田县| 女性| 改则县| 慈利县| 彝良县| 黑龙江省|