RocetMQ发送顺序消息的所有方法以及代码示例

RocetMQ发送顺序消息的所有方法以及代码示例

  • 一、使用RocketMQTemplate发送顺序消息
    • (1)SendResult syncSendOrderly(String destination, Message message, String hashKey);同步发送
    • (2)SendResult syncSendOrderly(String destination, Message message, String hashKey, long timeout);同步发送
    • (3)SendResult syncSendOrderly(String destination, Object payload, String hashKey);同步发送
    • (4)SendResult syncSendOrderly(String destination, Object payload, String hashKey, long timeout);同步发送
    • (5)\ SendResult syncSendOrderly(String destination, Collection messages, String hashKey);同步发送
    • (6)\ SendResult syncSendOrderly(String destination, Collection messages, String hashKey, long timeout);同步发送
    • (7)void asyncSendOrderly(String destination, Message message, String hashKey, SendCallback sendCallback);异步发送
    • (8)void asyncSendOrderly(String destination, Message message, String hashKey, SendCallback sendCallback, long timeout);异步发送
    • (9)void asyncSendOrderly(String destination, Object payload, String hashKey, SendCallback sendCallback);异步发送
    • (10)void asyncSendOrderly(String destination, Object payload, String hashKey, SendCallback sendCallback, long timeout);异步发送
    • (11)void sendOneWayOrderly(String destination, Message message, String hashKey);one-way模式,异步发送
    • (12)void sendOneWayOrderly(String destination, Object payload, String hashKey);one-way模式,异步发送
  • 二、使用DefaultMQProducer发送顺序消息
  • 2.1 DefaultMQProducer的创建
    • (1)DefaultMQProducer(String namespace, String producerGroup, RPCHook rpcHook);
    • (2)DefaultMQProducer(String producerGroup, RPCHook rpcHook);
    • (3)DefaultMQProducer(String namespace, String producerGroup);
    • (4)DefaultMQProducer(String producerGroup);
    • (5)DefaultMQProducer(RPCHook rpcHook);
    • (6)DefaultMQProducer();
    • (7)DefaultMQProducer(String namespace, String producerGroup, RPCHook rpcHook, boolean enableMsgTrace, String customizedTraceTopic);
    • (8)DefaultMQProducer(String producerGroup, boolean enableMsgTrace, String customizedTraceTopic);
    • (9)DefaultMQProducer(String producerGroup, boolean enableMsgTrace);
  • 2.2 发送顺序消息
    • (1)SendResult send(Message msg, MessageQueue mq) throws MQClientException, RemotingException, MQBrokerException, InterruptedException;同步发送
    • (2)SendResult send(Message msg, MessageQueue mq, long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException;同步发送
    • (3)void send(Message msg, MessageQueue mq, SendCallback sendCallback) throws MQClientException, RemotingException, InterruptedException;异步发送
    • (4)void send(Message msg, MessageQueue mq, SendCallback sendCallback, long timeout) throws MQClientException, RemotingException, InterruptedException;异步发送
    • (5)void sendOneway(Message msg, MessageQueue mq) throws MQClientException, RemotingException, InterruptedException;one-way模式,异步发送
    • (6)SendResult send(Message msg, MessageQueueSelector selector, Object arg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException;同步发送
    • (7)SendResult send(Message msg, MessageQueueSelector selector, Object arg, long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException;同步发送
    • (8)void send(Message msg, MessageQueueSelector selector, Object arg, SendCallback sendCallback) throws MQClientException, RemotingException, InterruptedException;异步发送
    • (9)void send(Message msg, MessageQueueSelector selector, Object arg, SendCallback sendCallback,long timeout) throws MQClientException, RemotingException, InterruptedException;异步发送
  • 总结与展望

一、使用RocketMQTemplate发送顺序消息

关于什么是顺序消息,可以参考这篇文章:RocketMQ的顺序消息(顺序消费),这里主要讲如何使用RocketMQ发送顺序消息。

(1)SendResult syncSendOrderly(String destination, Message message, String hashKey);同步发送

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的消息的顺序。

(2)SendResult syncSendOrderly(String destination, Message message, String hashKey, long 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;

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来设置超时时间。

(3)SendResult syncSendOrderly(String destination, Object payload, String hashKey);同步发送

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。

(4)SendResult syncSendOrderly(String destination, Object payload, String hashKey, long 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", 3000L);
        System.out.println(sendResult);
    }
}

相对于方法(3)增加了timeout来设置超时时间。

(5) SendResult syncSendOrderly(String destination, Collection messages, String hashKey);同步发送

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);
    }
}

批量发送顺序消息。

(6) SendResult syncSendOrderly(String destination, Collection messages, String hashKey, long 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", 3000L);
        System.out.println(sendResult);
    }
}

相对于方法(5)增加了timeout来设置超时时间。

(7)void asyncSendOrderly(String destination, Message message, String hashKey, 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");
            }
        });
    }
}

相对于同步发送顺序消息,异步发送只是多了一个SendCallback参数。注意:异步发送顺序消息并不能严格保证消息的顺序

异步发送模式在消息发送后立刻返回,当消息完全完成发送后,会调用回调函数sendCallback来告知发送者本次发送是成功或者失败。异步模式通常用于响应时间敏感业务场景,即承受不了同步发送消息时等待返回的耗时代价。

(8)void asyncSendOrderly(String destination, Message message, String hashKey, SendCallback sendCallback, long 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");
            }
        }, 3000L);
    }
}

相对于方法(7)增加了timeout来设置超时时间。

(9)void asyncSendOrderly(String destination, Object payload, String hashKey, 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;

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。

(10)void asyncSendOrderly(String destination, Object payload, String hashKey, SendCallback sendCallback, long 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");
            }
        }, 3000L);
    }
}

