(一)初識(shí)Disruptor

通過這篇文章來記錄和分享對(duì)Disruptor的初步了解認(rèn)識(shí)Disruptor框架巡社。

Disruptor是什么寂拆?

Disruptor是一個(gè)高性能的異步處理框架,或者可以認(rèn)為是線程間通信的高效低延時(shí)的內(nèi)存消息組件,它最大特點(diǎn)是高性能蔬崩,其LMAX架構(gòu)可以獲得每秒6百萬訂單蚁署,用1微秒的延遲獲得吞吐量為100K+便脊。
它是如何實(shí)現(xiàn)高性能的呢?它由于JDK內(nèi)置的隊(duì)列有什么區(qū)別呢光戈?

JDK內(nèi)置內(nèi)存隊(duì)列哪痰?

我們知道,Java內(nèi)置了幾種內(nèi)存消息隊(duì)列久妆,如下所示:

隊(duì)列 加鎖方式 是否有界 數(shù)據(jù)結(jié)構(gòu)
ArrayBlockingQueue 加鎖 有界 ArrayList
LinkedBlockingQueue 加鎖 無界 LinkedList
ConcurrentLinkedQueue CAS 無界 LinkedList
LinkedTransferQueue CAS 無界 LinkedList

我們知道CAS算法比通過加鎖實(shí)現(xiàn)同步性能高很多晌杰,而上表可以看出基于CAS實(shí)現(xiàn)的隊(duì)列都是無界的,而有界隊(duì)列是通過同步實(shí)現(xiàn)的筷弦。在系統(tǒng)穩(wěn)定性要求比較高的場(chǎng)景下肋演,為了防止生產(chǎn)者速度過快抑诸,如果采用無界隊(duì)列會(huì)最終導(dǎo)致內(nèi)存溢出,只能選擇有界隊(duì)列爹殊。
而有界隊(duì)列只有ArrayBlockingQueue蜕乡,該隊(duì)列是通過加鎖實(shí)現(xiàn)的,在請(qǐng)求鎖和釋放鎖時(shí)對(duì)性能開銷很大梗夸,這時(shí)候基于有界隊(duì)列的高性能的Disruptor就應(yīng)運(yùn)而生层玲。

Disruptor如何實(shí)現(xiàn)高性能?

Disruptor實(shí)現(xiàn)高性能主要體現(xiàn)了去掉了鎖反症,采用CAS算法辛块,同時(shí)內(nèi)部通過環(huán)形隊(duì)列實(shí)現(xiàn)有界隊(duì)列。

  • 環(huán)形數(shù)據(jù)結(jié)構(gòu)
    為了避免垃圾回收铅碍,采用數(shù)組而非鏈表憨降。同時(shí),數(shù)組對(duì)處理器的緩存機(jī)制更加友好该酗。
  • 元素位置定位
    數(shù)組長(zhǎng)度2^n授药,通過位運(yùn)算,加快定位的速度呜魄。下標(biāo)采取遞增的形式悔叽。不用擔(dān)心index溢出的問題。index是long類型爵嗅,即使100萬QPS的處理速度娇澎,也需要30萬年才能用完。
  • 無鎖設(shè)計(jì)
    每個(gè)生產(chǎn)者或者消費(fèi)者線程睹晒,會(huì)先申請(qǐng)可以操作的元素在數(shù)組中的位置趟庄,申請(qǐng)到之后,直接在該位置寫入或者讀取數(shù)據(jù)伪很。整個(gè)過程通過原子變量CAS戚啥,保證操作的線程安全。

Disruptor可以用來做什么锉试?

當(dāng)前業(yè)界開源組件使用Disruptor的包括Log4j2猫十、Apache Storm等,它可以用來作為高性能的有界內(nèi)存隊(duì)列呆盖,基于生產(chǎn)者消費(fèi)者模式拖云,實(shí)現(xiàn)一個(gè)/多個(gè)生產(chǎn)者對(duì)應(yīng)多個(gè)消費(fèi)者。它也可以認(rèn)為是觀察者模式的一種實(shí)現(xiàn)应又,或者發(fā)布訂閱模式宙项。


生產(chǎn)者消費(fèi)者.png

同時(shí),Disruptor還允許開發(fā)者使用多線程技術(shù)去創(chuàng)建基于任務(wù)的工作流株扛。Disruptor能用來并行創(chuàng)建任務(wù)尤筐,同時(shí)保證多個(gè)處理過程的有序性邑贴,并且它是沒有鎖的。

工作流.png

為什么要使用Disruptor叔磷?

使用Disruptor拢驾,主要用于對(duì)性能要求高、延遲低的場(chǎng)景改基,它通過“榨干”機(jī)器的性能來換取處理的高性能繁疤。如果你的項(xiàng)目有對(duì)性能要求高,對(duì)延遲要求低的需求秕狰,并且需要一個(gè)無鎖的有界隊(duì)列稠腊,來實(shí)現(xiàn)生產(chǎn)者/消費(fèi)者模式,那么Disruptor是你的不二選擇鸣哀。

怎么用Disruptor架忌?

