一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 基于RocketMQ推拉模式詳解

基于RocketMQ推拉模式詳解

2021-09-25 01:08mingxungu Java教程

這篇文章主要介紹了RocketMQ推拉模式的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

消費者客戶端有兩種方式從消息中間件獲取消息并消費。嚴格意義上來講,RocketMQ并沒有實現(xiàn)PUSH模式,而是對拉模式進行一層包裝,名字雖然是 Push 開頭,實際在實現(xiàn)時,使用 Pull 方式實現(xiàn)。

通過 Pull 不斷輪詢 Broker 獲取消息。當不存在新消息時,Broker 會掛起請求,直到有新消息產生,取消掛起,返回新消息。

1、概述

1.1、PULL方式

由消費者客戶端主動向消息中間件(MQ消息服務器代理)拉取消息;采用Pull方式,如何設置Pull消息的拉取頻率需要重點去考慮,舉個例子來說,可能1分鐘內連續(xù)來了1000條消息,然后2小時內沒有新消息產生(概括起來說就是“消息延遲與忙等待”)。

如果每次Pull的時間間隔比較久,會增加消息的延遲,即消息到達消費者的時間加長,MQ中消息的堆積量變大;若每次Pull的時間間隔較短,但是在一段時間內MQ中并沒有任何消息可以消費,那么會產生很多無效的Pull請求的RPC開銷,影響MQ整體的網絡性能;

1.2、PUSH方式

由消息中間件(MQ消息服務器代理)主動地將消息推送給消費者;采用Push方式,可以盡可能實時地將消息發(fā)送給消費者進行消費。

但是,在消費者的處理消息的能力較弱的時候(比如,消費者端的業(yè)務系統(tǒng)處理一條消息的流程比較復雜,其中的調用鏈路比較多導致消費時間比較久。

概括起來地說就是“慢消費問題”),而MQ不斷地向消費者Push消息,消費者端的緩沖區(qū)可能會溢出,導致異常;

2、PUSH模式

主動推送的模式實現(xiàn)起來簡單,避免了拉取的消費端業(yè)務邏輯的復雜度,消息的消費可以認為是實時的,同時也存在一定的弊端,要求消費端要有很強的消費能力。

2.1、代碼實現(xiàn)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class Consumer1 {   
    public static void main(String[] args){
        try {
            DefaultMQPushConsumer consumer = new DefaultMQPushConsumer();
            consumer.setConsumerGroup("consumer_push");
            consumer.setNamesrvAddr("10.10.12.203:9876;10.10.12.204:9876");
            consumer.subscribe("TopicTest", "*");
            consumer.registerMessageListener(new MessageListenerConcurrently(){
 
                @Override
                public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> paramList,
                        ConsumeConcurrentlyContext paramConsumeConcurrentlyContext) {
                    try {
                        for(MessageExt msg : paramList){
                            String msgbody = new String(msg.getBody(), "utf-8");
                            SimpleDateFormat sd = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
                            Date date = new Date(msg.getStoreTimestamp());
                            System.out.println("Consumer1===  存入時間 :  "+ sd.format(date) +" == MessageBody: "+ msgbody);//輸出消息內容
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                        return ConsumeConcurrentlyStatus.RECONSUME_LATER; //稍后再試
                    }
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; //消費成功
                }
            });
            consumer.start();
            System.out.println("Consumer1===啟動成功!");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

PUSH消費方式,需要注冊一個監(jiān)聽器Listener,,用來監(jiān)聽最新的消息,進行業(yè)務處理,同時反饋消息的消費狀態(tài),消費成功(CONSUME_SUCCESS)、消費重試(RECONSUME_LATER),消息重試會根據(jù)配置的消息的延遲等級的時間間隔,定時重新發(fā)送消費失敗的記錄。(PS:延遲消息中會重點討論)

PUSH消息方式由于返回了消息的狀態(tài),服務端會維護每個消費端的消費進度,內部會記錄消費進度,消息發(fā)送成功后會更新消費進度。

PUSH消息方式的局限性,是在HOLD住Consumer請求的時候需要占用資源,它適合用在消息隊列這種客戶端連接數(shù)可控的場景中。

上一個章節(jié)說明了服務端存儲的每個主題對應的消費組的每個消息隊列的偏移量

查看服務器文件上的消費進度信息:

/usr/local/rocketmq-all-4.2.0/store/config/consumerOffset.json

基于RocketMQ推拉模式詳解

3、PULL模式

3.1、代碼實現(xiàn)(1)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class PullConsumer {
    private static final Map<MessageQueue, Long> offseTable = new HashMap<MessageQueue, Long>();
 
    public static void main(String[] args) throws MQClientException {
        DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("pullConsumer");
        consumer.setNamesrvAddr("10.10.12.203:9876;10.10.12.204:9876");
        consumer.start();
 
        Set<MessageQueue> mqs = consumer.fetchSubscribeMessageQueues("TopicTest");
        for (MessageQueue mq : mqs) {
            
            SINGLE_MQ: while (true) {
                try {
                    PullResult pullResult =
                            consumer.pullBlockIfNotFound(mq, null, getMessageQueueOffset(mq), 32);
                    System.out.println("=============================================================");
                    System.out.println("Consume from the queue: " + mq + "offset:" + getMessageQueueOffset(mq) + "結果:" + pullResult.getPullStatus());
                    putMessageQueueOffset(mq, pullResult.getNextBeginOffset());
                    switch (pullResult.getPullStatus()) {
                    case FOUND:
                        List<MessageExt> messageExtList = pullResult.getMsgFoundList();
                        for (MessageExt m : messageExtList) {
                            System.out.print(new String(m.getBody()) +" == ");
                        }
                        System.out.println("");
                    case NO_MATCHED_MSG:
                        break;
                    case NO_NEW_MSG:
                        break SINGLE_MQ;
                    case OFFSET_ILLEGAL:
                        break;
                    default:
                        break;
                    }
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        consumer.shutdown();
    }
 
    private static void putMessageQueueOffset(MessageQueue mq, long offset) {
        offseTable.put(mq, offset);
    }
 
    private static long getMessageQueueOffset(MessageQueue mq) {
        Long offset = offseTable.get(mq);
        if (offset != null)
            return offset;
        return 0;
    }
}

結果:

基于RocketMQ推拉模式詳解

每次拉取消息的時候需要提供偏移量和拉取的消息的個數(shù),需要自己業(yè)務實現(xiàn)每個主題下的隊列的消費進度。

代碼實現(xiàn)(1)這種方式只能拉取歷史的消息,最新的消息拉取不了,也可以進行改造,來實現(xiàn)一直拉取。

3.2、代碼實現(xiàn)(2)

在MQPullConsumer這個類里面,有一個MessageQueueListener,它的目的就是當queue發(fā)生變化的時候,通知Consumer。也正是這個借口,幫助我們在Pull模式里面,實現(xiàn)負載均衡。

注意,這個接口在MQPushConsumer里面是沒有的,那里面有的是上面代碼里的MessageListener。

?
1
2
3
4
5
void registerMessageQueueListener(final String topic, final MessageQueueListener listener);
public interface MessageQueueListener {
    void messageQueueChanged(final String topic, final Set<MessageQueue> mqAll,
                             final Set<MessageQueue> mqDivided);
}

有了這個Listener,我們就可以動態(tài)的知道當前的Consumer分攤到了幾個MessageQueue。然后對這些MessageQueue,我們可以開個線程池來消費。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class PullConsumerExtend {
    public static void main(String[] args) throws MQClientException {
           //消費組
            final MQPullConsumerScheduleService scheduleService = new MQPullConsumerScheduleService("pullConsumer");
           //MQ NameService地址
            scheduleService.getDefaultMQPullConsumer().setNamesrvAddr("10.10.12.203:9876;10.10.12.204:9876");
           //負載均衡模式
            scheduleService.setMessageModel(MessageModel.CLUSTERING);
           //需要處理的消息topic
            scheduleService.registerPullTaskCallback("TopicTest", new PullTaskCallback() {
 
                @Override
                public void doPullTask(MessageQueue mq, PullTaskContext context) {
                    MQPullConsumer consumer = context.getPullConsumer();
                    try {
                        
                        long offset = consumer.fetchConsumeOffset(mq, false);
                        if (offset < 0)
                            offset = 0;
                        PullResult pullResult = consumer.pull(mq, "*", offset, 32);
                        System.out.println("");
                        System.out.println("Consume from the queue: " + mq + "offset:" + offset + "結果:" + pullResult.getPullStatus());
                        switch (pullResult.getPullStatus()) {
                            case FOUND:
                                List<MessageExt> messageExtList = pullResult.getMsgFoundList();
                                for (MessageExt m : messageExtList) {
                                    System.out.print(new String(m.getBody()) +" == ");
                                }
                                break;
                            case NO_MATCHED_MSG:
                                break;
                            case NO_NEW_MSG:
                            case OFFSET_ILLEGAL:
                                break;
                            default:
                                break;
                        }
                        consumer.updateConsumeOffset(mq, pullResult.getNextBeginOffset());
                        //設置下一下拉取的間隔時間
                        context.setPullNextDelayTimeMillis(10000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            scheduleService.start();
    }
}

結果:

基于RocketMQ推拉模式詳解

比較**代碼實現(xiàn)(1)**這種方式改進了很多,不需要業(yè)務維護每個消費隊列的消費進度,可以更新到服務端的。

弊端也很明顯就是每次隊列拉取消息的時間間隔,時間長導致消息擠壓,時間段消息少,影響服務端性能。

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://my.oschina.net/mingxungu/blog/3083956

延伸 · 閱讀

精彩推薦
  • Java教程Java實現(xiàn)搶紅包功能

    Java實現(xiàn)搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現(xiàn)搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發(fā)現(xiàn)好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發(fā)項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩(wěn)中求8032021-07-12
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發(fā)現(xiàn)了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7472021-02-04
  • Java教程Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決

    Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數(shù)據(jù)的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
主站蜘蛛池模板: 精品久久久久久久高清 | 国内体内she精视频免费 | 无套大战白嫩乌克兰美女 | 性夜影院爽黄A爽免费动漫 性色欲情网站IWWW九文堂 | 精选国产AV精选一区二区三区 | 天堂樱桃bt在线www | a男人天堂 | 亚洲国产AV无码综合在线 | 国产精品女主播大秀在线 | 91次元成年破解版 | 国产日本免费 | 特a级片| 韩国三级hd中文字幕李采潭 | 日韩毛片在线视频 | 精品视频 久久久 | 丝瓜黄瓜茄子西红柿秋葵榴莲 | 亚洲天天做夜夜做天天欢 | 日韩精品久久不卡中文字幕 | 厨房高h| 青青草原社区 | 男人猛进女人屁股免费 | 无套日出白浆在线播放 | 日韩一区二区三区不卡视频 | 国产趴着打光屁股sp抽打 | 97精品国产自在现线免费 | 国产极品麻豆91在线 | 日韩手机在线观看 | 亚洲精品97福利在线 | 亚洲品质自拍视频网站 | 2023最新伦理片 | 亚洲精品一线二线三线 | 亚洲国产精品久久精品成人网站 | 99久久免费国内精品 | 日韩二三区 | α片免费| 欧美日韩国产亚洲一区二区 | 国产91精品露脸国语对白 | 精品久久一 | 十大看黄网站 | 污小说h| 175m美女被网友灌醉啪啪玩脚 |