Java筆試面試-消息隊列面試題總結

1.消息隊列的應用場景有哪些?

答:消息隊列的應用場景如下。

  • 應用解耦,比如,用戶下單後,訂單系統需要通知庫存系統,假如庫存系統無法訪問,則訂單減庫存將失敗,從而導致訂單失敗。訂單系統與庫存系統耦合,這個時候如果使用消息隊列,可以返回給用戶成功,先把消息持久化,等庫存系統恢復後,就可以正常消費減去庫存了。
  • 削峯填谷,比如,秒殺活動,一般會因爲流量過大,從而導致流量暴增,應用掛掉,這個時候加上消息隊列,服務器接收到用戶的請求後,首先寫入消息隊列,假如消息隊列長度超過最大數量,則直接拋棄用戶請求或跳轉到錯誤頁面。
  • 日誌系統,比如,客戶端負責將日誌採集,然後定時寫入消息隊列,消息隊列再統一將日誌數據存儲和轉發。

2.RabbitMQ 有哪些優點?

答:RabbitMQ 的優點如下:

  • 可靠性,RabbitMQ 的持久化支持,保證了消息的穩定性;
  • 高併發,RabbitMQ 使用了 Erlang 開發語言,Erlang 是爲電話交換機開發的語言,天生自帶高併發光環和高可用特性;
  • 集羣部署簡單,正是因爲 Erlang 使得 RabbitMQ 集羣部署變的非常簡單;
  • 社區活躍度高,因爲 RabbitMQ 應用比較廣泛,所以社區的活躍度也很高;
  • 解決問題成本低,因爲資料比較多,所以解決問題的成本也很低;
  • 支持多種語言,主流的編程語言都支持,如 Java、.NET、PHP、Python、JavaScript、Ruby、Go 等;
  • 插件多方便使用,如網頁控制檯消息管理插件、消息延遲插件等。

3.RabbitMQ 有哪些重要的角色?

答:RabbitMQ 包含以下三個重要的角色:

  • 生產者:消息的創建者,負責創建和推送數據到消息服務器;
  • 消費者:消息的接收方,用於處理數據和確認消息;
  • 代理:就是 RabbitMQ 本身,用於扮演“快遞”的角色,本身不生產消息,只是扮演“快遞”的角色。

4.RabbitMQ 有哪些重要的組件?它們有什麼作用?

答:RabbitMQ 包含的重要組件有:ConnectionFactory(連接管理器)、Channel(信道)、Exchange(交換器)、Queue(隊列)、RoutingKey(路由鍵)、BindingKey(綁定鍵) 等重要的組件,它們的作用如下:

  • ConnectionFactory(連接管理器):應用程序與 RabbitMQ 之間建立連接的管理器,程序代碼中使用;
  • Channel(信道):消息推送使用的通道;
  • Exchange(交換器):用於接受、分配消息;
  • Queue(隊列):用於存儲生產者的消息;
  • RoutingKey(路由鍵):用於把生成者的數據分配到交換器上;
  • BindingKey(綁定鍵):用於把交換器的消息綁定到隊列上。

運行流程,如下圖所示:
在這裏插入圖片描述

5.什麼是消息持久化?

答:消息持久化是把消息保存到物理介質上,以防止消息的丟失。

6.RabbitMQ 要實現消息持久化,需要滿足哪些條件?

答:RabbitMQ 要實現消息持久化,必須滿足以下 4 個條件:

  • 投遞消息的時候 durable 設置爲 true,消息持久化,代碼:channel.queueDeclare(x, true, false, false, null),參數 2 設置爲 true 持久化;
  • 設置投遞模式 deliveryMode 設置爲 2(持久),代碼:channel.basicPublish(x, x, MessageProperties.PERSISTENTTEXTPLAIN,x),參數 3 設置爲存儲純文本到磁盤;
  • 消息已經到達持久化交換器上;
  • 消息已經到達持久化的隊列。

7.消息持久化有哪些缺點?如何緩解?

答:消息持久化的缺點是很消耗性能,因爲要寫入硬盤要比寫入內存性能較低很多,從而降低了服務器的吞吐量。可使用固態硬盤來提高讀寫速度,以達到緩解消息持久化的缺點。

8.如何使用 Java 代碼連接 RabbitMQ?

答:使用 Java 代碼連接 RabbitMQ 有以下兩種方式:

方式一:

