五分钟掌握RocketMQ消息生产

217 阅读4分钟

接下来将花费大概五分钟的时间来熟悉RocketMQ中比较重要的角色:生产者

  • 消息类型:普通消息(并发消息)、顺序消息、事务消息
  • 消息发送方式:同步发送、异步发送、单向发送

角色概括

在RocketMQ中,生产者是发送消息的一方。

image.png

  • 生产者组:由一个及多个生产者实例组成,在使用时指定同一个组名,一个生产者组可以生产多个Topic的消息。

  • 生产者实例:一个生产者组中的进程实例。

  • Topic:消息主题,一个Topic由一个或多个Queue组成。

生产者类

image.png

  • org.apache.rocketmq.client.producer.TransactionMQProducer :用于发送事务消息

  • org.apache.rocketmq.client.producer.DefaultMQProducer: 用于发送普通消息、顺序消息、批量消息等

消息结构

org.apache.rocketmq.common.message.Message

public class Message implements Serializable {
    private static final long serialVersionUID = 8445773977080406428L;
    private String topic;
    private int flag;
    private Map<String, String> properties;
    private byte[] body;
    private String transactionId;
  • topic :消息主题
  • flag :消息的标记,不做任何处理
  • properties:存储tags、keys、延迟级别等属性
public void setTags(String tags) {
    this.putProperty("TAGS", tags);
}

public void setKeys(String keys) {
    this.putProperty("KEYS", keys);
}

public void setDelayTimeLevel(int level) {
    this.putProperty("DELAY", String.valueOf(level));
}
  • body:消息内容,字节数组,注意编解码格式。
  • transactionId:事务Id

消息类型

RocketMQ支持普通(并发)消息、分区有序消息、事务消息、延迟消息。

  • 普通消息:普通消息没有发送消费顺序,因此性能可达十万TPS。
  • 分区有序消息:Topic中分多个分区对消息进行保存消费,在同一个分区的消息遵循FIFO原则。
  • 事务消息:RocketMQ通过发送Half消息、处理本地事务、提交或回滚消息来实现分布式事务,参与的对象只有生产端,消费端失败会导致分布式事务失效
  • 延迟消息:可通过设置定时或者延迟多长时间消费消息。

示例

同步发送消息

不额外创建线程,阻塞调用,直到返回消息发送的结果

package mq.producer;

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.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;

import java.io.UnsupportedEncodingException;

public class SyncProducer {
    /**
     * 同步消息发送
     *
     * @param args
     * @throws MQClientException
     * @throws MQBrokerException
     * @throws RemotingException
     * @throws InterruptedException
     * @throws UnsupportedEncodingException
     */
    public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException, UnsupportedEncodingException {
        System.out.println("SyncProducer start......");
        DefaultMQProducer defaultMQProducer = new DefaultMQProducer("pg_sync_01");
        defaultMQProducer.setNamesrvAddr("localhost:9876");
        defaultMQProducer.start();

        SendResult sendResult = defaultMQProducer.send(new Message("message_sync_01",
                "hello this is sync message!".getBytes(RemotingHelper.DEFAULT_CHARSET)));
        System.out.println(sendResult);
        defaultMQProducer.shutdown();
        System.out.println("SyncProducer end......");

    }
}

异步发送消息

额外创建线程,不阻塞,可通过回调方法处理消息发送的结果

package mq.producer;

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.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;

import java.io.UnsupportedEncodingException;

public class AsyncProducer {
    /**
     * 异步消息发送
     *
     * @param args
     * @throws MQClientException
     * @throws MQBrokerException
     * @throws RemotingException
     * @throws InterruptedException
     * @throws UnsupportedEncodingException
     */
    public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException, UnsupportedEncodingException {
        System.out.println("AsyncProducer start......");
        DefaultMQProducer defaultMQProducer = new DefaultMQProducer("pg_async_01");
        defaultMQProducer.setNamesrvAddr("localhost:9876");
        defaultMQProducer.start();

        defaultMQProducer.send(new Message("message_async_01",
                "hello this is async message!".getBytes(RemotingHelper.DEFAULT_CHARSET)), new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println("success " + sendResult);
            }

            @Override
            public void onException(Throwable throwable) {
                System.out.println("error " + throwable);

            }
        });
        System.out.println("AsyncProducer end......");

    }
}

分区有序消息

package mq.producer;

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.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;

import java.io.UnsupportedEncodingException;
import java.util.List;

public class OrderProducer {
    /**
     * 分区有序消息发送
     *
     * @param args
     * @throws MQClientException
     * @throws MQBrokerException
     * @throws RemotingException
     * @throws InterruptedException
     * @throws UnsupportedEncodingException
     */
    public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException, UnsupportedEncodingException {
        System.out.println("OrderProducer start......");
        DefaultMQProducer defaultMQProducer = new DefaultMQProducer("pg_order_01");
        defaultMQProducer.setNamesrvAddr("localhost:9876");
        defaultMQProducer.start();
        //根据arg来指定相同的分区
        Integer arg = 313;
        Message message = new Message("message_order_01",
                "hello this is async message!".getBytes(RemotingHelper.DEFAULT_CHARSET));
        defaultMQProducer.send(message, new MessageQueueSelector() {
            @Override
            public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                Integer id = (Integer) arg;
                int index = id % mqs.size();
                return mqs.get(index);
            }
        }, arg);
        System.out.println("OrderProducer end......");

    }
}