要學(xué)會(huì)基于Disruptor進(jìn)行編程,我們先了解下大概流程示意圖我衬,其中綠色部分是表示我們需要編寫和實(shí)現(xiàn)的類叹放。


Disruptor執(zhí)行簡(jiǎn)圖(2).png

下面我們實(shí)現(xiàn)一個(gè)簡(jiǎn)單的用例,生產(chǎn)者負(fù)責(zé)將輸入的字符串輸出到隊(duì)列挠羔,消費(fèi)者負(fù)責(zé)打印出來井仰。

public class DisruptorTest {
    /**
     * 消息事件類
     */
    public static class MessageEvent{
        /**
         * 原始消息
         */
        private String message;

        public String getMessage() {
            return message;
        }

        public void setMessage(String message) {
            this.message = message;
        }
    }

    /**
     * 消息事件工廠類
     */
    public static class MessageEventFactory implements EventFactory<MessageEvent>{
        @Override
        public MessageEvent newInstance() {
            return new MessageEvent();
        }
    }

    /**
     * 消息轉(zhuǎn)換類,負(fù)責(zé)將消息轉(zhuǎn)換為事件
     */
    public static class MessageEventTranslator implements EventTranslatorOneArg<MessageEvent,String> {
        @Override
        public void translateTo(MessageEvent messageEvent, long l, String s) {
            messageEvent.setMessage(s);
        }
    }

    /**
     * 消費(fèi)者線程工廠類
     */
    public static class MessageThreadFactory implements ThreadFactory{
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r,"Simple Disruptor Test Thread");
        }
    }

    /**
     * 消息事件處理類破加,這里只打印消息
     */
    public static class MessageEventHandler implements EventHandler<MessageEvent>{
        @Override
        public void onEvent(MessageEvent messageEvent, long l, boolean b) throws Exception {
            System.out.println(messageEvent.getMessage());
        }
    }

    /**
     * 異常處理類
     */
    public static class MessageExceptionHandler implements ExceptionHandler<MessageEvent>{
        @Override
        public void handleEventException(Throwable ex, long sequence, MessageEvent event) {
            ex.printStackTrace();
        }

        @Override
        public void handleOnStartException(Throwable ex) {
            ex.printStackTrace();

        }

        @Override
        public void handleOnShutdownException(Throwable ex) {
            ex.printStackTrace();

        }
    }

    /**
     * 消息生產(chǎn)者類
     */
    public static class MessageEventProducer{
        private RingBuffer<MessageEvent> ringBuffer;

        public MessageEventProducer(RingBuffer<MessageEvent> ringBuffer) {
            this.ringBuffer = ringBuffer;
        }

        /**
         * 將接收到的消息輸出到ringBuffer
         * @param message
         */
        public void onData(String message){
            EventTranslatorOneArg<MessageEvent,String> translator = new MessageEventTranslator();
            ringBuffer.publishEvent(translator,message);
        }
    }

    public static void main(String[] args) {
        String message = "Hello Disruptor!";
        int ringBufferSize = 1024;//必須是2的N次方
        Disruptor<MessageEvent> disruptor = new Disruptor<MessageEvent>(new MessageEventFactory(),ringBufferSize,new MessageThreadFactory(),ProducerType.SINGLE,new BlockingWaitStrategy());
        disruptor.handleEventsWith(new MessageEventHandler());
        disruptor.setDefaultExceptionHandler(new MessageExceptionHandler());
        RingBuffer<MessageEvent> ringBuffer = disruptor.start();
        MessageEventProducer producer = new MessageEventProducer(ringBuffer);
        producer.onData(message);
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末俱恶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子范舀,更是在濱河造成了極大的恐慌合是,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锭环,死亡現(xiàn)場(chǎng)離奇詭異聪全,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)田藐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門荔烧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人汽久,你說我怎么就攤上這事∮徊停” “怎么了景醇?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)吝岭。 經(jīng)常有香客問我三痰,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任诬垂,我火速辦了婚禮牍戚,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘获搏。我一直安慰自己赖条,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布常熙。 她就那樣靜靜地躺著纬乍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪裸卫。 梳的紋絲不亂的頭發(fā)上仿贬,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音墓贿,去河邊找鬼茧泪。 笑死,一個(gè)胖子當(dāng)著我的面吹牛聋袋,可吹牛的內(nèi)容都是我干的调炬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼舱馅,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼缰泡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起代嗤,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤棘钞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后干毅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宜猜,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年硝逢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了姨拥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渠鸽,死狀恐怖叫乌,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情徽缚,我是刑警寧澤憨奸,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站凿试,受9級(jí)特大地震影響排宰,放射性物質(zhì)發(fā)生泄漏似芝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一板甘、第九天 我趴在偏房一處隱蔽的房頂上張望党瓮。 院中可真熱鬧,春花似錦盐类、人聲如沸寞奸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蝇闭。三九已至,卻和暖如春硬毕,著一層夾襖步出監(jiān)牢的瞬間呻引,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工吐咳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逻悠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓韭脊,卻偏偏與公主長(zhǎng)得像童谒,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子沪羔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容