寫在前面:
1.消息中間件的發展:
第一代以ActiveMQ爲代表,遵循JMS(java消息服務)規範 第二代以RabbitMQ爲代表是一個有Erlang語言開發的AMQP(高級消息隊列協議)的開源實現 第三代以kafka爲代表,是一代高吞吐、高可用的消息中間件,以及RocketMQ
RocketMQ的特點:
1.RocketMQ 是一款分佈式、隊列模型的消息中間件,具有以下特點:
2.能夠保證嚴格的消息順序
3.提供豐富的消息拉取模式
4.高效的訂閱者水平擴展能力
5.實時的消息訂閱機制
6.億級消息堆積能力
7.分佈式高可用的部署架構,滿足至少一次消息傳遞語義
8.提供 docker 鏡像用於隔離測試和雲集羣部署
9.提供配置、指標和監控等功能豐富的 Dashboard
選用理由:
a.強調集羣無單點,可擴展,任意一點高可用,水平可擴展。
b.海量消息堆積能力,消息堆積後,寫入低延遲。
c.支持上萬個隊列
d.消息失敗重試機制
e.消息可查詢
f.開源社區活躍
g.成熟度(經過雙十一考驗)
RocketMQ物理部署結構:
rocketMQ幾個概念:
producer:消息生產者,生產者的作用就是將消息發送到 MQ,生產者本身既可以產生消息,如讀取文本信息等。也可以對外提供接口,由外部應用來調用接口,再由生產者將收到的消息發送到 MQ
producer group: 生產者組,簡單來說就是多個發送同一類消息的生產者稱之爲一個生產者組。在這裏可以不用關心,只要知道有這麼一個概念即可,Producer實例可以是多機器、單機器多進程、單進程中的多對象。Producer可以發送多個Topic。處理分佈式事務時,也需要Producer集羣提高可靠性
consumer : 消息消費者,簡單來說,消費 MQ 上的消息的應用程序就是消費者,至於消息是否進行邏輯處理,還是直接存儲到數據庫等取決於業務需要
consumer group : 消費者組,和生產者類似,消費同一類消息的多個 consumer 實例組成一個消費者組.Consumer實例 的集合。Consumer 實例可以是多機器、但機器多進程、單進程中的多對象。同一個Group中的實例,在集羣模式下,以均攤的方式消費;在廣播模式下,每個實例都全部消費。
Topic : Topic 是一種消息的邏輯分類,比如說你有訂單類的消息,也有庫存類的消息,那麼就需要進行分類,一個是訂單 Topic 存放訂單相關的消息,一個是庫存 Topic 存儲庫存相關的消息
Message : Message 是消息的載體。一個 Message 必須指定 topic,相當於寄信的地址。Message 還有一個可選的 tag 設置,以便消費端可以基於 tag 進行過濾消息。也可以添加額外的鍵值對,例如你需要一個業務 key 來查找 broker 上的消息,方便在開發過程中診斷問題
Tag : 標籤可以被認爲是對 Topic 進一步細化。一般在相同業務模塊中通過引入標籤來標記不同用途的消息
Broker : Broker 是 RocketMQ 系統的主要角色,其實就是前面一直說的 MQ。Broker 接收來自生產者的消息,儲存以及爲消費者拉取消息的請求做好準備
Name Server : Name Server 爲 producer 和 consumer 提供路由信息
Push Consumer : 應用通常向Consumer對象註冊一個Listener接口,一旦收到消息,Consumer對象立刻回調Listener接口方法。所以,所謂Push指的是客戶端內部的回調機制,並不是與服務端之間的機制
Pull Consumer : 應用通常主動調用Consumer從服務端拉消息,然後處理。這用的就是短輪詢方式了,在不同情況下,與長輪詢各有優點
1,linux安裝
官網介紹很詳細:
http://rocketmq.apache.org/docs/quick-start/
2,本地demo
2.1 pom.xml
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.3.0</version>
</dependency>
2.2 producer
2.2.1 同步調用:
package org.equaker.cache;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
/**
* 同步發送消息-RocketMQ
* @author EQuaker
*
*/
public class SyncProducer_RocketMQ {
public static void main(String[] args) throws Exception {
//Instantiate with a producer group name.
DefaultMQProducer producer = new
DefaultMQProducer("group1");
// Specify name server addresses.
producer.setNamesrvAddr("外網ip:9876");
//Launch the instance.
producer.start();
for (int i = 0; i < 10; i++) {
//Create a message instance, specifying topic, tag and message body.
Message msg = new Message("topic-1" /* Topic */,
"TagA" /* Tag */,
("Hello RocketMQ " +
i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
);
//Call send message to deliver message to one of brokers.
SendResult sendResult = producer.send(msg);
System.out.printf("send result:%s%n", sendResult);
}
//Shut down once the producer instance is not longer in use.
producer.shutdown();
}
}
2.2.2 異步調用
package org.equaker.cache;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
/**
* 異步發送消息-RocketMQ
* @author EQuaker
*
*/
public class AsyncProducer_RocketMQ {
public static void main(String[] args) throws Exception {
//Instantiate with a producer group name.
DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
// Specify name server addresses.
producer.setNamesrvAddr("外網ip:9876");
//Launch the instance.
producer.start();
producer.setRetryTimesWhenSendAsyncFailed(2);
producer.setSendMsgTimeout(5000);
for (int i = 0; i < 10; i++) {
final int index = i;
//Create a message instance, specifying topic, tag and message body.
Message msg = new Message("topic-2",
"TagB",
"OrderID188",
"Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
producer.send(msg, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("send success: " + sendResult.getMsgId());
}
@Override
public void onException(Throwable e) {
System.out.printf("%-10d Exception %s %n", index, e);
e.printStackTrace();
}
});
}
//Shut down once the producer instance is not longer in use.
//Thread.sleep(3000);
producer.shutdown();
}
}
注意:
可能會報錯:
1 org.apache.rocketmq.client.exception.MQClientException: No route info of this topic, topic-2
See http://rocketmq.apache.org/docs/faq/ for further details. Exception org.apache.rocketmq.client.exception.MQClientException: No route info of this topic, topic-2
See http://rocketmq.apache.org/docs/faq/ for further details.
主要是因爲rocketMQ異步發送,採用任務模式,及新線程,任務尚未完成就被producer.shutdown();
2.2.3 單向發送數據
package org.equaker.cache;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
/**
* 單向發送消息-RocketMQ
*
* @author EQuaker
*
*/
public class OnewayProducer_RocketMQ {
public static void main(String[] args) throws Exception {
// Instantiate with a producer group name.
DefaultMQProducer producer = new DefaultMQProducer("group3");
// Specify name server addresses.
producer.setNamesrvAddr("外網ip:9876");
// Launch the instance.
producer.start();
for (int i = 0; i < 10; i++) {
// Create a message instance, specifying topic, tag and message body.
Message msg = new Message("topic-3" /* Topic */, "TagA" /* Tag */,
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
);
// Call send message to deliver message to one of brokers.
producer.sendOneway(msg);
}
// Shut down once the producer instance is not longer in use.
producer.shutdown();
}
}
2.3 消費者
package org.equaker.cache;
import java.util.List;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
public class Consumer_RocketMQ {
public static void main(String[] args) throws Exception{
// Instantiate with specified consumer group name.
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consume1");
// Specify name server addresses.
consumer.setNamesrvAddr("外網ip:9876");
// Subscribe one more more topics to consume.
consumer.subscribe("topic-3", "*");
// Register callback to execute on arrival of messages fetched from brokers.
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);
MessageQueue messageQueue = context.getMessageQueue();
System.out.println("broker name: "+messageQueue.getBrokerName());
System.out.println("topic name: "+messageQueue.getTopic());
System.out.println("msgs length: "+msgs.size());
for(MessageExt msg : msgs) {
System.out.println(new String(msg.getBody()));
}
System.out.println("*******************");
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//Launch the consumer instance.
consumer.start();
System.out.printf("Consumer Started.%n");
}
}