概括下本文的內容:
我們可以使用 Logstash 的持久化隊列技術盡量保證數據可靠傳輸至 output;
適用場景:傳輸可靠性要求稍低的場景下(和 Kafka 類比),替換架構中的 Kafka 或者加固 Logstash 本身的可靠性,因為即使 queue.checkpoint.writes:1,也有可能因為磁盤故障(檢查點文件 和 queue 文件同時損壞)丟至多 1 條數據,核心的問題是在于沒有多副本和選舉相關的實現;
Deliver 策略:at-least-once;
默認情況下,Logstash在 pipeline stages(inputs → pipeline workers)之間使用內存有界隊列來緩沖事件。 這些內存中隊列的大小是固定的,不可配置。 如果 Logstash 遇到臨時機器故障,則內存中隊列的內容將丟失。 臨時機器故障是指 Logstash 或其主機異常終止但能夠重新啟動的情況。
為了防止異常終止期間的數據丟失,Logstash 具有持久性隊列功能,將消息隊列存儲在磁盤上。 持久隊列在 Logstash 中提供數據的持久性。
持久隊列對于需要大型緩沖區的 Logstash 部署也很有用。 換言之,可以啟用持久性隊列來緩存磁盤上的事件而無需使用較重的組件(如 Redis,RabbitMQ 或 Apache Kafka)來實現緩沖的發布訂戶模型。
總而言之,啟用持久隊列的好處如下:
- 處理突發事件,而不需要像 Redis 或 Apache Kafka 這樣的外部緩沖機制;
- 在正常關機期間以及 Logstash 異常終止時,提供 at-least-once 傳輸保證,防止消息丟失。 如果 Logstash 在事件傳遞時(我理解是事務執行過程中,in-flight)重新啟動,Logstash 將嘗試傳遞存儲在持久性隊列中的消息,直到傳送成功至少一次。
持久隊列的限制
以下是持久隊列功能未解決的問題:
- 不使用 request-response 協議的 input 插件無法避免數據丟失。 例如:tcp,udp,zeromq push + pull和許多其他輸入沒有機制來確認 receipt 的發送方。 具有確認功能的插件(如 beats 和 http)受到此隊列的良好保護。
- 它不會處理永久性的機器故障,如磁盤損壞,磁盤故障和機器丟失,持續到磁盤的數據不會被復制。
持久隊列如何工作
隊列位于同一進程的 input 和 filter 階段之間:
input → queue → filter + output
當 input 有事件準備好處理時,它將事件寫入隊列。當寫入隊列成功時, input 可以向其數據源發送 ack。
Logstash 只會在 filter 和 output 完成事件處理后,確認事件已完成。隊列保留 pipeline 已處理的事件的記錄。事件被記錄為已處理(acked),當且僅當事件已由 Logstash pipeline 完全處理。
Ack 這意味著事件已由所有配置的 filter 和 output 處理。例如,如果只有一個到 Elasticsearch的輸出,當 Elasticsearch output 已成功將此事件發送到 Elasticsearch 時,事件將被 ack。
在正常關機(Kill 或 SIGTERM)期間,Logstash 將停止從隊列中讀取,并完成由 filter 和 output 處理的 in-flight 事件。重新啟動后,Logstash 將恢復處理持久性隊列中的事件以及從 input 接受新事件。
如果 Logstash 異常終止,任何 in-flight 事件將不會被 ack,并且當 Logstash 重新啟動時將被 filter 和 output 重新處理。 Logstash 分批處理事件,因此對于任何給定的批處理,有可能已經成功完成了該批次,但是在發生異常終止時不能被記錄為已確認,所以可以解釋為什么會有重復發送。
如何配置持久化隊列
要配置持久性隊列,可以在Logstash設置文件中指定以下選項:
queue.type
:指定持久化以啟用持久性隊列。默認情況下,持久隊列被禁用(默認:queue.type:memory)。
path.queue
:數據文件將被存儲的目錄路徑。默認情況下,文件存儲在path.data/queue中。
queue.page_capacity
:隊列頁面的最大大小(以字節為單位)。隊列數據由僅附加文件稱為“頁面”組成。默認大小為250MB。更改此值不太可能具有性能優勢。
queue.max_events
:隊列中允許的最大事件數。默認值為0(無限制)。該值在內部用于Logstash測試。
queue.max_bytes
:隊列的總容量,以字節為單位。默認值為1024MB(1GB)。確保磁盤驅動器的容量大于此處指定的值。
如果同時指定了queue.max_events
和queue.max_bytes
,則Logstash將使用首先達到的條件。。
還可以指定控制檢查點文件何時更新的選項(queue.checkpoint.acks
,queue.checkpoint.writes
)。
示例配置:
queue.type:persisted
queue.max_bytes:4gb
處理 Back Pressure
當隊列已滿時,Logstash 會對 input 端施加壓力,以阻止流入 Logstash 的數據。這種機制有助于 Logstash 在 input 階段控制數據流量,而不會向比如 Elasticsearch 類似的 output 端瘋狂輸出。
使用queue.max_bytes
設置配置磁盤上隊列的總容量。以下示例將隊列的總容量設置為8gb:
queue.type:persisted
queue.max_bytes:8gb
指定這些設置后,Logstash 將緩存磁盤上的事件,直到隊列的大小達到8gb。當隊列充滿 unACKed 的事件,并且已達到大小限制時,Logstash 將不再接受新的事件。
每個 input 單獨處理 Back Pressure。例如,當 beats input 遇到 back pressure 時,它不再接受新連接并等待,直到隊列有空間來接受更多的事件。filter 和 output 階段完成處理隊列中的現有事件并確認它們后,Logstash 會自動開始接受新的事件。
控制 Durability
Durability 是存儲寫入的一種特性,可確保數據在寫入后可用。
當啟用持久性隊列功能時,Logstash 會將事件存儲在磁盤上。 Logstash以稱為檢查點的機制提交到磁盤。
為了討論 Durability,我們需要介紹一些關于如何實現持久隊列的細節。
首先,隊列本身是一組 pages。有兩種 page:head page 和 tail page。head page 是新事件寫入的地方,只有一個。當 head page 達到上限(參見queue.page_capacity
)時,它將成為一個 tail page,并創建一個新的 page。tail page 是不修改的,head page 是只能追加內容的。其次,隊列將記錄自己的詳細信息(pages,ack 確認信息等)記錄在一個名為checkpoint 文件的單獨文件中。
記錄檢查點時,Logstash將:
- 在 head page 上調用 fsync。
- 原子地寫入隊列的當前狀態至磁盤。
以下設置可用于調整持久性:
-
queue.checkpoint.writes
:Logstash 在寫入指定條數事件后,進行 checkpoint。目前,一個事件計為一個寫入,但這可能會在將來的版本中更改。 -
queue.checkpoint.acks
:Logstash 在指定條數事件 acked 后,進行 checkpoint。此配置用來控制 Logstash 處理(filter + output)環節的 Durability。
磁盤寫入具有資源成本。調整上述值或更高值將會調整 durability。例如,如果您想要所有 input 事件具備最高 durability,可以設置queue.checkpoint.writes:1
。
檢查點的過程是原子的,這意味著如果成功,將保存文件的任何更新。
如果 Logstash 終止,或者如果存在硬件級別故障,那么在持久性隊列中緩存但尚未檢查點的任何數據都將丟失。為了避免這種可能性,您可以設置 queue.checkpoint.writes:1
,但請記住,此設置會嚴重影響性能。
磁盤垃圾回收
在磁盤上,隊列存儲為一組 page,其中每個 page 是一個文件。 每個 page 最多可以是 queue.page_capacity
的大小。 在該 page 中的所有事件都被確認之后,page 被刪除(垃圾回收)。 如果較舊的 page 至少有一個尚未確認的事件,則整個 page 將保留在磁盤上,直到該 page 中的所有事件成功處理。 包含未處理事件的每個 page 將對queue.max_bytes
字節大小進行計數。