相对于方法(9)增加了timeout来设置超时时间。

(11)void sendOneWayOrderly(String destination, Message message, String hashKey);one-way模式,异步发送

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的异步发送方式。

(12)void sendOneWayOrderly(String destination, Object payload, String hashKey);one-way模式,异步发送

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有多个构造函数,我们可以根据不同的场景使用不同的构造函数创建对象。

2.1 DefaultMQProducer的创建

(1)DefaultMQProducer(String namespace, String producerGroup, RPCHook rpcHook);

DefaultMQProducer producer = new DefaultMQProducer("命名空间", "生产者组", new AclClientRPCHook(new SessionCredentials("用户名","密码")));

此构造函数的第一个参数是命名空间,命名空间需要在服务端提前创建。第二个参数是生产者组,一个生产者组可以包含多个生产者,生产者组不需要提前创建,在创建DefaultMQProducer对象的时候赋值一个生产者组就可以。第三个参数是RPCHook对象用于权限认证,相当于你登陆一个网站需要输入用户名和密码。

命名空间是RocketMQ中的一个资源管理概念。用户不同的业务场景一般都可以通过命名空间做隔离,并且针对不同的业务场景设置专门的配置,例如消息保留时间。不同命名空间之间的 Topic 相互隔离,订阅相互隔离,角色权限相互隔离。

(2)DefaultMQProducer(String producerGroup, RPCHook rpcHook);

DefaultMQProducer producer = new DefaultMQProducer("生产者组", new AclClientRPCHook(new SessionCredentials("用户名","密码")));

此构造函数底层还是调用了构造方法(1),只不过将namespace设为了null,在没有命名空间的时候可以使用此构造函数。

(3)DefaultMQProducer(String namespace, String producerGroup);

DefaultMQProducer producer = new DefaultMQProducer("命名空间", "生产者组");

此构造函数底层还是调用了构造方法(1),只不过将RPCHook 设为了null,在不需要acl认证的时候可以使用此构造函数。

(4)DefaultMQProducer(String producerGroup);

DefaultMQProducer producer = new DefaultMQProducer("生产者组");

此构造函数底层还是调用了构造方法(1),只不过将namespace和RPCHook设为了null,在没有命名空间和不需要acl认证的时候可以使用此构造函数。

(5)DefaultMQProducer(RPCHook rpcHook);

DefaultMQProducer producer = new DefaultMQProducer(new AclClientRPCHook(new SessionCredentials("用户名","密码")));

此构造函数底层还是调用了构造方法(1),只不过将namespace设为了null,由于prodcuerGroup不能为null,所以RocketMQ会使用默认的生产者组:DEFAULT_PRODUCER

(6)DefaultMQProducer();

DefaultMQProducer producer = new DefaultMQProducer();

此构造函数底层还是调用了构造方法(1),只不过将namespace和RPCHook设为了null,由于prodcuerGroup不能为null,所以RocketMQ会使用默认的生产者组:DEFAULT_PRODUCER

(7)DefaultMQProducer(String namespace, String producerGroup, RPCHook rpcHook, boolean enableMsgTrace, String customizedTraceTopic);

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中的消息内容:
跟踪topic的消息内容

(8)DefaultMQProducer(String producerGroup, boolean enableMsgTrace, String customizedTraceTopic);

DefaultMQProducer producer = new DefaultMQProducer("生产者组", true, "traceTopic");

此构造函数底层还是调用了构造方法(7),只不过将namespace和RPCHook设为了null,使用于没有命名空间和不需要acl认证的时候。

(9)DefaultMQProducer(String producerGroup, boolean enableMsgTrace);

DefaultMQProducer producer = new DefaultMQProducer("生产者组", true);

此构造函数底层还是调用了构造方法(7),只不过将namespace、RPCHook和customizedTraceTopic设为了null。

2.2 发送顺序消息

(1)SendResult send(Message msg, MessageQueue mq) throws MQClientException, RemotingException, MQBrokerException, InterruptedException;同步发送

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()方法中。这样做虽然可以保证顺序,但是如果消息全部都往一个队列中发送和消费,会导致效率非常低下。

(2)SendResult send(Message msg, MessageQueue mq, long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException;同步发送

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来设置超时时间。

(3)void send(Message msg, MessageQueue mq, SendCallback sendCallback) throws MQClientException, RemotingException, InterruptedException;异步发送

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参数表明这个发送方式是异步发送。

(4)void send(Message msg, MessageQueue mq, SendCallback sendCallback, long timeout) throws MQClientException, RemotingException, InterruptedException;异步发送

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来设置超时时间。

(5)void sendOneway(Message msg, MessageQueue mq) throws MQClientException, RemotingException, InterruptedException;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.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模式异步发送消息到某一个队列。

(6)SendResult send(Message msg, MessageQueueSelector selector, Object arg) throws MQClientException, RemotingException, MQBrokerException, InterruptedException;同步发送

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 list, Message message, Object arg)这一个方法,第一个参数是MessageQueue的列表,第二个参数是send()方法中的msg,第三参数是send()方法中的arg,我们要实现这个接口从而自定义MessageQueue的选择方式。

(7)SendResult send(Message msg, MessageQueueSelector selector, Object arg, long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException;同步发送

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来设置超时时间。

(8)void send(Message msg, MessageQueueSelector selector, Object arg, SendCallback sendCallback) throws MQClientException, RemotingException, InterruptedException;异步发送

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参数表明这个发送方式是异步发送。

(9)void send(Message msg, MessageQueueSelector selector, Object arg, SendCallback sendCallback,long timeout) throws MQClientException, RemotingException, InterruptedException;异步发送

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的文章。

你可能感兴趣的:(RocketMQ的应用,java-rocketmq,java,spring,boot)