flume日誌收集系統

flume日誌收集系統


概述

Flume是一個分佈式的、可靠的、高可用的海量日誌採集系統。它能夠將不同數據源的海量日誌數據進行高效收集、聚合、移動,最後存儲到一箇中心化數據存儲系統中。能夠做到實時推送事件,並且可以滿足數據量是持續且量級很大的情況。

基本組件

Flume傳輸的數據的基本單位是event,如果是文本文件,通常是一行記錄,這也是事務的基本單位,代表着一個數據流的最小完整單元。flume運行的核心是agent。它是一個完整的數據收集工具,含有三個核心組件,分別是source、channel、sink。Event從Source,流向Channel,再到Sink,本身爲一個byte數組,並可攜帶headers信息。基本流程如下: 

  • 1.Source 
    Source主要負責接收數據到Flume Agent組件,可以從其他系統或者應用程序中接收數據,比如web server、log4j等。也可以是從其他Agent的Sink通過RPC發送的數據

  • 2.Channel 
    Channel充當了Source與Sink之間的緩衝區,使得source與sink之間的藕合度降低,source只管向Channel發數據,sink只需從Channel取數據,有點類似於隊列的功能。Agent緩衝已經接收,但尚未寫出到另一個Agent或者存儲系統的數據。多個Source可以同時寫入到一個Channel,多個Sink也可以從相同的Channel讀取,但是一個Sink只能從一個Channel中讀取。它允許Source和Sink可以工作在不同的速率,使得flume可以處理Source 高峯時的負載,即使此時Sink無法讀取Channel。

Channel有多種方式:有MemoryChannel、KafkaChannel、JDBCChannel、FileChannel等。 
Memory Channel 實現是一個內存緩衝區,性能比較好,若JVM或者機器重啓,Channel衝的任何數據都將丟失。如果允許數據小量丟失,推薦使用; 
KafkaChannel 是用Kafka作爲Channel,即就是將數據保存在kafka中。 
File Channel,event 保存在本地磁盤中,可靠性高,只要磁盤上存儲的數據仍然是起作用的和可訪問的,就不會丟失數據,但吞吐量低於Memory Channel; 
JDBC Channel,event保存在關係數據中,一般不推薦使用;

  • 3.Sink 
    Sink主要是移除Channel中的數據並寫入到另一個Agent或者數據存儲或者一些其他系統的組件中。Sink會連續的輪詢Channel中的事件,將事件傳送到下一個目的地,一旦成功傳遞後,Sink就會通知Channel將該事件刪除,這些都是依據於flume中的事務機制實現的。

flume event

flume中event是數據傳輸的基本單元,由消息頭header和消息體body組成,其定義接口如下:

  1. //所在文件:flume-ng-sdk\src\main\java\org\apache\flume\Event.java
  2. public interface Event {
  3. /**
  4. * Returns a map of name-value pairs describing the data stored in the body.
  5. */
  6. public Map<String, String> getHeaders();
  7. /**
  8. * Set the event headers
  9. * @param headers Map of headers to replace the current headers.
  10. */
  11. public void setHeaders(Map<String, String> headers);
  12. /**
  13. * Returns the raw byte array of the data contained in this event.
  14. */
  15. public byte[] getBody();
  16. /**
  17. * Sets the raw byte array of the data contained in this event.
  18. * @param body The data.
  19. */
  20. public void setBody(byte[] body);
  21. }

可以看出header類型爲key-value的Map集合,body爲byte字節數組類型。消息頭header並不是用來傳輸數據的,只是爲了路由和跟蹤發送事件的優先級和嚴重性,還可以給事件添加事件ID等,接下來看下event的基本實現,主要就是實現了header、body兩部分的設置,以及toString方法的重寫:

  1. public class SimpleEvent implements Event {
  2. private Map<String, String> headers;
  3. private byte[] body;
  4. public SimpleEvent() {
  5. headers = new HashMap<String, String>();
  6. body = new byte[0];
  7. }
  8. @Override
  9. public Map<String, String> getHeaders() {
  10. return headers;
  11. }
  12. @Override
  13. public void setHeaders(Map<String, String> headers) {
  14. this.headers = headers;
  15. }
  16. @Override
  17. public byte[] getBody() {
  18. return body;
  19. }
  20. @Override
  21. public void setBody(byte[] body) {
  22. if (body == null) {
  23. body = new byte[0];
  24. }
  25. this.body = body;
  26. }
  27. @Override
  28. public String toString() {
  29. Integer bodyLen = null;
  30. if (body != null) bodyLen = body.length;
  31. return "[Event headers = " + headers + ", body.length = " + bodyLen + " ]";
  32. }
  33. }

看完了event的具體實現,接下來就是event對象的創建了:

  1. /**dir:flume-ng-sdk\src\main\java\org\apache\flume\event\EventBuilder.java
  2. * Instantiate an Event instance based on the provided body and headers.
  3. * If <code>headers</code> is <code>null</code>, then it is ignored.
  4. * @param body
  5. * @param headers
  6. * @return
  7. */
  8. public static Event withBody(byte[] body, Map<String, String> headers) {
  9. Event event = new SimpleEvent(); //創建一個SimpleEvent對象
  10. if (body == null) { //若消息體爲空,則創建字節數組,設置消息體
  11. body = new byte[0];
  12. }
  13. event.setBody(body);
  14. if (headers != null) { //消息頭不爲空,設置header
  15. event.setHeaders(new HashMap<String, String>(headers));
  16. }
  17. return event; //返回event事件
  18. }

