Structured Logging 須要更好的基礎設施支持

English Version: https://taowen.gitbooks.io/tsdb/content/indexing/indexing.htmlhtml

現狀

所謂 structured logging 就是往日誌文件裏打json格式的日誌,而後按照固定字段去檢索的需求。這種需求目前通常是用這樣的一個基礎設施來知足的:git

業務進程 => 日誌文件 => 同機的日誌蒐集agent => 日誌解析 => kafka => 日誌索引程序 => elasticsearch

對於日誌的存儲和檢索需求來講,用上面設置的 elasticsearch 能夠說已經解決得很好了(惟一查詢DSL比較古怪的問題,用SQL也能夠解決 https://github.com/taowen/es-monitor)。github

存在的主要問題就是如今的設置是用「非結構化日誌」來支持「結構化日誌」的需求。由於整個基礎設施假定輸入的數據都是無格式,而且面向行處理的,因此整個導入的過程沒法爲結構化日誌優化,沒有辦法得到原本能夠達到的性能。問題表現爲三個主要的點上:數據庫

  • 應用程序必須在可靠性和高性能之間選擇日誌方案json

  • 數據在多個集羣之間倒騰,致使佔用大量機房內網帶寬以及提升了端到端的延遲後端

  • 低效的數據封包格式,以及按行處理服務器

可靠性 v.s. 高性能

當業務進程須要提供一份流式的事件作爲分析用途的時候,就每每須要在高可靠性和高性能之間選擇。通常來講有這樣兩種選擇:app

  • 業務進程 => kafka (直接寫kafka)socket

  • 業務進程 => 日誌文件 => 採集agent => kafkaelasticsearch

選擇第一種方式會很是可靠。首先少了中間環境,其次寫kafka是同步過程,甚至可讓kafka作到所有replica都ack才返回success。可是第一種方式的主要問題是一旦kafka出問題了就可能會影響到業務進程自己。若是業務進程決定不一樣步寫kafka,那又有丟數據的風險。

第二種方式所以變得有吸引力,一方面日誌文件寫入不影響業務進程自身的速度,同時持久化的文件必定程度上能夠保證數據不丟失。可是目前尚未特別靠譜的方案來保證這條路徑和第一種方式同樣可靠。

使用方須要在這兩種模式之間進行選擇說明如今的基礎設施還不夠完善。理想的kafka寫入方式應該兼具可靠性和高性能:

  1. 首先業務進程經過本地socket報告事件給本機的agent

  2. 本機的agent經過memory mapped file 落磁盤保證事件不丟

  3. agent批量把聚集的事件可靠寫入到 kafka 集羣裏

也就是在業務進程和遠程的kafka集羣之間添加一個本地的可靠隊列。基本上rsyslog的設計是知足這樣的條件的,可是實際使用中發現rsyslog仍然不夠完美,能夠爲structured logging定製一個更好的agent:

  • 更好的性能

  • 對結構的原生支持,並能夠在入口處校驗格式

  • 批量聚集的事件能夠把小的event打成大的封包格式,甚至能夠把行轉成列壓縮,並利於後端按列處理

add a local https://github.com/OpenHFT/Chronicle-Queue before kafka would be awsome

多集羣之間倒騰數據

分佈式系統最多見的口號是你不用關心數據在哪裏,咱們幫你把數據自動分佈到多臺機器上去。問題是現實狀況是一個完整的流程須要多個分佈式系統來彼此配合,可是它們都各自爲政,本身都是一個獨立的王國。最差的狀況是這樣

clusterA 1                      clusterB 1
clusterA 接口機 => clusterA 2 => clusterB 接口機 => clusterB 2
                   clusterA 3                      clusterB 3

每一個集羣都提供一個數據的入口,而後從這個入口轉發一層到本身後端。從一條數據的角度來講,他須要不斷地在機器之間倒手,序列化反序列,路由再路由,才能到達最終的目的地。對於一個常見的日誌集羣來講,數據就是這樣地不斷地被倒騰。對於非結構化日誌來講,這樣的倒騰是必要的,由於日誌要通過不斷的再處理,可是對於結構化日誌的場景來講,就是浪費:

業務進程集羣 => 日誌解析集羣 (或者kafka入庫程序集羣) => kafka集羣 => ES入庫程序集羣 => ES集羣

理想的設置是

業務進程集羣 => 本地agent => kafka/es 集羣

數據經過本地agent直接錄入到 kafka/es混合的集羣裏。這種混合沒法經過簡單地同機部署kafka和es來達到,由於這兩個分佈式系統都是假定本身掌控數據的分佈的。只有修改kafka和es讓兩個系統深度融合才能實現,讓kafka作爲es的write ahead logging(WAL,在es中叫translog)才能達到最佳的效果。這個kafka/es混合集羣就是一個混合的事件存儲

  • kafka提供事件的順序消費支持

  • es提供事件的檢索支持

  • es利用kafka作爲本身的WAL,實現索引的可靠創建

低效地封包格式和按行處理

由於整個基礎設施是爲非結構化日誌準備的,因此一些習慣地作法也是爲了方便非結構化日誌而存在的,好比:

  • 每一個事件封裝成一個json

  • 事件之間用\n換行符隔開

這種封包格式相比正則解析日誌是巨大的進步了,可是仍然是很是低效的。對於結構化日誌咱們有更好的選擇

  • 事件能夠用帶schema信息的格式,避免反覆出現key/value裏的key。好比pb,avro這樣的格式

  • 事件能夠被打包成一個大的包,包內能夠按列表示數據,進行高效壓縮

  • 事件之間能夠用頭信息指定本身的包的尺寸來隔開,比掃描\n更快

除了包格式的選擇上,處理方式上也是從頭至尾都是按行處理的模式。最佳的處理方式應該是這樣的

按行產生事件 => n行批量組包成一個塊 => 塊內按列進行處理和存儲

在記錄處理的offset上時,每一個offset對應一個塊而不是具體的一行事件。多個行打包成一個高效的按列組織的塊方便批量處理(特別是CPU有針對性的向量化優化)。

在Elasticsearch/Lucene內部也缺少高效地結構化數據入庫(最終是寫入lucene的doc-values文件)的渠道。從源頭就假定輸入是按行組織的JSON文檔。並且內部是以List<Number>而不是long[]來表示數據。原始的數據若是產生就是有格式的,徹底能夠避免一堆中間對象產生。直接產生就是long[]的形式,而後簡單傳輸到Elasticsearch服務器就以long[]進行文件寫入。

總結

Elasticsearch是一個很好的存儲事件的數據庫,很是好地解決了存儲和檢索兩大難題。可是對於結構化的數據入庫支持並非很好,理想地狀況應該是這樣的:

  • 提供一個本地agent,實現數據可靠地高性能入kafka

  • kafka/elasticsearch深度融合,避免集羣拷貝產生的額外負擔,kafka直接做爲elasticsearch的WAL存在

  • 數據以高效的格式封包,而且可以從源頭開始就按列處理,從頭至尾都是long[]而不是List<Map<String, Object>>

相關文章
相關標籤/搜索