1. 程式人生 > >flume使用(五):taildirSource重複獲取資料和不釋放資源解決辦法

flume使用(五):taildirSource重複獲取資料和不釋放資源解決辦法

一、問題思考

(1)log4j的日誌檔案肯定是會根據規則進行滾動的:當*.log滿了就會滾動把前檔案更名為*.log.1,然後重新進行*.log檔案列印。這樣flume就會把*.log.1檔案當作新檔案,又重新讀取一遍,導致重複。

(2)當flume監控的日誌檔案被移走或刪除,flume仍然在監控中,並沒有釋放資源,當然,在一定時間後會自動釋放,這個時間根據官方文件設定預設值是120000ms。

二、處理方式

我這裡不叫解決方式,在其他人的文章中說這兩個是bug,個人認為這都不是bug。大家都知道flume作為apache的頂級專案,真有這樣的bug在它的託管網站上肯定有相關pull並且肯定會有儘快的解決。至少,在flume1.8上會解決掉。個人查看了flume1.8處理的bug和功能的增加list中,對於(1)(2)沒有關於這樣解決項。

官方文件1.8的release說明:只有這一項關於taildir,解決的是當flume關閉檔案同時該檔案正更新資料。

官網:http://flume.apache.org/releases/1.8.0.html

(1)flume會把重新命名的檔案重新當作新檔案讀取是因為正則表示式的原因,因為重新命名後的檔名仍然符合正則表示式。所以第一,重新命名後的檔案仍然會被flume監控;第二,flume是根據檔案inode&&檔案絕對路徑 、檔案是否為null&&檔案絕對路徑,這樣的條件來判斷是否是同一個檔案這個可以看原始碼:下載原始碼,放到maven專案(注意路徑名稱對應),找到taildirsource的包。

先看執行案例:

確實是有重複,然後看原始碼:flume-taildir-source工程

  • ReliableTaildirEventReader 類的 updateTailFiles 方法
  public List<Long> updateTailFiles(boolean skipToEnd) throws IOException {
    updateTime = System.currentTimeMillis();
    List<Long> updatedInodes = Lists.newArrayList();

    for (TaildirMatcher taildir : taildirCache) {
      Map<String, String> headers = headerTable.row(taildir.getFileGroup());

      for (File f : taildir.getMatchingFiles()) {
        long inode = getInode(f);
        TailFile tf = tailFiles.get(inode);
        if (tf == null || !tf.getPath().equals(f.getAbsolutePath())) {
          long startPos = skipToEnd ? f.length() : 0;
          tf = openFile(f, headers, inode, startPos);
        } else {
          boolean updated = tf.getLastUpdated() < f.lastModified();
          if (updated) {
            if (tf.getRaf() == null) {
              tf = openFile(f, headers, inode, tf.getPos());
            }
            if (f.length() < tf.getPos()) {
              logger.info("Pos " + tf.getPos() + " is larger than file size! "
                  + "Restarting from pos 0, file: " + tf.getPath() + ", inode: " + inode);
              tf.updatePos(tf.getPath(), inode, 0);
            }
          }
          tf.setNeedTail(updated);
        }
        tailFiles.put(inode, tf);
        updatedInodes.add(inode);
      }
    }
    return updatedInodes;
  }
重點:
 for (File f : taildir.getMatchingFiles()) {
        long inode = getInode(f);
        TailFile tf = tailFiles.get(inode);
        if (tf == null || !tf.getPath().equals(f.getAbsolutePath())) {
          long startPos = skipToEnd ? f.length() : 0;
          tf = openFile(f, headers, inode, startPos);
        } 
  • TailFile 類的 updatePos 方法:
  public boolean updatePos(String path, long inode, long pos) throws IOException {
    if (this.inode == inode && this.path.equals(path)) {
      setPos(pos);
      updateFilePos(pos);
      logger.info("Updated position, file: " + path + ", inode: " + inode + ", pos: " + pos);
      return true;
    }
    return false;
  }

這樣帶來的麻煩就是當檔案更名後仍然符合正則表示式時,會被flume進行監控,即使inode相同而檔名不同,flume就認為是新檔案。

實際上這是開發者自身給自己造成的不便,完全可以通過監控檔名的正則表示式來排除重新命名的檔案。

就如正則表示式:【.*.log.* 】這樣的正則表示式當然檔案由 .ac.log 重新命名為.ac.log.1會帶來重複讀取的問題。

而正則表示式:【.*.log】 當檔案由 .ac.log 重新命名為 .ac.log.1 就不會被flume監控,就不會有重複讀取的問題。

以上是針對這個問題並flume團隊沒有改正這個問題原因的思考。

當然,如果類似【.*.log.* 】這樣的正則表示式在實際生產中是非常必要使用的話,那麼flume團隊應該會根據github上issue的呼聲大小來考慮是否修正到專案中。

那麼實際生產中真需要這樣的正則表示式來監控目錄下的檔案的話,為了避免重複讀取,就需要對flume1.7原始碼進行修改:

處理問題(1)方式

1.修改 ReliableTaildirEventReader

修改 ReliableTaildirEventReader 類的 updateTailFiles方法。

去除tf.getPath().equals(f.getAbsolutePath()) 。只用判斷檔案不為空即可,不用判斷檔案的名字,因為log4j 日誌切分檔案會重新命名檔案。

 if (tf == null || !tf.getPath().equals(f.getAbsolutePath())) {

修改為:
 if (tf == null) {

2.修改TailFile

修改TailFile 類的 updatePos方法。

inode 已經能夠確定唯一的 檔案,不用加 path 作為判定條件

    if (this.inode == inode && this.path.equals(path)) {
修改為:
    if (this.inode == inode) {

3.將修改過的程式碼打包為自定義source的jar

可以直接打包taildirSource元件即可,然後替換該元件的jar

此時可以進行測試。

處理問題(2)

問題(2)說的是,當監控的檔案不存在了,flume資源沒有釋放。

這個問題也不是問題,實際上,資源的確會釋放,但是 是有一定時間等待。

檢視flume1.7官方文件taildirSource說明:


可知,如果這個檔案在預設值120000ms內都沒有新行append,就會關閉資源;而當有新行append就自動開啟該資源。

也就是說,預設120000ms--》2分鐘後會自動關閉所謂沒有釋放的資源。

為了避免這麼長時間的資源浪費,可以把這個值調小一些。但是,官方給定的預設值為什麼這麼大(相對於類似超時時間都是秒單位的,而這是分鐘單位)?當然不能為所欲為的把這個值改小,頻繁的開關檔案資源造成系統資源的浪費更應該考慮。

一般沒有很好的測試過效能的話,還是按照預設值來就可以了。

以上,是個人見解,若有研究不當之處,敘述不當之處,敬請指出,倍加感謝!