注意:
- 相同group的消费客户端,属于同一个集群。相同group下的topic和tag必须一致,不然信息会丢失。
- 消费端的group和生产者的group没有直接关系,可以不同。
2.1 demo
2.1.1 pom.xml(我部署的mq为4.4.0版本)
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.4.0</version>
</dependency>
2.1.2 application.properties
server.port=8082
rocketmq.producer.groupName=groupOne
rocketmq.producer.namesrvAddr=localhost:9876
2.1.3 product
@Service
public class ProducterService {
@Value("${rocketmq.producer.namesrvAddr}")
private String nameAddr;
@Value("${rocketmq.producer.groupName}")
private String group;
private DefaultMQProducer defaultMQProducer;
@PostConstruct
public void getProducer() {
{
defaultMQProducer = new DefaultMQProducer(group);
defaultMQProducer.setNamesrvAddr(nameAddr);
}
}
/*同步消费模式*/
public void defaultMQProducer() {
try {
defaultMQProducer.start();
for(int i = 0;i < 10; i++) {
Message message = new Message("topic-one","test","key"+i, ("sync"+i).getBytes());
// message.setDelayTimeLevel(3);//延迟发送
SendResult result = defaultMQProducer.send(message);
System.out.println("send msgId " + result.getMsgId() + " resultStatus:" + result.getSendStatus());
}
defaultMQProducer.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.1.4 Consumer
@Service
public class ConsumerService {
@Value("${rocketmq.producer.namesrvAddr}")
private String nameAddr;
@Value("${rocketmq.producer.groupName}")
private String group;
6.3 消费者
/**
* 无序消费
* 和defultConsumer2的conusmer group,topic和tag一致是同一个集群
* */
@PostConstruct
public void defultConsumer() throws MQClientException {
int max = 2;
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(group);
consumer.setNamesrvAddr(nameAddr);
consumer.subscribe("topic-one","test||pre");
consumer.setInstanceName("one");
/*设置消费失败后的重试次数,重试指定次数还是失败后不成功,则被放入死信队列,需要人工干预*/
consumer.setMaxReconsumeTimes(max);
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg: msgs) {
try {
System.out.println("******消费 One:" + new String(msg.getBody(), "utf-8") + " key is:" +msg.getKeys());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}finally {
if(msg.getReconsumeTimes() == max) {
System.out.println("到了最大重试次数了");
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
}
}
2.2分类
2.2.1 异步发送消息
- 异步发送是指发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式
- 消息发送后,会另起线程执行SendCallback里的方法
- 上文中producer里的方法改为如下,其他一致
/**
* 异步发送消息
* */6.3 消费者
public void asyncMqProducer() throws MQClientException, RemotingException, InterruptedException {
defaultMQProducer.start();
defaultMQProducer.setRetryTimesWhenSendAsyncFailed(0);
for(int i=0;i<10;i++) {
Message message = new Message("topic-one","test","OrderID"+i,("async"+i).getBytes());
System.out.println("product ready to send message "+ i);
defaultMQPr6.oducer.send(message, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("async success msg:"+sendResult.getSendStatus());
}
@Override
public void onException(Throwable e) {
System.out.println("async failed msg:"+e.getMessage());
}
});
}
Thread.sleep(10000);
defaultMQPro6.ducer.shutdown();
}
2.2.2 单向发送消息
/**
* 单向发送消息
* */
public void defaultOneWayMQProducer() {
try {
defaultMQProducer.start();
for(int i = 0;i < 10; i++) {
Message message = new Message("topic-one","test","key"+i, ("sync"+i).getBytes());
defaultMQProducer.sendOneway(message);
}
defaultMQ6.Producer.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
2.2.3顺序消息
- 通过轮询所有队列来发送的(负载均衡策略),orderId 相同的发送到同一个队列
- 同一个队列里的消息消费是有序的,非同队列中的消息消费无序
- Producer
/**
* 顺序消息
* 通过轮询所有队列来发送的(负载均衡策略)
* 消费端使用MessageListenerConcurrently
* */
public void orderMqProducer() throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
defaultMQProducer.start();
for (int i = 1; i< 11; i++) {
int orderId = i % 2;
Message message = new Message("topic-one", "test","OrderID"+i,("orderMqProducer"+i).getBytes());
System.out.println("orderMqProducer ready to send message "+ i);
SendResult result = 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);
}
},orderId );
System.out.println("orderMqProducer send msgId " + result.getMsgId() + " resultStatus:" + result.getSendStatus());
}
defaultMQProducer.shutdown();
}
2.Consumer
/**
* 顺序消费
* 同一个队列里的信息顺序消费
* 只是针对集群消费
* */
@PostConstruct
public void defultConsumerOrderly() throws MQClientException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(group);
consumer.setNamesrvAddr(nameAddr);
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe("topic-one","test");
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
for (MessageExt msg: msgs) {
try {
System.out.println("###defultConsumerOrderly msg:" + new String(msg.getBody(), "utf-8") + " key is:" +msg.getKeys());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();6.3 消费者
}
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
consumer.start();
}
2.2.4批量消费
/**
* 批量发送的要求
* 1.同一批次的信息要有同样的topic
* 2.同样的waitStoreMsgOK
* 3.不是计划消息
* 4.同一批次所有的消息大小不超过1M
* */
public void BatchMqProducer(){
try {
defaultMQProducer.start();
AtomicInteger integer = new AtomicInteger();
System.out.println(integer.getAndIncrement());
List<Message> messages = new ArrayList<>();
for (int i = 1; i< 2; i++) {
Message message = new Message("topic-one", "test","OrderID"+i,("BatchMqProducer"+i).getBytes());
System.out.println("BatchMqProducer ready to send message "+ i);
messages.add(message);
}
SendResult result = defaultMQProducer.send(messages);
System.out.println( "BatchMqProducer resultStatus:" + result.getSendStatus());
defaultMQProducer.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
2.2.5事务消费
- 生产者发送Prepared消息,会拿到消息的地址
- 执行本地事物(executeLocalTransaction),执行成功或者失败会通过第一阶段拿到的地址去访问消息,并修改状态。
- RocketMQ轮训发现`Prepared消息`时,会回查事务(checkLocalTransaction)。
注意:
- rocektmq 4.4支持事务回查
- 并且需要在broker配置中配置:”checkTransactionMessageEnable=true”
- 事务回查没有完成之前不要执行producer.shutdown,否则执行回查的线程池会关闭。
1 TransactionListenerImpl
public class TransactionListenerImpl implements TransactionListener {
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
return LocalTransactionState.COMMIT_MESSAGE;
}
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
System.out.println("***********checkLocalTransaction################");
return LocalTransactionState.COMMIT_MESSAGE;
}
}
2 生产者
/**
* 事务消息
* */
public void DefultTransactionProducer() throws MQClientException, InterruptedException {
TransactionMQProducer producer = new TransactionMQProducer(group);
producer.setNamesrvAddr(nameAddr);
TransactionListener listener = new TransactionListenerImpl();
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("transaction-thread");
return thread;
}
});
producer.setExecutorService(executorService);
producer.setTransactionListener(listener);
producer.start();
for (int i = 0; i< 10; i++) {
Message message = new Message("topic-one", "TagA","OrderID"+i,("DefultTransactionProducer"+i).getBytes());
TransactionSendResult result = producer.sendMessageInTransaction(message,null);
Thread.sleep(10);
}
for (int i = 0; i < 100; i++) {
Thread.sleep(1000);
}
producer.shutdown();
}
3 消费者
/**
* 无序消费
* */
@PostConstruct
public void defultConsumer() throws MQClientException {
int max = 2;
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(group);
consumer.setNamesrvAddr(nameAddr);
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe("topic-one","*");
consumer.setInstanceName("one");
/*设置消费失败后的重试次数,重试指定次数还是失败后不成功,则被放入死信队列,需要人工干预*/
consumer.setMaxReconsumeTimes(max);
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg: msgs) {
try {
System.out.println("******消费 One:" + new String(msg.getBody(), "utf-8") + " key is:" +msg.getKeys() +" tag:"+msg.getTags() +" topic:"+msg.getTopic() );
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}finally {
if(msg.getReconsumeTimes() == max) {
System.out.println("到了最大重试次数了");
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
}