===================================
(接上文:《架構設計:系統間通訊(32)——其他消息中間件及場景利用(下2)》)
以上兩種方案中為了讓業務系統能夠集成日志收集功能,我們或多或少需要在業務系統端編寫1些代碼。雖然通過1些代碼結構的設計,可以減少乃至完全隔離這些代碼和業務代碼的耦合度,但是畢竟需要業務開發團隊花費精力對這些代碼進行保護,業務系統部署時業務對這些代碼的配置信息做相應的調劑。
這里我們再為讀者介紹1種非侵入式的日志收集方案。我們都知道業務系統被訪問時,都會產生1些訪問痕跡。 一樣以“閱讀商品詳情”這個場景為例,當訪問者打開1個“商品詳情”頁面時(URL記為A),那末首先Nginx的access日志就會有相應的80端口的訪問日志,如果“商品詳情”的信息并不是全靜態的,那末接下來業務服務上工作的代碼還會在Log4j文件上輸出相應的訪問信息(如果開發人員使用了Log4j的話)。我們要做的事情就是找1款軟件,將這些日志信息搜集起來并寄存在適合的位置,以便數據分析平臺隨后利用這些數據進行分析工作。
固然為了保證這些日志信息中有完全的原始屬性,業務系統的開發人員和運維人員應當事前調和1種雙方都認可的日志描寫格式,和日志文件的存儲位置和存儲規則等信息。
Flume is a distributed, reliable, and available service for efficiently collecting, aggregating, and moving large amounts of log data. It has a simple and flexible architecture based on streaming data flows. It is robust and fault tolerant with tunable reliability mechanisms and many failover and recovery mechanisms. It uses a simple extensible data model that allows for online analytic application.
以上文字援用來自Apache Flume官網(http://flume.apache.org/)。大意是:Flume是1個散布式的、具有高可靠的、高可用性的用于有效地搜集、匯總日志數據的服務。它的架構基于數據流,簡單靈活。。。我們要介紹的非侵入日志收集方案,就基于Apache Flume進行實現。
Apache Flume非常非常簡單,并且官方給出的用戶手冊已足夠您了解它的使用方式和工作原理(http://flume.apache.org/FlumeUserGuide.html),所以本文其實不會專門介紹Flume的安裝和基本使用,并試著將Flume的使用融入到實例講授中。如果您希望更深入學習Flume的設計實現,筆者還是建立您瀏覽Flume的源代碼,在其官網的用戶文檔中已給出了幾個關鍵的實現類,通過這些實現類便可倒查Flume使用的各種設計模式:
Flume和業務服務系統在物理服務器上分別獨立工作,在操作系統層面上是兩個獨立的進程,并沒有任何關聯。Flume只對操作系統上的文件系統、或指定的網絡端口又或RPC服務進行數據流監控(Flume中稱之為Source)。當指定的文件、指定的網絡端口或指定的RPC服務有新的數據產生時,Flume就會依照預先的配置將這些數據傳輸到指定位置(Flume中稱之為Sink)。這個指定位置可以是網絡地址、可以是文件系統還可以是另外一個軟件。Source和Sink之間的數據流傳輸通告,稱之為Channel。
上圖來源于Apache Flume官方網站,是1個關于Flume中Source、Sink的例子。在這個例子中,Flume采取1個HTTP Source,用來接收外部傳來的HTTP協議的數據;Flume的Sink端采取HDFS Sink,用來將從Channel中得到的數據寫入HDFS。那末基于上文介紹的Apache Flume工作特性,我們采取以下思路進行日志收集方案3的設計:
上圖中業務系統工作在140、141、1423個物理節點上,并產生Log4j文件。固然您也能夠直接使用JBOSS、Tomcat等服務的原生日志文件作為日志數據來源。有的情況下,我們需要對Nginx等代理服務上Http要求情況進行分析,那末可使用Nginx的access.log文件作為日志數據的源是來源。您還可以根據設計需要,在每個物理節點上同時監控多個文件。
在140、141、1423個物理節點上,還分別安裝了Apache Flume。他們的工作任務都是1樣的,即從指定的需要監控的日志文件中讀取數據變化,通過配置好的Channel送到指定的Sink中。在上圖的設置中既是監控Log4j文件的變化,通過Channel使用Thrift RPC方式傳輸到遠程服務器192.168.61.138的6666端口。
物理節點192.168.61.138負責搜集來自于140、141、1423個物理節點通過Thrift RPC傳輸到6666端口的日志數據信息。并且通過Channel傳輸到適當的存儲方案中,這些適當的存儲方案多是HDFS、多是某1種MQ還多是某種對象存儲系統(例如Ceph)、乃至可能就是本地文件系統。
上文已說明,192.168.61.140物理節點上Apache Flume的主要任務是監控業務服務的Log4j日志文件,當日志文件產生新的數據時,通過Flume中已配置好的Channel發送至指定的Sink。配置信息以下:
agent.sources = s1
agent.channels = c1
agent.sinks = t1
# source ===========================
# log4j.log文件的變化將作為flume的源
agent.sources.s1.type = exec
agent.sources.s1.channels = c1
agent.sources.s1.command = tail -f /logs/log4j.log
# channel ==================================
# 連接source和sink的通道
agent.channels.c1.type = memory
agent.channels.c1.capacity = 1000
# sink t1 ===================================
# 通過通道送來的數據,將通過 thrift RPC調用,送到138節點的6666端口
agent.sinks.t1.type = thrift
agent.sinks.t1.channel = c1
agent.sinks.t1.hostname = 192.168.61.138
agent.sinks.t1.port = 6666
192.168.61.141和192.168.61.142兩個物理節點也承載了業務服務,并且業務服務會將日志輸出到一樣的Log4j的位置。所以這兩個節點上Apache Flume的配置和以上140物理節點中Apache Flume的配置1致。這里就不再對另外兩個物理節點的配置進行贅述了。
另外需要注意的是agent.sources.s1.command配置的Linux tail 命令。tail命令可以顯示當前文件的變化情況,如果您只代有-f參數,即表示從文件末尾的最后10行開始對文件的變化情況進行監控。如果這樣配置,那末當Flume啟動時,就會認為Log4j文件中已存在的10行記錄為新收到的日志數據,造成誤發。
要解決這個問題可使用-n參數,并指定從文件的最末尾開始監控文件變化情況:
# 應當使用
tail -f -n 0 /logs/log4j.log
# 注意:tail -f /logs/log4j.log 命令相當于:
# tail -f -n 10 /logs/log4j.log
192.168.61.138節點上的Flume,用來搜集140⑴42節點通過Thrift RPC傳來的日志數據。這些數據搜集后,將被138節點上的Flume寄存到適合的位置。這些位置可以是HDFS,HBASE、本地文件系統還可以是Apache Kafka等。
agent.sources = s1
agent.channels = c1
agent.sinks = t1
# thrift ==================
# 使用thrift rpc監聽節點的6666端口,以便接收數據
agent.sources.s1.type = thrift
agent.sources.s1.channels = c1
agent.sources.s1.bind = 0.0.0.0
agent.sources.s1.port = 6666
# sink hdfs ==============
# agent.sinks.t1.type = hdfs
# agent.sinks.t1.channel = c1
# agent.sinks.t1.hdfs.path = hdfs://ip:port/events/%y-%m-%d/%H%M/%S
# agent.sinks.t1.hdfs.filePrefix = events-
# agent.sinks.t1.hdfs.round = true
# agent.sinks.t1.hdfs.roundValue = 10
# agent.sinks.t1.hdfs.roundUnit = minute
# sink=====================
# 為了檢測全部配置是不是正確,可先輸出到控制臺
agent.sinks.t1.type = logger
agent.sinks.t1.channel = c1
# channel=================
agent.channels.c1.type = memory
agent.channels.c1.capacity = 1000
以上配置文件中,為了查看這個收集系統的配置是不是成功,我們將在Flume控制臺作為Sink進行輸出。注釋的信息是HDFS作為Sink的配置。
上1小節的解決方案3中,最薄弱的位置是承當日志數據匯總任務的138節點。全部日志搜集主架構中只存在1個這樣的匯總節點,1旦138節點由于各種緣由宕機主架構就將崩潰。即便138節點能夠穩定工作,由于138節點同時承當多個物理節點傳來的數據日志,那末它也極有可能成為性能瓶頸。所以我們需要找到1種方案3中薄弱位置的辦法。
還好,Apache Flume為我們提供了非常簡單實用的高可用模式:Load_balance模式和Failover模式。這兩種工作模式都是對多個Sink如何配合工作進行描寫:
這類工作模式提供了多個sinks負載均衡的能力。Load_balance會保護1個active sinks列表,基于這個列表,使用round_robin(輪詢調度) 或 random(隨機) 的選擇機制(默許為:round_robin),向sinks集合?;旧线@兩種選擇方式已夠用了,如果您對調度選擇有特別的要求,則可以通過繼承AbstractSinkSelector類來實現自定義的選擇機制。
這類工作模式提供了多個sinks的故障轉移能力。Failover保護了兩個sinks列表,Failover list和Live list,在Failover模式下,Flume會優先選擇優先級最高的Sink作為主要的發送目標。當這個Sink連續失敗時Flume會把這個Sink移入Failover list,并且設置1個冷凍時間。在這個冷凍時間以后,Flume又會試圖使用這個Sink發送數據,1旦發送成功,這個Sink會被重新移入Live list。
為了保證能夠為數據匯總節點分擔性能壓力,我們使用Load_balance模式進1步演示對數據匯總節點的優化。
從上圖中可以看到在方案3的優化方法中,我們使用1個新的節點(192.168.61.139)和原本的138節點1起構成1組負載節點,共同承當日志數據的匯總任務。那末前端日志監控節點(140、141、1423個節點)也需要做相應的配置文件修改。
agent.sources = s1
agent.channels = c1
# 設置了兩個sink
agent.sinks = lt1 lt2
agent.sinkgroups = g1
# source ===========================
# 數據源還是來自于log4j日志文件的新增數據
agent.sources.s1.type = exec
agent.sources.s1.channels = c1
agent.sources.s1.command = tail -f -n 0 /log/log4j.log
# sink lt1 ===================================
agent.sinks.lt1.type = thrift
agent.sinks.lt1.channel = c1
agent.sinks.lt1.hostname = 192.168.61.138
agent.sinks.lt1.port = 6666
# sink lt2 ==================================
agent.sinks.lt2.type = thrift
agent.sinks.lt2.channel = c1
agent.sinks.lt2.hostname = 192.168.61.139
agent.sinks.lt2.port = 6666
# channel ==================================
agent.channels.c1.type = memory
agent.channels.c1.capacity = 1000
# sinkgroup ===============================
# 兩個sink:lt1 lt2 設置成1組sink。并使用load_balance模式進行工作
agent.sinkgroups.g1.sinks = lt1 lt2
agent.sinkgroups.g1.processor.type = load_balance
agent.sinkgroups.g1.processor.backoff = true
agent.sinkgroups.g1.processor.selector = random
141和142兩個日志數據監控節點的配置和140節點的配置1致,所以一樣不再贅述了。
agent.sources = s1
agent.channels = c1
agent.sinks = t1
# thrift==================
agent.sources.s1.type = thrift
agent.sources.s1.channels = c1
agent.sources.s1.bind = 0.0.0.0
agent.sources.s1.port = 6666
# sink=====================
agent.sinks.t1.type = logger
agent.sinks.t1.channel = c1
# channel=================
agent.channels.c1.type = memory
agent.channels.c1.capacity = 1000
新增的139節點上Flume的配置信息和原有138節點上Flume的配置信息是1致的。這樣保證了不管日志數據被發送到哪個節點,都能正確進行存儲。
日志收集方案3也存在局限性:這類方案不合適用于開放性日志收集系統。也就是說,如果您的日志收集系統需要像“百度站長統計工具”那樣,從設計之初的目標就是要發布給互聯網上各個站點使用的。那末這類基于操作系統日志變化,并采取第3方軟件完成收集進程的架構方案就不適用。
另外,方案3我們使用了Thrift RPC進行網絡通訊。這個方式是可以用于真實的生產環境的,但是需要進行更多的配置項指定。以下兩個鏈接地址是分別是使用thrift作為source和sink時可使用的配置屬性。
http://flume.apache.org/FlumeUserGuide.html#thrift-source
http://flume.apache.org/FlumeUserGuide.html#thrift-sink
除Thrift RPC之外,筆者還推薦使用Avro。
//TODO 這是1個扣子,后續的文章會講到
經過《架構設計:系統間通訊(19)——MQ:消息協議(上)》開始的14篇文章,我們基本上介紹了消息隊列的基本知識和使用實戰。從下文開始我們轉向ESB企業服務總線的知識講授。