单向消息发送

额外创建线程,不阻塞,不关注消息发送的结果

package mq.producer;

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.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;

import java.io.UnsupportedEncodingException;

public class OnewayProducer {
    /**
     * 单向消息发送
     *
     * @param args
     * @throws MQClientException
     * @throws MQBrokerException
     * @throws RemotingException
     * @throws InterruptedException
     * @throws UnsupportedEncodingException
     */
    public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException, UnsupportedEncodingException {
        System.out.println("OnewayProducer start......");
        DefaultMQProducer defaultMQProducer = new DefaultMQProducer("pg_oneway_01");
        defaultMQProducer.setNamesrvAddr("localhost:9876");
        defaultMQProducer.start();

        defaultMQProducer.sendOneway(new Message("message_oneway_01",
                "hello this is oneway message!".getBytes(RemotingHelper.DEFAULT_CHARSET)));
        System.out.println("OnewayProducer end......");
        defaultMQProducer.shutdown();

    }
}

发送延迟消息

可通过设置Message的DelayTimeLevel来指定消息消费的延迟级别

延迟级别从1开始对应1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h

package mq.producer;

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.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;

import java.io.UnsupportedEncodingException;

public class DelayProducer {
    /**
     * 延迟消息发送
     *
     * @param args
     * @throws MQClientException
     * @throws MQBrokerException
     * @throws RemotingException
     * @throws InterruptedException
     * @throws UnsupportedEncodingException
     */
    public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException, UnsupportedEncodingException {
        System.out.println("DelayProducer start......");
        DefaultMQProducer defaultMQProducer = new DefaultMQProducer("pg_delay_01");
        defaultMQProducer.setNamesrvAddr("localhost:9876");
        defaultMQProducer.start();
        Message message = new Message("message_delay_01",
                "hello this is delay message!".getBytes(RemotingHelper.DEFAULT_CHARSET));
        message.setDelayTimeLevel(3);
        SendResult sendResult = defaultMQProducer.send(message);
        System.out.println(sendResult);
        System.out.println("DelayProducer end......");
        defaultMQProducer.shutdown();

    }
}

事务消息

  1. 发送Half消息到Broker,Broker将其queueOffset设置为0,消费者对这条消息不可见。
  2. 本地事务执行成功,发送COMMIT消息到Broker,Broker将queueOffset设置为正常值,此时消费者可以正常消费
  3. 处理失败则发送ROLLBACK给Broker,Broker将Half消息删除。
package mq.producer;

import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.*;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;

import java.io.UnsupportedEncodingException;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TransactionProducer {
    /**
     * 事务消息发送
     *
     * @param args
     * @throws MQClientException
     * @throws MQBrokerException
     * @throws RemotingException
     * @throws InterruptedException
     * @throws UnsupportedEncodingException
     */
    public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException, UnsupportedEncodingException {
        System.out.println("TransactionProducer start......");
        TransactionMQProducer transactionProducer = new TransactionMQProducer("pg_transaction_01");
        TransactionListener transactionListener = new TransactionListener() {
            private AtomicInteger transactionIndex = new AtomicInteger(0);
            private ConcurrentHashMap<String, Integer> localTrans = new ConcurrentHashMap<>();

            @Override
            public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
                int value = transactionIndex.getAndIncrement();
                int status = value % 3;
                localTrans.put(msg.getTransactionId(), status);
                return LocalTransactionState.UNKNOW;
            }

            @Override
            public LocalTransactionState checkLocalTransaction(MessageExt msg) {
                Integer status = localTrans.get(msg.getTransactionId());
                if (status == null) return LocalTransactionState.COMMIT_MESSAGE;

                switch (status) {
                    case 0:
                        return LocalTransactionState.UNKNOW;
                    case 1:
                        return LocalTransactionState.COMMIT_MESSAGE;
                    case 2:
                        return LocalTransactionState.ROLLBACK_MESSAGE;
                }
                return LocalTransactionState.COMMIT_MESSAGE;
            }
        };
        transactionProducer.setTransactionListener(transactionListener);
        //线程池
        ExecutorService executorService = new ThreadPoolExecutor(2, 5, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2000), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "client-transaction-msg-check-thread");
            }
        });
        transactionProducer.setExecutorService(executorService);
        transactionProducer.setNamesrvAddr("localhost:9876");
        transactionProducer.start();

        Message message = new Message("message_transaction_01",
                "hello this is  transaction message!".getBytes(RemotingHelper.DEFAULT_CHARSET));
        SendResult sendResult = transactionProducer.sendMessageInTransaction(message, 1);
        SendResult sendResult2 = transactionProducer.sendMessageInTransaction(message, 2);
        System.out.println(sendResult);
        System.out.println(sendResult2);

        for (int i = 0; i < 100000; i++) {
            Thread.sleep(1000);
        }
        transactionProducer.shutdown();

    }
}