寫在前面
注意,本章寫在前面的東西比較多,這都是爲了後面的OpenMessaging 與 RocketMq 結合做準備。
1。OpenMessaging 是啥東西。這是一種新興的理論,可以說是理論吧,因爲他並沒有新技術的產生,更像是一種總結,統一管理的概念。大概意思就是,現在消息隊列產品太多了比如,rocketMQ,rabbitMq,kafaka,****啥的,每一種消息發佈產品都有侷限性,比如語言,平臺,消息規範,序列化啥的。爲了避免這種問題,阿里的高級架構師,就提出需要統一度量衡,並率先在自家產品RocketMq 的身上提供的具體的實現,不過這種實現由於提出不久,所以功能比較單一,不是很強大。
2。System.getenv("OMS_RMQ_DIRECT_NAME_SRV"),寫在 環境變量裏面無效。這是由於shell子進程的問題。eclipse(其他軟件同理)也是一個子進程,當先開啓eclipse,後新增變量的時候,由於讀取環境變量不刷新,導致讀取不到,一般的做法是關閉eclipse,然後再開啓即可。注意,是關閉在開啓,而不是重啓。因爲重啓的話,進程並沒有銷燬,新建的過程,導致環境變量也不會重新讀取。具體System.getProperties("")與System.getenv(""),有啥區別,請自行百度。
3。請自行了結RocketMq的基礎知識,這裏不做過多解釋。
正文開始:
pom.xml:
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.5.0</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-openmessaging</artifactId>
<version>4.5.0</version>
</dependency>
apache官網的openmessaging demo相信大家都看了(http://rocketmq.apache.org/docs/openmessaging-example/),
但由於版本問題,所以運行不成功,這裏做一些解釋,及部分源碼解讀。
生產者:
OpenMeassageProducer.java
package org.equaker.cache.rocketmq;
import java.nio.charset.Charset;
import io.openmessaging.Future;
import io.openmessaging.FutureListener;
import io.openmessaging.Message;
import io.openmessaging.MessagingAccessPoint;
import io.openmessaging.internal.DefaultKeyValue;
import io.openmessaging.internal.MessagingAccessPointAdapter;
import io.openmessaging.producer.Producer;
import io.openmessaging.producer.SendResult;
public class OpenMeassageProducer {
public static void main(String[] args) {
DefaultKeyValue defaultKeyValue = new DefaultKeyValue();
final MessagingAccessPoint messagingAccessPoint =
MessagingAccessPointAdapter.getMessagingAccessPoint("oms:rocketmq://ip:9876/topic-1",defaultKeyValue);
final Producer producer = messagingAccessPoint.createProducer();
messagingAccessPoint.startup();
System.out.printf("MessagingAccessPoint startup OK%n");
producer.startup();
System.out.printf("Producer startup OK%n");
{
Message message = producer.createBytesMessage("OMS_HELLO_TOPIC", "OMS_HELLO_BODY".getBytes(Charset.forName("UTF-8")));
SendResult sendResult = producer.send(message);
System.out.printf("Send sync message OK, msgId: %s%n", sendResult.messageId());
}
{
final Future<SendResult> result = producer.sendAsync(producer.createBytesMessage("OMS_HELLO_TOPIC", "OMS_HELLO_BODY".getBytes(Charset.forName("UTF-8"))));
result.addListener(new FutureListener<SendResult>() {
@Override
public void operationComplete(Future<SendResult> future) {
System.out.printf("Send async message OK, msgId: %s%n", future.get().messageId());
}
});
}
{
producer.sendOneway(producer.createBytesMessage("OMS_HELLO_TOPIC", "OMS_HELLO_BODY".getBytes(Charset.forName("UTF-8"))));
System.out.printf("Send oneway message OK%n");
}
producer.shutdown();
messagingAccessPoint.shutdown();
}
}
運行官網demo或者本文提供的demo的時候,可能會出現,
The OMS driver URL [openmessaging:rocketmq://ip:9876] is illegal,
name server is null,please set it
大概就是這個意思吧,我們可以看到源碼裏面的規範。
OpenMessaging的協議以oms開頭,並不是官網提供的openmessaging,uri總共分爲四部分,
例如:oms:rocketmq://127.0.0.1:9876/topic-1
第一部分oms,這一部分指定openmessaging協議,一般不會改變,類似http,jdbc啥的。
第二部分rocketmq,指定具體的消息隊列產品,目前市場上,好像也只有rocketmq,提供了openmessaging的部分實現。等其他消息隊列產品提供實現的時候,會提供協議的。
第三部分127.0.0.1:9876,就是簡單的消息隊列產品地址了,這個沒啥解釋的。
第四部分/topic-1,一個域信息,相當於,做一個分類的作用。
所以,粗出現上述異常的時候,請先檢查自己的url地址。
當url沒有問題的時候,還有一個坑等着你呢。來,我們接着看源碼。
1,創建producer對象。
接着源碼走,創建生產者實現類。
再走,實例化父類AbstractOMSProducer。
最後的源碼,
總結:當創建消息生產者Producer的時候,他會讀取系統環境變量OMS_RMQ_DIRECT_NAME_SRV,如果爲true,就會設置name server,我們知道rocketmq必須要設置name server的,他起到一個路由的作用。
所以必須設置環境變量,
我的設置如下:
記得關閉,重啓eclipse。
這下子,消息生產者可以運行成功了。接下來就是消費者了。
消費者:
消費者-1:
OpenMessagingPullConsumer.java
package org.equaker.cache.rocketmq;
import io.openmessaging.Message;
import io.openmessaging.MessagingAccessPoint;
import io.openmessaging.OMSBuiltinKeys;
import io.openmessaging.consumer.PullConsumer;
import io.openmessaging.internal.DefaultKeyValue;
import io.openmessaging.internal.MessagingAccessPointAdapter;
import io.openmessaging.rocketmq.domain.NonStandardKeys;
public class OpenMessagingPullConsumer {
public static void main(String[] args) {
DefaultKeyValue defaultKeyValue = new DefaultKeyValue();
defaultKeyValue.put(NonStandardKeys.CONSUMER_GROUP, "consume1");
defaultKeyValue.put(OMSBuiltinKeys.CONSUMER_ID,"1");
defaultKeyValue.put(OMSBuiltinKeys.REGION,"OMS_HELLO_TOPIC");
//defaultKeyValue.put(OMSBuiltinKeys.OPERATION_TIMEOUT, );
final MessagingAccessPoint messagingAccessPoint = MessagingAccessPointAdapter
.getMessagingAccessPoint("oms:rocketmq://***:9876/topic-1", defaultKeyValue);
final PullConsumer consumer = messagingAccessPoint
.createPullConsumer(defaultKeyValue);
//設置topic
consumer.attachQueue("OMS_HELLO_TOPIC",defaultKeyValue);
messagingAccessPoint.startup();
System.out.printf("MessagingAccessPoint startup OK%n");
System.out.printf("Consumer startup OK%n");
consumer.startup();
Integer count = 0;
//輪詢,獲取message
while(true) {
System.out.println("輪詢:"+ count++);
Message message = consumer.receive();
if (message != null) {
System.out.println("body:>>>"+new String(message.getBody(byte[].class)));
String msgId = message.sysHeaders().getString(Message.BuiltinKeys.MESSAGE_ID);
System.out.printf("Received one message: %s%n", msgId);
consumer.ack(msgId);
}
}
// consumer.shutdown();
// messagingAccessPoint.shutdown();
}
}
這是官網PullConsumer的例子,
這裏的運行只需要注意幾個小問題即可,首先沒有設置customer group的話,會報異常,但是這個customer group 不是
defaultKeyValue.put(NonStandardKeys.CONSUMER_GROUP, "consume1");
而是,
defaultKeyValue.put(OMSBuiltinKeys.CONSUMER_ID,"1");
我們看一下源碼
顯然,沒有設置customer_id的話會報異常。 至於他參數解析的過程涉及到參數的命名規範問題。
規則就在這裏了,就是會根據屬性反射調用set方法。醜陋的代碼總會有的。
一定要注意設置
consumer.attachQueue("OMS_HELLO_TOPIC",defaultKeyValue);
相當於訂閱話題。不然,鬼知道你要哪樣的信息。
消費者-2:
OpenMessagingPushConsumer.java
package org.equaker.cache.rocketmq;
import io.openmessaging.Message;
import io.openmessaging.MessagingAccessPoint;
import io.openmessaging.OMS;
import io.openmessaging.OMSBuiltinKeys;
import io.openmessaging.consumer.MessageListener;
import io.openmessaging.consumer.PushConsumer;
import io.openmessaging.internal.DefaultKeyValue;
import io.openmessaging.internal.MessagingAccessPointAdapter;
import io.openmessaging.rocketmq.domain.NonStandardKeys;
public class OpenMessagingPushConsumer {
public static void main(String[] args) {
DefaultKeyValue defaultKeyValue = new DefaultKeyValue();
defaultKeyValue.put(NonStandardKeys.CONSUMER_GROUP, "consume1");
defaultKeyValue.put(OMSBuiltinKeys.CONSUMER_ID,"1");
defaultKeyValue.put(OMSBuiltinKeys.REGION,"OMS_HELLO_TOPIC");
final MessagingAccessPoint messagingAccessPoint = MessagingAccessPointAdapter
.getMessagingAccessPoint("oms:rocketmq://127.0.0.1:9876/topic-1",defaultKeyValue);
final PushConsumer consumer = messagingAccessPoint.
createPushConsumer(OMS.newKeyValue().put(NonStandardKeys.CONSUMER_GROUP, "OMS_CONSUMER"));
messagingAccessPoint.startup();
System.out.printf("MessagingAccessPoint startup OK%n");
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
consumer.shutdown();
messagingAccessPoint.shutdown();
}
}));
consumer.attachQueue("OMS_HELLO_TOPIC", new MessageListener() {
@Override
public void onReceived(Message message, Context context) {
System.out.println("body:>>>"+new String(message.getBody(byte[].class)));
System.out.printf("Received one message: %s%n", message.sysHeaders().getString(Message.BuiltinKeys.MESSAGE_ID));
context.ack();
}
});
consumer.startup();
System.out.printf("Consumer Started.%n");
}
}
這個 也就比 pullConsumer 多了一個消息監聽器,不用採用輪詢的方式查詢消息。其他注意信息,參考PullConsumer