Spring Boot與消息(JMS、AMQP、RabbitMQ)

1.概述。

  • 大多應用中,可通過消息服務中間件來提升系統異步通信、擴展解耦能力
  • 消息服務中兩個重要概念: 消息代理(message broker)和目的地(destination)。 當消息發送者發送消息以後,將由消息代理接管,消息代理保證消息傳遞到指定目的地。
  • 消息隊列主要有兩種形式的目的地 :
    • 隊列(queue):點對點消息通信(point-to-point)
    • 主題(topic):發佈(publish)/訂閱(subscribe)消息通信
  • 點對點式: 消息發送者發送消息,消息代理將其放入一個隊列中,消息接收者從隊列中獲取消息內容,消息讀取後被移出隊列 消息只有唯一的發送者和接受者,但並不是說只能有一個接收者
  • 發佈訂閱式: 發送者(發佈者)發送消息到主題,多個接收者(訂閱者)監聽(訂閱)這個主題,那麼就會在消息到達時同時收到消息
  • JMS(Java Message Service)JAVA消息服務: 基於JVM消息代理的規範。ActiveMQ、HornetMQ是JMS實現
  • AMQP(Advanced Message Queuing Protocol): 高級消息隊列協議,也是一個消息代理的規範,兼容JMS RabbitMQ是AMQP的實現
  • Spring支持
    • spring-jms提供了對JMS的支持
    • spring-rabbit提供了對AMQP的支持
    • 需要ConnectionFactory的實現來連接消息代理
    • 提供JmsTemplate、RabbitTemplate來發送消息
    • @JmsListener(JMS)、@RabbitListener(AMQP)註解在方法上監聽消息代理髮布的消息
    • @EnableJms、@EnableRabbit開啓支持
  • Spring Boot自動配置
    • JmsAutoConfiguration
    • RabbitAutoConfiguration

2.應用場景 。

  • 異步處理
    • 第一種情況屬於同步處理。用戶發起註冊請求,服務器將數據寫入到數據庫用50ms,再發送郵件用50ms,再發送短信用50ms,這樣用戶等待時間就是150ms。
    • 第二種情況屬於併發操作。用戶發起註冊請求,服務器將數據寫入到數據庫用50ms,再用多線程的方式同時執行發送郵件和發送短信使用50ms,這樣用戶等待時間就是100ms。
    • 第三種情況屬於消息處理。用戶發送註冊請求,服務器將數據寫入到數據庫用了50ms,將註冊的信息寫入到消息隊列用了5ms左右,然後就可以響應給用戶已經註冊成功。後面的操作就是,監聽消息隊列(如果隊列有消息),獲取註冊的信息,發送郵件和發送短信給用戶即可。

 

 

 

 

 

3.RabbitMQ簡介

  • RabbitMQ簡介:
    • RabbitMQ是一個由erlang開發的AMQP(Advanved Message Queue Protocol)的開源實現。
  • 核心概念
    • Message
      • 消息,消息是不具名的,它由消息頭和消息體組成。消息體是不透明的,而消息頭則由一系列的可選屬性組成,這些屬性包括routing-key(路由鍵)、priority(相對於其他消息的優先權)、delivery-mode(指出該消息可能需要持久性存儲)等。
    • Publisher
      • 消息的生產者,也是一個向交換器發佈消息的客戶端應用程序。
    • Exchange
      • 交換器,用來接收生產者發送的消息並將這些消息路由給服務器中的隊列。
      • Exchange有4種類型:
        • direct(默認,點對點),fanout(發佈訂閱模式),  topic(匹配路由鍵routing key的發佈訂閱模式), 和headers(匹配 AMQP 消息的 header,和direct差不多,但是性能很差,幾乎不用了),不同類型的Exchange轉發消息的策略有所區別。
    • Queue
      • 消息隊列,用來保存消息直到發送給消費者。它是消息的容器,也是消息的終點。一個消息可投入一個或多個隊列。消息一直在隊列裏面,等待消費者連接到這個隊列將其取走。
    • Binding
      • 綁定,用於消息隊列和交換器之間的關聯。一個綁定就是基於路由鍵將交換器和消息隊列連接起來的路由規則,所以可以將交換器理解成一個由綁定構成的路由表。 Exchange 和Queue的綁定可以是多對多的關係。
    • Connection
      • 網絡連接,比如一個TCP連接。
    • Channel
      • 信道,多路複用連接中的一條獨立的雙向數據流通道。信道是建立在真實的TCP連接內的虛擬連接,AMQP 命令都是通過信道發出去的,不管是發佈消息、訂閱隊列還是接收消息,這些動作都是通過信道完成。因爲對於操作系統來說建立和銷燬 TCP 都是非常昂貴的開銷,所以引入了信道的概念,以複用一條 TCP 連接。
    • Consumer
      • 消息的消費者,表示一個從消息隊列中取得消息的客戶端應用程序。
    • Virtual Host
      • 虛擬主機,表示一批交換器、消息隊列和相關對象。虛擬主機是共享相同的身份認證和加密環境的獨立服務器域。每個 vhost 本質上就是一個 mini 版的 RabbitMQ 服務器,擁有自己的隊列、交換器、綁定和權限機制。vhost 是 AMQP 概念的基礎,必須在連接時指定,RabbitMQ 默認的 vhost 是 / 。
    • Broker
      • 表示消息隊列服務器實體

4.RabbitMQ整合

  • 引入 spring-boot-starter-amqp
 <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency
  • application.yml配置 
spring.rabbitmq.host=localhost
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.port=5672
#spring.rabbitmq.virtual-host=
  • 測試RabbitMQ
    • AmqpAdmin:管理組件
    • RabbitTemplate:消息發送處理組件
   @Autowired
   RabbitTemplate rabbitTemplate;

   @Autowired
   AmqpAdmin amqpAdmin;

   @Test
   public void createExchange(){
      //創建Exchange(direct:點對點模式)
      //amqpAdmin.declareExchange(new DirectExchange("amqpadmin.exchange"));

      //創建Queue隊列,並指定routing-key路由鍵和Durable是否持久化
      //amqpAdmin.declareQueue(new Queue("amqpadmin.queue", true));
      
      //創建綁定規則
      //amqpAdmin.declareBinding(new Binding("amqpadmin.queue",Binding.DestinationType.QUEUE,"amqpadmin.exchange","amqp.haha",null));
   }

   /**
    * 1、單播(點對點)
    */
   @Test
   public void contextLoads() {
      //Message需要自己構造一個;定義消息體內容和消息頭
      //rabbitTemplate.send(exchage, routeKey, message);
      //object默認當成消息體,只需要傳入要發送的對象,自動序列化發送給rabbitmq;
      //rabbitTemplate.convertAndSend(exchage, routeKey, object);
      Map<String,Object> map = new HashMap<>();
      map.put("msg","這是第一個消息");
      map.put("data", Arrays.asList("helloworld",123,true));
      //對象被默認序列化以後發送出去
      rabbitTemplate.convertAndSend("exchange.direct", "llsydn.news", map);
   }

   //接受數據,如何將數據自動的轉爲json發送出去
   @Test
   public void receive(){
      Object o = rabbitTemplate.receiveAndConvert("llsydn.news");
      System.out.println(o.getClass());
      System.out.println(o);
   }

   /**
    * 廣播
    */
   @Test
   public void sendMsg(){
      Map<String,Object> map = new HashMap<>(); 
      map.put("msg", "這是第一個消息"); 
      map.put("data", Arrays.asList("helloworld", 123, true));
      rabbitTemplate.convertAndSend("exchange.fanout", "", map);
   }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章