整合storm-hdfs過程中源碼學習

前一段整合了stomr-hdfs,但是發現在原有的storm-hdfs-0.9.4.jar中的寫入數據的邏輯不滿足我們的需求,於是乎需要看源碼,然後在源碼的基礎上改寫源碼,滿足自己的需求。

整合storm-hdfs的過程,其實也就是編寫storm的拓撲結構,然後調用storm-hdfs-0.9.4.jar中的hdfsBolt,通過配置hdfsBolt的一些與hdfs有關的參數,將數據寫入到hdfs中。

配置的參數:

1、RecordFormat:定義字段定界符,你可以使用換行符\n或者製表符\t;

2、SyncPolicy:定義每次寫入的tuple的數量;

3、FileRotationPolicy:定義寫入的hdfs文件的輪轉策略,你可以以時間輪轉(TimedRotationPolicy)、大小輪轉(FileSizeRotationPolicy)、不輪轉(NoRotationPolicy);

4、FileNameFormat:定義寫入文件的路徑(withPath)和文件名的前後綴(withPrefix、withExtension);

5、withFsUrl:定義hdfs的地址。

hdfsBolt中寫數據的源碼:

  1. public void execute(Tuple tuple)  
  2.  {  
  3.    try  
  4.    {  
  5.      byte[] bytes = this.format.format(tuple);  //對每一條數據添加定界符  
  6.      synchronized (this.writeLock)  
  7.      {  
  8.        this.out.write(bytes);     //調用輸出流寫數據  
  9.        this.offset += bytes.length;  //更新寫入文件的當前大小  
  10.        if (this.syncPolicy.mark(tuple, this.offset))      //當數據條數滿足所配的條數時,寫入到hdfs  
  11.        {         
  12.          if ((this.out instanceof HdfsDataOutputStream)) {        
  13.            ((HdfsDataOutputStream)this.out).hsync(EnumSet.of(HdfsDataOutputStream.SyncFlag.UPDATE_LENGTH));  
  14.          } else {  
  15.            this.out.hsync();  
  16.          }  
  17.          this.syncPolicy.reset();  
  18.        }  
  19.      }  
  20.      this.collector.ack(tuple);  
  21.      if (this.rotationPolicy.mark(tuple, this.offset))   //噹噹前文件大小等於所配輪轉文件的大小,則輪轉文件,重建新的寫入文件  
  22.      {  
  23.        rotateOutputFile();  
  24.        this.offset = 0L;  
  25.        this.rotationPolicy.reset();  
  26.      }  
  27.    }  
  28.    catch (IOException e)  
  29.    {  
  30.      LOG.warn("write/sync failed.", e);  
  31.      this.collector.fail(tuple);  
  32.    }  
  33.  }  


hdfsBolt每次新建文件的方法:

  1. Path createOutputFile()  
  2.     throws IOException  
  3.   {  
  4.     Path path = new Path(this.fileNameFormat.getPath(), this.fileNameFormat.getName(this.rotation, System.currentTimeMillis()));  
  5.     this.out = this.fs.create(path);  //新建一個輸出流(對應一個新的文件)  
  6.     return path;   //返回路徑  
  7.   }  


輪轉文件的方法:

  1. protected void rotateOutputFile()  
  2.     throws IOException  
  3.   {  
  4.     LOG.info("Rotating output file...");  
  5.     long start = System.currentTimeMillis();  
  6.     synchronized (this.writeLock)  
  7.     {  
  8.       closeOutputFile();    //關閉前一個文件的輸出流  
  9.       this.rotation += 1;  //輪轉數加一(這裏的輪轉數會反應到文件名上)  
  10.         
  11.       Path newFile = createOutputFile();   //新建一個文件  
  12.       LOG.info("Performing {} file rotation actions.", Integer.valueOf(this.rotationActions.size()));  
  13.       for (RotationAction action : this.rotationActions) {  
  14.         action.execute(this.fs, this.currentFile);  
  15.       }  
  16.       this.currentFile = newFile;  //更新當前寫入文件的路徑  
  17.     }  
  18.     long time = System.currentTimeMillis() - start;  
  19.     LOG.info("File rotation took {} ms.", Long.valueOf(time));  
  20.   }  



添加字段定界符源碼:

  1. public byte[] format(Tuple tuple)  
  2.   {  
  3.     StringBuilder sb = new StringBuilder();  
  4.     Fields fields = this.fields == null ? tuple.getFields() : this.fields;  
  5.     int size = fields.size();  
  6.     for (int i = 0; i < size; i++)  
  7.     {  
  8.       sb.append(tuple.getValueByField(fields.get(i)));  
  9.       if (i != size - 1) {  
  10.         sb.append(this.fieldDelimiter);  
  11.       }  
  12.     }  
  13.     sb.append(this.recordDelimiter);   //添加定界符  
  14.     return sb.toString().getBytes();  
  15.   }  