public static Connection GetRabbitConnection() {
    ConnectionFactory factory = new ConnectionFactory();
    factory.setUsername(Config.UserName);
    factory.setPassword(Config.Password);
    factory.setVirtualHost(Config.VHost);
    factory.setHost(Config.Host);
    factory.setPort(Config.Port);
    Connection conn = null;
    try {
        conn = factory.newConnection();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return conn;
}

方式二:

public static Connection GetRabbitConnection2() {
    ConnectionFactory factory = new ConnectionFactory();
    // 連接格式:amqp://userName:password@hostName:portNumber/virtualHost
    String uri = String.format("amqp://%s:%s@%s:%d%s", Config.UserName, Config.Password, Config.Host, Config.Port,
            Config.VHost);
    Connection conn = null;
    try {
        factory.setUri(uri);
        factory.setVirtualHost(Config.VHost);
        conn = factory.newConnection();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return conn;
}

9.使用 Java 代碼編寫一個 RabbitMQ 消費和生產的示例?

答:代碼如下:

public static void main(String[] args) {
    publisher();     // 生產消息
    consumer();     // 消費消息
}

/**
 * 推送消息
 */
public static void publisher() {
    // 創建一個連接
    Connection conn = ConnectionFactoryUtil.GetRabbitConnection();
    if (conn != null) {
        try {
            // 創建通道
            Channel channel = conn.createChannel();
            // 聲明隊列【參數說明:參數一:隊列名稱,參數二:是否持久化;參數三:是否獨佔模式;參數四:消費者斷開連接時是否刪除隊列;參數五:消息其他參數】
            channel.queueDeclare(Config.QueueName, false, false, false, null);
            String content = String.format("當前時間:%s", new Date().getTime());
            // 發送內容【參數說明:參數一:交換機名稱;參數二:隊列名稱,參數三:消息的其他屬性-routing headers,此屬性爲MessageProperties.PERSISTENT_TEXT_PLAIN用於設置純文本消息存儲到硬盤;參數四:消息主體】
            channel.basicPublish("", Config.QueueName, null, content.getBytes("UTF-8"));
            System.out.println("已發送消息:" + content);
            // 關閉連接
            channel.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/**
 * 消費消息
 */
public static void consumer() {
    // 創建一個連接
    Connection conn = ConnectionFactoryUtil.GetRabbitConnection();
    if (conn != null) {
        try {
            // 創建通道
            Channel channel = conn.createChannel();
            // 聲明隊列【參數說明:參數一:隊列名稱,參數二:是否持久化;參數三:是否獨佔模式;參數四:消費者斷開連接時是否刪除隊列;參數五:消息其他參數】
            channel.queueDeclare(Config.QueueName, false, false, false, null);

            // 創建訂閱器,並接受消息
            channel.basicConsume(Config.QueueName, false, "", new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
                        byte[] body) throws IOException {
                    String routingKey = envelope.getRoutingKey(); // 隊列名稱
                    String contentType = properties.getContentType(); // 內容類型
                    String content = new String(body, "utf-8"); // 消息正文
                    System.out.println("消息正文:" + content);
                    channel.basicAck(envelope.getDeliveryTag(), false); // 手動確認消息【參數說明:參數一:該消息的index;參數二:是否批量應答,true批量確認小於index的消息】
                }
            });

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

10.RabbitMQ 交換器類型有哪些?

答:RabbitMQ 消費類型也就是交換器(Exchange)類型有以下四種:

  • direct:輪詢方式
  • headers:輪詢方式,允許使用 header 而非路由鍵匹配消息,性能差,幾乎不用
  • fanout:廣播方式,發送給所有訂閱者
  • topic:匹配模式,允許使用正則表達式匹配消息

RabbitMQ 默認的是 direct 方式。

11.RabbitMQ 如何確保每個消息能被消費?

答:RabbitMQ 使用 ack 消息確認的方式保證每個消息都能被消費,開發者可根據自己的實際業務,選擇 channel.basicAck() 方法手動確認消息被消費。

12.RabbitMQ 接收到消息之後必須消費嗎?

答:RabbitMQ 接收到消息之後可以不消費,在消息確認消費之前,可以做以下兩件事:

  • 拒絕消息消費,使用 channel.basicReject(消息編號, true) 方法,消息會被分配給其他訂閱者;
  • 設置爲死信隊列,死信隊列是用於專門存放被拒絕的消息隊列。

13.topic 模式下發布了一個路由鍵爲“com.mq.rabbit.error”的消息,請問以下不能接收到消息的是?

A:cn.mq.rabbit.*
B:#.error
C:cn.mq.*
D:cn.mq.#

答:C

題目解析:“*”用於匹配一個分段(用“.”分割)的內容,“#”用於匹配 0 和多個字符。

14.以下可以獲取歷史消息的是?

A:topic 交換器
B:fanout 交換器
C:direct 交換器
D:以上都不是

答:C

題目解析:fanout 和 topic 都是廣播形式的,因此無法獲取歷史消息,而 direct 可以。

15.RabbitMQ 包含事務功能嗎?如何使用?

答:RabbitMQ 包含事務功能,主要是對信道(Channel)的設置,主要方法有以下三個:

  • channel.txSelect() 聲明啓動事務模式;
  • channel.txComment() 提交事務;
  • channel.txRollback() 回滾事務。

16.RabbitMQ 的事務在什麼情況下是無效的?

答:RabbitMQ 的事務在 autoAck=true 也就是自動消費確認的時候,事務是無效的。因爲如果是自動消費確認,RabbitMQ 會直接把消息從隊列中移除,即使後面事務回滾也不能起到任何作用。

17.Kafka 可以脫離 ZooKeeper 單獨使用嗎?

答:Kafka 不能脫離 ZooKeeper 單獨使用,因爲 Kafka 使用 ZooKeeper 管理和協調 Kafka 的節點服務器。

18.Kafka 有幾種數據保留的策略?

答:Kafka 有兩種數據保存策略:按照過期時間保留和按照存儲的消息大小保留。

19.Kafka 同時設置了 7 天和 10G 清除數據,到第五天的時候消息達到了 10G,這個時候 Kafka 將如何處理?

答:這個時候 Kafka 會執行數據清除工作,時間和大小不論哪個滿足條件,都會清空數據。

20.什麼情況會導致 Kafka 運行變慢?

答:以下情況可導致 Kafka 運行變慢:

  • CPU 性能瓶頸
  • 磁盤讀寫瓶頸
  • 網絡瓶頸

21.使用 Kafka 集羣需要注意什麼?

答:Kafka 集羣使用需要注意以下事項:

  • 集羣的數量不是越多越好,最好不要超過 7 個,因爲節點越多,消息複製需要的時間就越長,整個羣組的吞吐量就越低;
  • 集羣數量最好是單數,因爲超過一半故障集羣就不能用了,設置爲單數容錯率更高。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章