注意,本章写在前面的东西比较多,这都是为了后面的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:
org.apache.rocketmq
rocketmq-client
4.5.0
org.apache.rocketmq
rocketmq-openmessaging
4.5.0
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 result = producer.sendAsync(producer.createBytesMessage("OMS_HELLO_TOPIC", "OMS_HELLO_BODY".getBytes(Charset.forName("UTF-8"))));
result.addListener(new FutureListener() {
@Override
public void operationComplete(Future 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。
这下子,消息生产者可以运行成功了。接下来就是消费者了。
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);
相当于订阅话题。不然,鬼知道你要哪样的信息。
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