CountSyncPolicy源碼,CountSyncPolicy實現SyncPolicy的接口方法:

  1. public class CountSyncPolicy  
  2.   implements SyncPolicy  
  3. {  
  4.   private int count;       //配置的每次寫入tuple數量  
  5.   private int executeCount = 0//當前已經執行的tuple的數量  
  6.     
  7.   public CountSyncPolicy(int count)  
  8.   {  
  9.     this.count = count;  
  10.   }  
  11.     
  12.   public boolean mark(Tuple tuple, long offset)    //判斷當前寫入輸出流緩存中的數量是否超過每次寫入數量  
  13.   {  
  14.     this.executeCount += 1;  
  15.     return this.executeCount >= this.count;  
  16.   }  
  17.     
  18.   public void reset()  
  19.   {  
  20.     this.executeCount = 0;    //重置方法,每次寫入後,執行重置方法歸零  
  21.   }  
  22. }  



FileSizeRotationPolicy的源碼(FileSizeRotationPolicy實現FileRotationPolicy的接口方法):

  1. public class FileSizeRotationPolicy  
  2.   implements FileRotationPolicy  
  3. {  
  4.   private static final Logger LOG = LoggerFactory.getLogger(FileSizeRotationPolicy.class);  
  5.   private long maxBytes;  //文件寫滿的大小  
  6.     
  7.   public static enum Units  
  8.   {                                                                                                                                           //文件切換輪轉的大小單位  
  9.     KB(Math.pow(2.0D, 10.0D)),  MB(Math.pow(2.0D, 20.0D)),  GB(Math.pow(2.0D, 30.0D)),  TB(Math.pow(2.0D, 40.0D));  
  10.       
  11.     private long byteCount;  
  12.       
  13.     private Units(long byteCount)  
  14.     {  
  15.       this.byteCount = byteCount;  
  16.     }  
  17.       
  18.     public long getByteCount()  
  19.     {  
  20.       return this.byteCount;  
  21.     }  
  22.   }  
  23.     
  24.   private long lastOffset = 0L;  
  25.   private long currentBytesWritten = 0L;  
  26.     
  27.   public FileSizeRotationPolicy(float count, Units units)  
  28.   {  
  29.     this.maxBytes = ((count * (float)units.getByteCount())); //根據切換文件的單位來計算文件寫滿該有的大小  
  30.   }  
  31.     
  32.   public boolean mark(Tuple tuple, long offset)   //文件是否切換的判斷方法  
  33.   {  
  34.     long diff = offset - this.lastOffset;  
  35.     this.currentBytesWritten += diff;  
  36.     this.lastOffset = offset;  
  37.     return this.currentBytesWritten >= this.maxBytes;  
  38.   }  
  39.     
  40.   public void reset()    //重置方法  
  41.   {  
  42.     this.currentBytesWritten = 0L;  //當前文件已寫的大小  
  43.     this.lastOffset = 0L;  //一次寫入後的offset值  
  44.   }  
  45. }  


DefaultFileNameFormat的源碼(DefaultFileNameFormat實現FileNameFormat的接口方法):

  1. public class DefaultFileNameFormat  
  2.   implements FileNameFormat  
  3. {  
  4.   private String componentId;  
  5.   private int taskId;         //任務名id  
  6.   private String path = "/storm"//寫入的目錄路徑  
  7.   private String prefix = "";   //文件名前綴  
  8.   private String extension = ".txt";//文件名後綴  
  9.     
  10.   public DefaultFileNameFormat withPrefix(String prefix)  
  11.   {  
  12.     this.prefix = prefix;  
  13.     return this;  
  14.   }  
  15.     
  16.   public DefaultFileNameFormat withExtension(String extension)  
  17.   {  
  18.     this.extension = extension;  
  19.     return this;  
  20.   }  
  21.     
  22.   public DefaultFileNameFormat withPath(String path)  
  23.   {  
  24.     this.path = path;  
  25.     return this;  
  26.   }  
  27.     
  28.   public void prepare(Map conf, TopologyContext topologyContext)  
  29.   {  
  30.     this.componentId = topologyContext.getThisComponentId();  
  31.     this.taskId = topologyContext.getThisTaskId();  
  32.   }  
  33.     
  34.   public String getName(long rotation, long timeStamp)  //得到寫入文件的文件名  
  35.   {  
  36.     return this.prefix + this.componentId + "-" + this.taskId + "-" + rotation + "-" + timeStamp + this.extension;  
  37.   }  
  38.     
  39.   public String getPath()  
  40.   {  
  41.     return this.path;  
  42.   }  
  43. }  

http://blog.csdn.net/u014039577/article/details/50215913
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章