更新時間:2022-12-28 16:19:39 來源:動力節點 瀏覽1298次
現在是各個軟件開發相關企業的招人高峰期,消息隊列也是高頻發的面試題目,所以我們今天精心的準備了有關消息隊列經常提及到的面試題以及答案的梳理,希望能給正在求職的人提供幫助。
說一說生產者與消費者模式
參考答案
所謂生產者-消費者問題,實際上主要是包含了兩類線程。一種是生產者線程用于生產數據,另一種是消費者線程用于消費數據,為了解耦生產者和消費者的關系,通常會采用共享的數據區域,就像是一個倉庫。生產者生產數據之后直接放置在共享數據區中,并不需要關心消費者的行為。而消費者只需要從共享數據區中去獲取數據,就不再需要關心生產者的行為。但是,這個共享數據區域中應該具備這樣的線程間并發協作的功能:
如果共享數據區已滿的話,阻塞生產者繼續生產數據放置入內;
如果共享數據區為空的話,阻塞消費者繼續消費數據。
在Java語言中,實現生產者消費者問題時,可以采用三種方式:
使用 Object 的 wait/notify 的消息通知機制;
使用 Lock 的 Condition 的 await/signal 的消息通知機制;
使用 BlockingQueue 實現。
消息隊列如何保證順序消費?
參考答案
在生產中經常會有一些類似報表系統這樣的系統,需要做 MySQL 的 binlog 同步。比如訂單系統要同步訂單表的數據到大數據部門的 MySQL 庫中用于報表統計分析,通常的做法是基于 Canal 這樣的中間件去監聽訂單數據庫的 binlog,然后把這些 binlog 發送到 MQ 中,再由消費者從 MQ 中獲取 binlog 落地到大數據部門的 MySQL 中。
在這個過程中,可能會有對某個訂單的增刪改操作,比如有三條 binlog 執行順序是增加、修改、刪除。消費者愣是換了順序給執行成刪除、修改、增加,這樣能行嗎?肯定是不行的。不同的消息隊列產品,產生消息錯亂的原因,以及解決方案是不同的。下面我們以RabbitMQ、Kafka、RocketMQ為例,來說明保證順序消費的辦法。
RabbitMQ:
對于 RabbitMQ 來說,導致上面順序錯亂的原因通常是消費者是集群部署,不同的消費者消費到了同一訂單的不同的消息。如消費者A執行了增加,消費者B執行了修改,消費者C執行了刪除,但是消費者C執行比消費者B快,消費者B又比消費者A快,就會導致消費 binlog 執行到數據庫的時候順序錯亂,本該順序是增加、修改、刪除,變成了刪除、修改、增加。如下圖:
RabbitMQ 的問題是由于不同的消息都發送到了同一個 queue 中,多個消費者都消費同一個 queue 的消息。解決這個問題,我們可以給 RabbitMQ 創建多個 queue,每個消費者固定消費一個 queue 的消息,生產者發送消息的時候,同一個訂單號的消息發送到同一個 queue 中,由于同一個 queue 的消息是一定會保證有序的,那么同一個訂單號的消息就只會被一個消費者順序消費,從而保證了消息的順序性。如下圖:
Kafka:
對于 Kafka 來說,一個 topic 下同一個 partition 中的消息肯定是有序的,生產者在寫的時候可以指定一個 key,通過我們會用訂單號作為 key,這個 key 對應的消息都會發送到同一個 partition 中,所以消費者消費到的消息也一定是有序的。
那么為什么 Kafka 還會存在消息錯亂的問題呢?問題就出在消費者身上。通常我們消費到同一個 key 的多條消息后,會使用多線程技術去并發處理來提高消息處理速度,否則一條消息的處理需要耗時幾十 毫秒,1 秒也就只能處理幾十條消息,吞吐量就太低了。而多線程并發處理的話,binlog 執行到數據庫的時候就不一定還是原來的順序了。如下圖:
Kafka 從生產者到消費者消費消息這一整個過程其實都是可以保證有序的,導致最終亂序是由于消費者端需要使用多線程并發處理消息來提高吞吐量,比如消費者消費到了消息以后,開啟 32 個線程處理消息,每個線程線程處理消息的快慢是不一致的,所以才會導致最終消息有可能不一致。
所以對于 Kafka 的消息順序性保證,其實我們只需要保證同一個訂單號的消息只被同一個線程處理的就可以了。由此我們可以在線程處理前增加個內存隊列,每個線程只負責處理其中一個內存隊列的消息,同一個訂單號的消息發送到同一個內存隊列中即可。如下圖:
RocketMQ:
對于 RocketMQ 來說,每個 Topic 可以指定多個 MessageQueue,當我們寫入消息的時候,會把消息均勻地分發到不同的 MessageQueue 中,比如同一個訂單號的消息,增加 binlog 寫入到 MessageQueue1 中,修改 binlog 寫入到 MessageQueue2 中,刪除 binlog 寫入到 MessageQueue3 中。
但是當消費者有多臺機器的時候,會組成一個 Consumer Group,Consumer Group 中的每臺機器都會負責消費一部分 MessageQueue 的消息,所以可能消費者A消費了 MessageQueue1 的消息執行增加操作,消費者B消費了 MessageQueue2 的消息執行修改操作,消費者C消費了 MessageQueue3 的消息執行刪除操作,但是此時消費 binlog 執行到數據庫的時候就不一定是消費者A先執行了,有可能消費者C先執行刪除操作,因為幾臺消費者是并行執行,是不能夠保證他們之間的執行順序的。如下圖:
RocketMQ 的消息亂序是由于同一個訂單號的 binlog 進入了不同的 MessageQueue,進而導致一個訂單的 binlog 被不同機器上的 Consumer 處理。
要解決 RocketMQ 的亂序問題,我們只需要想辦法讓同一個訂單的 binlog 進入到同一個 MessageQueue 中就可以了。因為同一個 MessageQueue 內的消息是一定有序的,一個 MessageQueue 中的消息只能交給一個 Consumer 來進行處理,所以 Consumer 消費的時候就一定會是有序的。
以上就是“精心準備了高頻出現的消息隊列面試題”,你能回答上來嗎?如果想要了解更多的Java面試題相關內容,可以關注動力節點Java官網。
0基礎 0學費 15天面授
有基礎 直達就業
業余時間 高薪轉行
工作1~3年,加薪神器
工作3~5年,晉升架構
提交申請后,顧問老師會電話與您溝通安排學習