DefaultMQProducer producer = new DefaultMQProducer("ProducerGroupName");
producer.start();
for (int i = 0; i < 128; i++)
try {
{
Message msg = new Message("TopicTest",
"TagA",
"OrderID188",
"Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = producer.send(msg);
System.out.printf("%s%n", sendResult);
}
} catch (Exception e) {
e.printStackTrace();
}
producer.shutdown();
Message msg = new Message("Jodie_topic_1023",
"TagA",
"OrderID188",
"Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
producer.send(msg, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
countDownLatch.countDown();
System.out.printf("%-10d OK %s %n", index, sendResult.getMsgId());
}
@Override
public void onException(Throwable e) {
countDownLatch.countDown();
System.out.printf("%-10d Exception %s %n", index, e);
e.printStackTrace();
}
});
和普通消息相比send的时候多设置了一个SendCallback
指定
成功了回调:onSuccess
失败了回调:onException
发送消息调用
producer.sendOneway(msg);
只管发送消息没有返回值也没有回调
很多时候消息之间可能是存在顺序的
比如一个订单的多种状态流转,如果异步来做还是希望消息按顺序接受
发送端要把有先后关系的消息发送到同一个MessageQueue
String[] tags = new String[] {"TagA", "TagB", "TagC", "TagD", "TagE"};
for (int i = 0; i < 100; i++) {
int orderId = i % 10;
Message msg =
new Message("TopicTestjjj", tags[i % tags.length], "KEY" + i,
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = producer.send(msg, 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);
}
}, orderId);
}
在MessageQueueSelector实现里实现了通过orderId选择MessageQueue的逻辑
既然已经在一个队列里了那么消费者就按顺序消费即可
其实是消费者对MessageQueue上锁并拉取批量消息消费
consumer.registerMessageListener(new MessageListenerOrderly() {
AtomicLong consumeTimes = new AtomicLong(0);
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
//业务处理
return ConsumeOrderlyStatus.SUCCESS;
}
});
注册的类型为MessageListenerOrderly
consumer.setMessageModel(MessageModel.BROADCASTING);
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
设置消息类型为广播、注册ConsumeConcurrentlyContext 异步处理消息监听
此时管理offset改为broker进行管理,所有注册的consumer处理完了就把offset增加
消息发送到Broker后先把消息存储到一个系统队列中,有对应的定时任务定时拉取该被消费的消息给对应的业务group
// Instantiate a producer to send scheduled messages
DefaultMQProducer producer = new DefaultMQProducer("ExampleProducerGroup");
// Launch producer
producer.start();
int totalMessagesToSend = 100;
for (int i = 0; i < totalMessagesToSend; i++) {
Message message = new Message("TestTopic", ("Hello scheduled message " + i).getBytes());
// This message will be delivered to consumer 10 seconds later.
message.setDelayTimeLevel(3);
// Send the message
producer.send(message);
}
// Shutdown producer after use.
producer.shutdown();
主要就是设置了延时类型message.setDelayTimeLevel(3);
这个3指的是第三类型的延时时间,在配置中可以看类型和延时时间的关系
默认为:
messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
减少网络IO
但是有要求:
DefaultMQProducer producer = new DefaultMQProducer("BatchProducerGroupName");
producer.start();
//If you just send messages of no more than 1MiB at a time, it is easy to use batch
//Messages of the same batch should have: same topic, same waitStoreMsgOK and no schedule support
String topic = "BatchTest";
List<Message> messages = new ArrayList<>();
messages.add(new Message(topic, "Tag", "OrderID001", "Hello world 0".getBytes()));
messages.add(new Message(topic, "Tag", "OrderID002", "Hello world 1".getBytes()));
messages.add(new Message(topic, "Tag", "OrderID003", "Hello world 2".getBytes()));
producer.send(messages);
可以通过
这些过滤条件虽然是consumer设置的
但是其实处理的是broker
这样可以节省带宽
consumer.subscribe("TagFilterTest", "TagA || TagC");
消费端设置监听的tag,其他类型不处理
consumer.subscribe("SqlFilterTest",
MessageSelector.bySql("(TAGS is not null and TAGS in ('TagA', 'TagB')) and (a is not null and a between 0 and 3)"));
消费端按SQL的where部分设置监听
只保证了发送端的事务
消费端可以靠MQ本来就有的最终一致性保证
消息投递状态:
LocalTransactionState.UNKNOW:未知
LocalTransactionState.COMMIT_MESSAGE:成功
LocalTransactionState.ROLLBACK_MESSAGE:失败
消费端监听消息处理为TransactionListener
有2个方法
executeLocalTransaction:提交消息后立即执行,返回消息投递状态
checkLocalTransaction: 如果立即执行返回的状态是未知,则进入阶梯等待时间回调此方法,共15次,可在这个方法里定制事务检查策略,如查表等方案
限制:不支持批量和延时消息
public static void main(String[] args) throws Exception {
//自定义线程池 专门处理事物回查、不过不设置则自动创建一个
ExecutorService executorService = new ThreadPoolExecutor(2, 5, 100, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(2000), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("client-transaction-msg-check-thread");
return thread;
}
});
//这里发送端改了 是事物专用的发送端
TransactionMQProducer producer = new TransactionMQProducer("lz_demo_test_group");
producer.setNamesrvAddr("注册中心地址和端口"); //(2)
producer.setExecutorService(executorService);
producer.setTransactionListener(new TransactionListenerImpl());
producer.start();
String[] tags = new String[] {"TagA", "TagB", "TagC", "TagD", "TagE"};
for (int i = 0; i < 10; i++) {
try {
Message msg = new Message("TopicTest", tags[i % tags.length], "KEY" + i,
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = producer.sendMessageInTransaction(msg, null);
System.out.printf("%s%n", sendResult);
Thread.sleep(10);
} catch (MQClientException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 100000; i++) {
Thread.sleep(1000);
}
producer.shutdown();
}
static class TransactionListenerImpl implements TransactionListener {
private AtomicInteger transactionIndex = new AtomicInteger(0);
private ConcurrentHashMap<String, Integer> localTrans = new ConcurrentHashMap<>();
/**
* 半事物消息提交给MQ后 执行此方法
* 官方建议在这里 执行本地事物
* 可能需要行为参数化
*/
@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 (null != status) {
switch (status) {
case 0:
return LocalTransactionState.UNKNOW;
case 1:
return LocalTransactionState.COMMIT_MESSAGE;
case 2:
return LocalTransactionState.ROLLBACK_MESSAGE;
default:
return LocalTransactionState.COMMIT_MESSAGE;
}
}
return LocalTransactionState.COMMIT_MESSAGE;
}
}