Disruptor的應用示例——大文件拆分


結合最近Disruptor的學習,和之前一直思考解決的大文件拆分問題,想到是否可以使用Disruptor作為生產者/消費者傳遞數據的通道呢?借助其高效的傳遞,理論上應當可以提升性能。此文便是此想法的落地實現。

問題描述

將大文件按照指定大小拆分為若干小文件。具體可參考:大文件拆分方案的java實踐(附源碼)

方案設計

設計簡圖

如下:

核心組件

  • FileReadTask —— Disruptor的生產者線程,負責讀取源文件,;
  • Disruptor —— FileReadTask和FileLineEventHandler線程之間傳遞數據的通道,無阻塞;
  • RingBuffer —— Disruptor的核心組件,負責暫存被傳遞的消息,同時負責協調生產者和消費者之間的工作節奏;
  • FileLineEventHandler —— 不斷從Disruptor中讀取FileLine,並直接扔給FileWriteTask的queue,是Disruptor的消費者,同時也是queue的生產者;
  • FileWriteTask —— 從queue中讀取FileLine,並寫入到子文件,是queue的消費者。

設計思路

使用Disruptor作為生產者和消費者之間傳遞數據的通道,利用Disruptor高效傳遞數據的特性提升性能;

FileLineEventHandler作為Disruptor的消費者,只負責從中讀取數據,但是不負責耗時長的子文件操作;

FIleWriteTask服務耗時長的文件寫入工作,且每個task獨享queue,減少資源競爭。

 性能表現

實測下來,和之前的‘生產者/消費者+nio’方案性能相當,最佳性能點為:

方案

-Xms

-Xmx

readTaskNum

writeTaskNum

queueSize

Durition
(ms)

jvm_
CPU(%)

jvm_
mem

Physics
_mem

生產者/消費者+nio

512m

512m

24

8

4096

8158

80

100M

4.6G

Disruptor+生產者/消費者+nio

512m

512m

2

2

1024

6191  

80

500m

4.2G

 相對與不使用Disruptor的方案,時延有所下降,但是並不明顯,兩個方案主要瓶頸都在於FileReadTask中對文件進行拆分的邏輯處理太費時,需要逐個字節讀取並比對是否為換行符/回車符。所以性能提升並不是很明顯。且性能表現並不穩定。

心得

這個示例或許沒有達到想要的效果,但是通過這個實例,將Disruptor用到了生產者和消費者模式中,體會Disruptor的設計初衷,提升生產者與消費者之間數據傳遞的效率,尤其是在純粹地快速交換數據的場景非常有用。

Disruptor持有的entry對象不宜直接傳遞給后續消費者使用,鑒於Disruptor會對RingBuffer的entries做內存預加載,且會循環使用對應entries,所以如果供消費者直接使用,會出現數據覆蓋的問題。可以參考實例代碼中FileLineEventHandler對寫入queue的FileLine的處理。

代碼示例

github地址:https://github.com/daoqidelv/filespilt-demo

包路徑:com.daoqidlv.filespilt.disruptor

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM