如何實現一個簡單易用的 RocketMQ SDK

勇哥编程游记發表於2024-05-06

2018 年,做為架構負責人,接到一個架構需求:實現一個簡單易用的 RocketMQ SDK 。

因為各個團隊 RocketMQ 原生客戶端配置起來千奇百怪,有的配置存在風險,各團隊負責人都需要一個簡潔易用的 RocketMQ SDK 。

我立馬調研相關開源的方案,當時 RocketMQ-Spring 專案並沒有開源,而阿里雲的 ONS SDK 是開源的,我只能講目標轉向 阿里雲 ONS 。

透過學習 ONS 的設計方式,我對於 RocketMQ 的客戶端原理有了進一步瞭解,同時參考 ONS 的設計,也實現了公司內部使用的 RocketMQ SDK 。

專案地址:https://github.com/makemyownlife/platform-rocketmq

之所以說簡單,就是讓使用者(開發者)使用 SDK 時,減少心智負擔

舉三個例子:

1 傳送順序訊息

使用原生程式碼傳送訊息時,會使用如下的程式碼:

SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
    @Override
    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
        Integer id = (Integer) arg;
        int index = id % mqs.size();
        return mqs.get(index);
    }
}, orderId);

我們可以將 SDK API 簡化為:

SendResult send(final ProducerMessage message, final String shardingKey);

開發者不需要定義佇列選擇器,只需要傳遞分片鍵 orderId 即可。

2 單條訊息消費

使用原來程式碼定義消費監聽器時,使用如下的程式碼:

consumer.registerMessageListener(new MessageListenerConcurrently() {
    @Override
    public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
        System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
        // 返回訊息消費狀態,ConsumeConcurrentlyStatus.CONSUME_SUCCESS為消費成功
        return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
    }
});

監聽器內部,對於開發者操作的物件是訊息列表 msgs ,很多開發同學想只操作一條訊息。

於是,我們可以將 SDK API 簡化為:

consumer.subscribe("mytest", new ConsumerListener() {
    @Override
    public ConsumerAction consumer(ConsumerMessage msg) {
        byte[] body = msg.getBody();
        System.out.println("msg:" + new String(body));
        return ConsumerAction.CommitMessage;
    }
});

開發者在消費時,可以一條一條操作,程式碼簡潔了不少。

同時,很多開發者在使用普通消費、順序消費時,需要返回延時消費的狀態碼時,兩種消費模式定義的列舉也不相同。我們將列舉做了統一:

/**
 * 消費訊息的返回結果
 */
public enum ConsumerAction {

    /**
     * 消費成功,繼續消費下一條訊息
     */
    CommitMessage,
    
    /**
     * 消費失敗,告知伺服器稍後再投遞這條訊息,繼續消費其他訊息
     */
    ReconsumeLater;
}

3 訂閱關係一致

實際場景裡,訂閱關係不一致是極容易發生的事情,就算是高階別的架構師也會翻車,每次翻車現場都是慘不忍睹。

正確的訂閱關係見下圖:

正確的訂閱關係

程式碼邏輯角度來看,每個消費者例項內訂閱方法的主題、 TAG、監聽邏輯都需要保持一致

當訂閱關係不一致時,在 Broker 端同一個消費組內的各個消費者客戶端的訂閱資訊相互被覆蓋,從而導致某個消費者客戶端無法拉取到新的訊息。

怎麼解決呢 ?

我當時想起了阿里技術專家沈詢的一句話:

世界上解決一個計算機問題最簡單的方法:“恰好”不需要解決它 !

公司內部出現訂閱關係一致99%的問題是:消費者組一致的前提下,主題相同,但 TAG 不相同。

基於此,我的設計思路就明確了:不開放訂閱 TAG 的許可權!

沒想到吧,我就是這麼粗暴。

按照這種設計思路,雖然開始有的程式設計師會有質疑,但你和他梳理好消費者組的定義,以及做好領域劃分,對業務來講,反而清晰了。

4 寫到最後

我並不認為我們寫得多麼的好,只是想讓同學們理解:

1、成長的第一步就是模仿

2、把開發者當使用者,以使用者的體驗為先

我經常去閱讀阿里雲產品的 SDK , 去思考他們為什麼這麼設計, 為什麼和開源的有所不同,有的時候開始沒想明白,但模仿得多了,好像也慢慢懂了。

要緊的是果敢地邁出第一步,對與錯先都不管,自古就沒有把一切都設計好再開步的事。

別想把一切都弄清楚,再去走路。魯莽者要學會思考,善思者要克服的是猶豫。

——史鐵生


如果我的文章對你有所幫助,還請幫忙點贊、在看、轉發一下,你的支援會激勵我輸出更高質量的文章,非常感謝!

相關文章