关于什么是顺序消息,可以参考这篇文章:RocketMQ的顺序消息(顺序消费),这里主要讲如何使用RocketMQ发送顺序消息。
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
public class OrderlyMessageProducer {
@Autowired
RocketMQTemplate rocketMQTemplate;
public void sendMessages() {
Message<String> sendMessage = MessageBuilder.withPayload("这里些消息体")
.setHeader("消息属性的键","消息属性的值")
.setHeader("KEYS", "消息的key")
.build();
SendResult sendResult = rocketMQTemplate.syncSendOrderly("topicA:tagA", sendMessage, "hashKey");
System.out.println(sendResult);
}
}
相对于发送普通消息,发送顺序消息的关键是设置hashKey这个参数。接下来浅显的讲一下实现顺序消息的原理。如下是RocketMQ发送顺序消息的部分源码:
import java.util.List;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
public class SelectMessageQueueByHash implements MessageQueueSelector {
public SelectMessageQueueByHash() {
}
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
int value = arg.hashCode() % mqs.size();
if (value < 0) {
value = Math.abs(value);
}
return (MessageQueue)mqs.get(value);
}
}
通过源码可以看到,RocketMQ会获取每个Java对象都有的hashCode来对queue的数量取模,如果取模后得到的值小于0则取绝对值,最后根据取模后的值去集合中获取一个queue。然后消息就发送到这个queue中。
每个topic都有1-16个queue,queue的数量是在创建topic时设定的,每个消息会负载均衡地发送到这些queue中的其中一个。顺序消息的原理就是使需要保证顺序的消息都发送到同一个queue,从而保证顺序。这里对底层原理不做过多的说明。
hashKey相同的消息被保证发送到topic下相同的queue从而实现顺序消息。实际工作中,hashKey往往被设为订单ID,交易ID等从而保证相同订单ID的消息的顺序。
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
public class OrderlyMessageProducer {
@Autowired
RocketMQTemplate rocketMQTemplate;
public void sendMessages() {
Message<String> sendMessage = MessageBuilder.withPayload("这里设置消息体")
.setHeader("消息属性的键","消息属性的值")
.setHeader("KEYS", "消息的key")
.build();
SendResult sendResult = rocketMQTemplate.syncSendOrderly("topicA:tagA", sendMessage, "hashKey", 3000L);
System.out.println(sendResult);
}
}
相对于方法(1)增加了timeout来设置超时时间。
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
public class OrderlyMessageProducer {
@Autowired
RocketMQTemplate rocketMQTemplate;
public void sendMessages() {
SendResult sendResult = rocketMQTemplate.syncSendOrderly("topicA:tagA", "这里设置消息体", "hashKey");
System.out.println(sendResult);
}
}
相对于方法(1),第二个参数变成了Object类型,我们可以直接传入字符串或者实体类,RocketMQ会在底层帮我们转化成Message对象。此方法的缺点是不能设置消息的属性和key。
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
public class OrderlyMessageProducer {
@Autowired
RocketMQTemplate rocketMQTemplate;
public void sendMessages() {
SendResult sendResult = rocketMQTemplate.syncSendOrderly("topicA:tagA", "这里设置消息体", "hashKey", 3000L);
System.out.println(sendResult);
}
}
相对于方法(3)增加了timeout来设置超时时间。
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import java.util.ArrayList;
import java.util.Collection;
public class OrderlyMessageProducer {
@Autowired
RocketMQTemplate rocketMQTemplate;
public void sendMessages() {
Collection<Message<String>> messages = new ArrayList<>();
for (int i=0; i<3; i++) {
Message<String> sendMessage = MessageBuilder.withPayload("这里设置消息体" + i)
.setHeader("消息属性的键","消息属性的值")
.setHeader("KEYS", "消息的key")
.build();
messages.add(sendMessage);
}
SendResult sendResult = rocketMQTemplate.syncSendOrderly("topicA:tagA", messages, "hashKey");
System.out.println(sendResult);
}
}
批量发送顺序消息。
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import java.util.ArrayList;
import java.util.Collection;
public class OrderlyMessageProducer {
@Autowired
RocketMQTemplate rocketMQTemplate;
public void sendMessages() {
Collection<Message<String>> messages = new ArrayList<>();
for (int i=0; i<3; i++) {
Message<String> sendMessage = MessageBuilder.withPayload("这里设置消息体" + i)
.setHeader("消息属性的键","消息属性的值")
.setHeader("KEYS", "消息的key")
.build();
messages.add(sendMessage);
}
SendResult sendResult = rocketMQTemplate.syncSendOrderly("topicA:tagA", messages, "hashKey", 3000L);
System.out.println(sendResult);
}
}
相对于方法(5)增加了timeout来设置超时时间。
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
public class OrderlyMessageProducer {
@Autowired
RocketMQTemplate rocketMQTemplate;
public void sendMessages() {
Message<String> sendMessage = MessageBuilder.withPayload("这里设置消息体")
.setHeader("消息属性的键","消息属性的值")
.setHeader("KEYS", "消息的key")
.build();
rocketMQTemplate.asyncSendOrderly("topicA:tagA", sendMessage, "hashKey", new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("Send success");
}
@Override
public void onException(Throwable throwable) {
System.out.println("Send fail");
}
});
}
}
相对于同步发送顺序消息,异步发送只是多了一个SendCallback参数。注意:异步发送顺序消息并不能严格保证消息的顺序
。
异步发送模式在消息发送后立刻返回,当消息完全完成发送后,会调用回调函数sendCallback来告知发送者本次发送是成功或者失败。异步模式通常用于响应时间敏感业务场景,即承受不了同步发送消息时等待返回的耗时代价。
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
public class OrderlyMessageProducer {
@Autowired
RocketMQTemplate rocketMQTemplate;
public void sendMessages() {
Message<String> sendMessage = MessageBuilder.withPayload("这里设置消息体")
.setHeader("消息属性的键","消息属性的值")
.setHeader("KEYS", "消息的key")
.build();
rocketMQTemplate.asyncSendOrderly("topicA:tagA", sendMessage, "hashKey", new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("Send success");
}
@Override
public void onException(Throwable throwable) {
System.out.println("Send fail");
}
}, 3000L);
}
}
相对于方法(7)增加了timeout来设置超时时间。
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
public class OrderlyMessageProducer {
@Autowired
RocketMQTemplate rocketMQTemplate;
public void sendMessages() {
rocketMQTemplate.asyncSendOrderly("topicA:tagA", "这里设置消息体", "hashKey", new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("Send success");
}
@Override
public void onException(Throwable throwable) {
System.out.println("Send fail");
}
});
}
}
相对于方法(7),第二个参数变成了Object类型,我们可以直接传入字符串或者实体类,RocketMQ会在底层帮我们转化成Message对象。此方法的缺点是不能设置消息的属性和key。
import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
public class OrderlyMessageProducer {
@Autowired
RocketMQTemplate rocketMQTemplate;
public void sendMessages() {
rocketMQTemplate.asyncSendOrderly("topicA:tagA", "这里设置消息体", "hashKey", new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("Send success");
}
@Override
public void onException(Throwable throwable) {
System.out.println("Send fail");
}
}, 3000L);
}
}
相对于方法(9)增加了timeout来设置超时时间。
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
public class OrderlyMessageProducer {
@Autowired
RocketMQTemplate rocketMQTemplate;
public void sendMessages() {
Message<String> sendMessage = MessageBuilder.withPayload("这里些消息体")
.setHeader("消息属性的键","消息属性的值")
.setHeader("KEYS", "消息的key")
.build();
rocketMQTemplate.sendOneWayOrderly("topicA:tagA", sendMessage, "hashKey");
}
}
使用one-way模式发送顺序消息。本质上是讲SendCallback设为null的异步发送消息。注意:异步发送顺序消息并不能严格保证消息的顺序
。
采用one-way发送模式发送消息的时候,发送端发送完消息后会立即返回,不会等待来自broker的ack来告知本次消息发送是否完全完成发送。这种模式吞吐量很大,但是存在消息丢失的风险,所以其适用于不重要的消息发送,比如日志收集。one-way模式本质上是没有sendCallback的异步发送方式。
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
public class OrderlyMessageProducer {
@Autowired
RocketMQTemplate rocketMQTemplate;
public void sendMessages() {
rocketMQTemplate.sendOneWayOrderly("topicA:tagA", "这里设置消息体", "hashKey");
}
}
相对于方法(11)第二个参数变成了Object类型,我们可以直接传入字符串或者实体类,RocketMQ会在底层帮我们转化成Message对象。此方法的缺点是不能设置消息的属性和key。
综上所述,使用RocketMQTemplate发送顺序消息相对与发送普通消息只是多了一个hashKey参数,详细的如何使用RocketMQTemplate发送普通消息可以参考我的另一篇文章:RocketMQ发送普通消息的所有方法以及代码示例。值得注意的是,异步发送顺序消息并不能严格保证消息的顺序。
DefaultMQProducer有多个构造函数,我们可以根据不同的场景使用不同的构造函数创建对象。
DefaultMQProducer producer = new DefaultMQProducer("命名空间", "生产者组", new AclClientRPCHook(new SessionCredentials("用户名","密码")));
此构造函数的第一个参数是命名空间,命名空间需要在服务端提前创建。第二个参数是生产者组,一个生产者组可以包含多个生产者,生产者组不需要提前创建,在创建DefaultMQProducer对象的时候赋值一个生产者组就可以。第三个参数是RPCHook对象用于权限认证,相当于你登陆一个网站需要输入用户名和密码。
命名空间是RocketMQ中的一个资源管理概念。用户不同的业务场景一般都可以通过命名空间做隔离,并且针对不同的业务场景设置专门的配置,例如消息保留时间。不同命名空间之间的 Topic 相互隔离,订阅相互隔离,角色权限相互隔离。
DefaultMQProducer producer = new DefaultMQProducer("生产者组", new AclClientRPCHook(new SessionCredentials("用户名","密码")));
此构造函数底层还是调用了构造方法(1)
,只不过将namespace设为了null,在没有命名空间的时候可以使用此构造函数。
DefaultMQProducer producer = new DefaultMQProducer("命名空间", "生产者组");
此构造函数底层还是调用了构造方法(1)
,只不过将RPCHook 设为了null,在不需要acl认证的时候可以使用此构造函数。
DefaultMQProducer producer = new DefaultMQProducer("生产者组");
此构造函数底层还是调用了构造方法(1)
,只不过将namespace和RPCHook设为了null,在没有命名空间和不需要acl认证的时候可以使用此构造函数。
DefaultMQProducer producer = new DefaultMQProducer(new AclClientRPCHook(new SessionCredentials("用户名","密码")));
此构造函数底层还是调用了构造方法(1)
,只不过将namespace设为了null,由于prodcuerGroup不能为null,所以RocketMQ会使用默认的生产者组:DEFAULT_PRODUCER
。
DefaultMQProducer producer = new DefaultMQProducer();
此构造函数底层还是调用了构造方法(1)
,只不过将namespace和RPCHook设为了null,由于prodcuerGroup不能为null,所以RocketMQ会使用默认的生产者组:DEFAULT_PRODUCER
。
DefaultMQProducer producer = new DefaultMQProducer("命名空间", "生产者组", new AclClientRPCHook(new SessionCredentials("用户名","密码")), true, "traceTopic");
此构造函数的第一个参数是命名空间,命名空间需要在服务端提前创建。第二个参数是生产者组,一个生产者组可以包含多个生产者,生产者组不需要提前创建,在创建DefaultMQProducer对象的时候赋值一个生产者组就可以。第三个参数是RPCHook对象用于权限认证,相当于你登陆一个网站需要输入用户名和密码。第四个参数是布尔类型,表示是否开启消息追踪。第五个参数是消息跟踪的topic的名称,这个topic专门用来做消息追踪的,一般不会用这个topic生产和消费业务数据。开启追踪后,追踪topic内会记录生产者的一些信息,比如生产者IP、消息的MessageID等
。例如下面的代码就是开启追踪并设置trace-topic
为追踪topic,然后将消息发送到topicA中,于是topicA里面是业务数据,trace-topic里面是用于消息追踪的追踪数据。也就是发送一次消息会发送一份业务数据和一份追踪数据到业务topic和追踪topic
,
package com.sgm.esb.gateway.service;
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.nio.charset.StandardCharsets;
public class DefaultMQProducerTest {
public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
DefaultMQProducer producer = new DefaultMQProducer("生产者组",
new AclClientRPCHook(new SessionCredentials("用户名","密码")),
true, "trace-topic");
producer.setNamesrvAddr("nameServer集群IP");
producer.start();
Message sendMessage = new Message("topicA", "tagA", "这里设置消息体".getBytes(StandardCharsets.UTF_8));
sendMessage.putUserProperty("消息的属性的键", "消息的属性的值");
sendMessage.setKeys("消息的key");
producer.send(sendMessage);
producer.shutdown();
}
}
如下是追踪topic中的消息内容:
DefaultMQProducer producer = new DefaultMQProducer("生产者组", true, "traceTopic");
此构造函数底层还是调用了构造方法(7)
,只不过将namespace和RPCHook设为了null,使用于没有命名空间和不需要acl认证的时候。
DefaultMQProducer producer = new DefaultMQProducer("生产者组", true);
此构造函数底层还是调用了构造方法(7)
,只不过将namespace、RPCHook和customizedTraceTopic设为了null。
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class DefaultMQProducerTest {
public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
DefaultMQProducer producer = new DefaultMQProducer("生产者组",
new AclClientRPCHook(new SessionCredentials("用户名","密码")),
true, "trace-topic");
producer.setNamesrvAddr("nameServer集群IP");
producer.start();
Message sendMessage = new Message("topicA", "tagA", ("这里设置消息体").getBytes(StandardCharsets.UTF_8));
sendMessage.putUserProperty("消息的属性的键", "消息的属性的值");
sendMessage.setKeys("消息的key");
// fetchPublishMessageQueues()中传入的topic必须和Message设置的topic一致,否则发送消息的时候会报错
List<MessageQueue> messageQueues = producer.fetchPublishMessageQueues("topicA");
// 根据需求获取一个MessageQueue
MessageQueue queue = messageQueues.get(0);
producer.send(sendMessage, queue);
producer.shutdown();
}
}
SendResult send(Message msg, MessageQueue mq)第二个参数是MessageQueue类型,表示一个消息队列,设置了这个参数之后消息都会发到这个队列中去,由于队列这种数据结构具有先进先出的特性,所以可以保证消息的顺序性。我们先使用fetchPublishMessageQueues(String topic)方法获取topicA下的MessageQueue,然后根据实际的需要选取一个queue作为参数传入send()方法中。这样做虽然可以保证顺序,但是如果消息全部都往一个队列中发送和消费,会导致效率非常低下。
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class DefaultMQProducerTest {
public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
DefaultMQProducer producer = new DefaultMQProducer("生产者组",
new AclClientRPCHook(new SessionCredentials("用户名","密码")),
true, "trace-topic");
producer.setNamesrvAddr("nameServer集群IP");
producer.start();
Message sendMessage = new Message("topicA", "tagA", ("这里设置消息体").getBytes(StandardCharsets.UTF_8));
sendMessage.putUserProperty("消息的属性的键", "消息的属性的值");
sendMessage.setKeys("消息的key");
// fetchPublishMessageQueues()中传入的topic必须和Message设置的topic一致,否则发送消息的时候会报错
List<MessageQueue> messageQueues = producer.fetchPublishMessageQueues("topicA");
// 根据需求获取一个MessageQueue
MessageQueue queue = messageQueues.get(0);
producer.send(sendMessage, queue, 3000L);
producer.shutdown();
}
}
相对于方法(1)增加了timeout来设置超时时间。
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
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.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class DefaultMQProducerTest {
public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
DefaultMQProducer producer = new DefaultMQProducer("生产者组",
new AclClientRPCHook(new SessionCredentials("用户名","密码")),
true, "trace-topic");
producer.setNamesrvAddr("nameServer集群IP");
producer.start();
Message sendMessage = new Message("topicA", "tagA", ("这里设置消息体").getBytes(StandardCharsets.UTF_8));
sendMessage.putUserProperty("消息的属性的键", "消息的属性的值");
sendMessage.setKeys("消息的key");
// fetchPublishMessageQueues()中传入的topic必须和Message设置的topic一致,否则发送消息的时候会报错
List<MessageQueue> messageQueues = producer.fetchPublishMessageQueues("topicA");
// 根据需求获取一个MessageQueue
MessageQueue queue = messageQueues.get(0);
producer.send(sendMessage, queue, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("Send success");
}
@Override
public void onException(Throwable throwable) {
System.out.println("Send fail");
}
});
producer.shutdown();
}
}
增加了SendCallback参数表明这个发送方式是异步发送。
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
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.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class DefaultMQProducerTest {
public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
DefaultMQProducer producer = new DefaultMQProducer("生产者组",
new AclClientRPCHook(new SessionCredentials("用户名","密码")),
true, "trace-topic");
producer.setNamesrvAddr("nameServer集群IP");
producer.start();
Message sendMessage = new Message("topicA", "tagA", ("这里设置消息体").getBytes(StandardCharsets.UTF_8));
sendMessage.putUserProperty("消息的属性的键", "消息的属性的值");
sendMessage.setKeys("消息的key");
// fetchPublishMessageQueues()中传入的topic必须和Message设置的topic一致,否则发送消息的时候会报错
List<MessageQueue> messageQueues = producer.fetchPublishMessageQueues("topicA");
// 根据需求获取一个MessageQueue
MessageQueue queue = messageQueues.get(0);
producer.send(sendMessage, queue, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("Send success");
}
@Override
public void onException(Throwable throwable) {
System.out.println("Send fail");
}
}, 3000L);
producer.shutdown();
}
}
相对于方法(3)增加了timeout来设置超时时间。
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class DefaultMQProducerTest {
public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
DefaultMQProducer producer = new DefaultMQProducer("生产者组",
new AclClientRPCHook(new SessionCredentials("用户名","密码")),
true, "trace-topic");
producer.setNamesrvAddr("nameServer集群IP");
producer.start();
Message sendMessage = new Message("topicA", "tagA", ("这里设置消息体").getBytes(StandardCharsets.UTF_8));
sendMessage.putUserProperty("消息的属性的键", "消息的属性的值");
sendMessage.setKeys("消息的key");
// fetchPublishMessageQueues()中传入的topic必须和Message设置的topic一致,否则会报错
List<MessageQueue> messageQueues = producer.fetchPublishMessageQueues("topicA");
// 根据需求获取一个MessageQueue
MessageQueue queue = messageQueues.get(0);
producer.sendOneway(sendMessage, queue);
producer.shutdown();
}
}
使用one-way模式异步发送消息到某一个队列。
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class DefaultMQProducerTest {
public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
DefaultMQProducer producer = new DefaultMQProducer("生产者组",
new AclClientRPCHook(new SessionCredentials("用户名","密码")),
true, "trace-topic");
producer.setNamesrvAddr("nameServer集群IP");
producer.start();
Message sendMessage = new Message("topicA", "tagA", ("这里设置消息体").getBytes(StandardCharsets.UTF_8));
sendMessage.putUserProperty("消息的属性的键", "消息的属性的值");
sendMessage.setKeys("消息的key");
producer.send(sendMessage, new MessageQueueSelector() {
@Override
// select方法的arg参数就是send(Message msg, MessageQueueSelector selector, Object arg)的第三个参数
public MessageQueue select(List<MessageQueue> list, Message message, Object arg) {
// 自定义选取MessageQueue的方法。这里是根据Object的hashCode来选取
int value = arg.hashCode() % list.size();
if (value < 0) {
value = Math.abs(value);
}
return list.get(value);
}
}, "hashKey");
producer.shutdown();
}
}
SendResult send(Message msg, MessageQueueSelector selector, Object arg)的第二个参数MessageQueueSelector是一个接口,里面只有MessageQueue select(List
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class DefaultMQProducerTest {
public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
DefaultMQProducer producer = new DefaultMQProducer("生产者组",
new AclClientRPCHook(new SessionCredentials("用户名","密码")),
true, "trace-topic");
producer.setNamesrvAddr("nameServer集群IP");
producer.start();
Message sendMessage = new Message("topicA", "tagA", ("这里设置消息体").getBytes(StandardCharsets.UTF_8));
sendMessage.putUserProperty("消息的属性的键", "消息的属性的值");
sendMessage.setKeys("消息的key");
producer.send(sendMessage, new MessageQueueSelector() {
@Override
// select方法的arg参数就是send(Message msg, MessageQueueSelector selector, Object arg)的第三个参数
public MessageQueue select(List<MessageQueue> list, Message message, Object arg) {
// 自定义选取MessageQueue的方法。这里是根据Object的hashCode来选取
int value = arg.hashCode() % list.size();
if (value < 0) {
value = Math.abs(value);
}
return list.get(value);
}
}, "hashKey", 3000L);
producer.shutdown();
}
}
相对于方法(6)增加了timeout来设置超时时间。
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
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.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class DefaultMQProducerTest {
public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
DefaultMQProducer producer = new DefaultMQProducer("生产者组",
new AclClientRPCHook(new SessionCredentials("用户名","密码")),
true, "trace-topic");
producer.setNamesrvAddr("nameServer集群IP");
producer.start();
Message sendMessage = new Message("topicA", "tagA", ("这里设置消息体").getBytes(StandardCharsets.UTF_8));
sendMessage.putUserProperty("消息的属性的键", "消息的属性的值");
sendMessage.setKeys("消息的key");
producer.send(sendMessage, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> list, Message message, Object arg) {
int value = arg.hashCode() % list.size();
if (value < 0) {
value = Math.abs(value);
}
return list.get(value);
}
}, "hashKey", new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("Send success");
}
@Override
public void onException(Throwable throwable) {
System.out.println("Send fail");
}
});
producer.shutdown();
}
}
增加了SendCallback参数表明这个发送方式是异步发送。
import org.apache.rocketmq.acl.common.AclClientRPCHook;
import org.apache.rocketmq.acl.common.SessionCredentials;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
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.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class DefaultMQProducerTest {
public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
DefaultMQProducer producer = new DefaultMQProducer("生产者组",
new AclClientRPCHook(new SessionCredentials("用户名","密码")),
true, "trace-topic");
producer.setNamesrvAddr("nameServer集群IP");
producer.start();
Message sendMessage = new Message("topicA", "tagA", ("这里设置消息体").getBytes(StandardCharsets.UTF_8));
sendMessage.putUserProperty("消息的属性的键", "消息的属性的值");
sendMessage.setKeys("消息的key");
producer.send(sendMessage, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> list, Message message, Object arg) {
int value = arg.hashCode() % list.size();
if (value < 0) {
value = Math.abs(value);
}
return list.get(value);
}
}, "hashKey", new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("Send success");
}
@Override
public void onException(Throwable throwable) {
System.out.println("Send fail");
}
}, 3000L);
producer.shutdown();
}
}
相对于方法(8)增加了timeout来设置超时时间。
使用RocketMQTemplate发送顺序消息相对于发送普通消息只是要设置一下hashKey,hashKey一般设为交易ID、订单ID等。使用DefaultMQProducer发送顺序消息相对于使用RocketMQTemplate我们需要自己实现MessageQueueSelector接口来自定义MessageQueue的选取方式。这是我的第二篇文章,我的第一篇文章《RocketMQ发送普通消息的所有方法以及代码示例》有需要的读者也可以阅读,之后我会继续写关于RocketMQ的文章。