天天看點

storm中DAU實時計算方案

所就職的公司是一家網際網路視訊公司,存在大量的實時計算需求,計算uv,pv等一些經典的實時名額統計。由于要統計當天的實時

UV,當天的uv由于要存儲當天的所有的key,面臨本地記憶體不夠用的問題,異常重新開機後會丢失本地緩存,造成計算結果不準确的問題。;如果使用外部緩存比如redis,memcache等,在高并發時會出現效率問題。

在不斷的實踐中,不斷改進方案,積累了如下經驗:

1.使用bitMap可以節約記憶體。

  使用redis的bitMap,并發時候會有問題。

a .隻使用本地記憶體

由于reidis在資料量較大時,并發會達到瓶頸,幹脆繞過redis,全部使用基于本地記憶體的bitMap方案。這樣即省記憶體,又避免了redis的并發達不到性能要求的問題。

b.使用備份來解決DAU異常恢複問題。

實時任務異常時,由于曆史緩存丢失,造成資料不準确問題。

定時或者定量将本地緩存的b的redis持久化的一個redis,hdfs,kafka中,這些資料隻起到副本備份的作用。

當worker異常重新開機時,先從備份中恢複曆史的曆史的redis中的DAU的資料。然後再開始實時計算。

c.如何備份?備份哪些資料?

解決思路:

使用redis,hdfs等來存儲備份資料,

bitMap的資料序列化,儲存到檔案,然後上傳到hdfs上,或者redis上面。

dauBolt,接收到消息時,在execute方法中先背景恢複本地緩存,恢複成功後,再進行後續的業務處理。

使用kafka來備份當天的uuidSet  :需要備份當天的所有的uuid 

副本的備份每5分鐘備份一次:使用kafka作為副本備份,可以 每1分鐘或者每積累2W條UUID,就将這些UUID封裝成一個msg,發送到kafka的一個topic主題隊列uuidSetTopic裡面,誰先到達執行條件就先執行那個? msg中要包含備份時候的時間戳發送時候的目前時間。

DauBolt:用來恢複當天到目前為止的uuidSet;dau相關的名額統計等;DauBolt會接收到uuidSetTopic的資料,還有userActionTopic的資料。

處理邏輯:

1.DauBolt的計算邏輯:

  如果接收到的是uuidSetTopic的資料:

  取出資料中的時間戳,

  判斷是否是當天的備份資料,如果不是,就不處理,丢棄掉。

  判斷是否本地緩存已經恢複,如果恢複,就不處理,同時通知dauSet_Spout不再讀取uuidSetTopic的資料了。

                                             如果沒恢複, 就解析uuidSet,并逐個恢複到bitMap中。

判斷恢複完成的邏輯:

  取uuidSetTopic的資料中的時間戳dt。

  dt 和目前時間的差小于1.5分鐘,(uuidSetTopic 最遲是一分鐘一條。1.5分鐘可以保證這個時間段内隻有一個因為湊不夠2w條的強制發送的)并且msg的uuid個數小于2w,則說明恢複進入尾聲了,基本上追上了目前時間。

連續兩次差小于1.5分鐘,uuid個數小于2w,也說明追上了最新的值;

  dt 和目前時間的差 小于1.5 分鐘(90s), 假設目前時間差是50s,msg的uuid個數等于2w,說明目前資料較多,不到一分鐘就生産了2w條資料;接着往下取資料,将時間差的閥值縮小目前的內插補點50s;繼續該邏輯,直到時間差大于設定的值,說明追上了最新的資料,本地緩存恢複成功。

  如果目前時間和bolt的啟動時間的間隔差超過15分鐘(也就是允許有15分鐘來恢複dau的計算的本地緩存),也認為恢複完成(不能無限期的等待它恢複啊,但是要在日志中列印出來這種情況)。

2.如果接收到的是userAction的資料:

  取出資料中的uuid

  目前的本地緩存是否已經恢複: 如果恢複,取出uuid,判斷是否在本地的bitMap中,如果不在,更新DAU的值。

                                               如果沒有恢複,不進行uuid是否在bitMap的判斷。

  将uuid 發送到uuidSetSave_bolt.

恢複線程需要根據時間來判斷是否恢複成功了。

當storm 任務啟動前,先向dauBakTopic發送一條消息,UUID為空set,時間戳為目前時間。

作者:

丹江湖畔養蜂子的趙大爹

出處:http://www.cnblogs.com/honeybee/

關于作者:丹江湖畔養蜂子的趙大爹

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連結

繼續閱讀