RocketMQ与OpenMessaging

写在前面

注意,本章写在前面的东西比较多,这都是为了后面的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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章