以上就是flume 事件event的一個基本創建過程。接下來看看Channel中的Transaction機制如何保證數據完整性。

flume事務(Transaction)

事務是Flume的可靠性的基礎,能夠保證傳輸數據的完整性。這裏說的完整性保證主要取決於Agent中使用的Channel的持久性保證,若使用的是內置的Source或Sink以及一個持久的Channel,Agent可以保證不會丟失數據。flume提供了無數據丟失的保證,主要實現就是Channel中的事務機制(熟悉數據庫原理的應該會對事務這個概念有一些瞭解,但此處的事務又不同於數據庫事務)。每個flume事務代表一批自動寫入到Channel或者從Channel刪除的事件,無論是當Source將事件寫入Channel時,或Sink從Channel讀取事件時,它必須在事務的範圍之內進行操作。 
下圖就是一個事務的大致結構: 

put event是寫事件,take event是讀事件。Source寫事件至Channel時,事務由Channel處理器(ChannelProcessor)處理,只有event被成功寫入Channel,ChannelProcessor才提交事務,否則,ChannelProcessor將回滾該事務並關閉它。代碼邏輯可以從下看出:

  1. //flume-ng-core\src\main\java\org\apache\flume\channel\ChannelProcessor.java
  2. // Process required channels
  3. for (Channel reqChannel : reqChannelQueue.keySet()) {
  4. Transaction tx = reqChannel.getTransaction();
  5. Preconditions.checkNotNull(tx, "Transaction object must not be null");
  6. try {
  7. tx.begin();
  8. List<Event> batch = reqChannelQueue.get(reqChannel);
  9. for (Event event : batch) {
  10. reqChannel.put(event);
  11. }
  12. tx.commit();
  13. } catch (Throwable t) {
  14. tx.rollback();
  15. if (t instanceof Error) {
  16. LOG.error("Error while writing to required channel: " + reqChannel, t);
  17. throw (Error) t;
  18. } else if (t instanceof ChannelException) {
  19. throw (ChannelException) t;
  20. } else {
  21. throw new ChannelException("Unable to put batch on required " +
  22. "channel: " + reqChannel, t);
  23. }
  24. } finally {
  25. if (tx != null) {
  26. tx.close();
  27. }
  28. }

從代碼中可以發現涉及了事務Transaction的begin、commit、rollback、close四個主要方法.此處使用了回調函數機制。

  1. public interface Transaction {
  2. enum TransactionState { Started, Committed, RolledBack, Closed }
  3. void begin();
  4. void commit();
  5. void rollback();
  6. void close();
  7. }

對於讀事件(take event),只有當數據被安全寫出到存儲系統或者下一個agent中的Source,事務才被提交,一旦數據在最終目的地安全,就提交事務,否則寫操作失敗,Flume必須回滾事務,以確保事件不會丟失。對於確保數據安全到達是使用了RPC機制完成。 
讀事件event

  1. public Status process() throws EventDeliveryException {
  2. Status status = Status.READY;
  3. Channel channel = getChannel();
  4. Transaction transaction = channel.getTransaction();
  5. if (resetConnectionFlag.get()) {
  6. resetConnection();
  7. // if the time to reset is long and the timeout is short
  8. // this may cancel the next reset request
  9. // this should however not be an issue
  10. resetConnectionFlag.set(false);
  11. }
  12. try {
  13. transaction.begin();
  14. verifyConnection();
  15. List<Event> batch = Lists.newLinkedList();
  16. for (int i = 0; i < client.getBatchSize(); i++) {
  17. Event event = channel.take();
  18. if (event == null) {
  19. break;
  20. }
  21. batch.add(event);
  22. }
  23. int size = batch.size();
  24. int batchSize = client.getBatchSize();
  25. if (size == 0) {
  26. sinkCounter.incrementBatchEmptyCount();
  27. status = Status.BACKOFF;
  28. } else {
  29. if (size < batchSize) {
  30. sinkCounter.incrementBatchUnderflowCount();
  31. } else {
  32. sinkCounter.incrementBatchCompleteCount();
  33. }
  34. sinkCounter.addToEventDrainAttemptCount(size);
  35. client.appendBatch(batch);
  36. }
  37. transaction.commit();
  38. sinkCounter.addToEventDrainSuccessCount(size);
  39. } catch (Throwable t) {
  40. transaction.rollback();
  41. if (t instanceof Error) {
  42. throw (Error) t;
  43. } else if (t instanceof ChannelException) {
  44. logger.error("Rpc Sink " + getName() + ": Unable to get event from" +
  45. " channel " + channel.getName() + ". Exception follows.", t);
  46. status = Status.BACKOFF;
  47. } else {
  48. destroyConnection();
  49. throw new EventDeliveryException("Failed to send events", t);
  50. }
  51. } finally {
  52. transaction.close();
  53. }
  54. return status;
  55. }
  56. @VisibleForTesting
  57. RpcClient getUnderlyingClient() {
  58. return client;
  59. }
  60. }

以上就是有關flume的一些基本介紹,後邊會繼續分析介紹,深入理解學習。


發佈了45 篇原創文章 · 獲贊 17 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章