1、事务消息
1.1什么是事务消息
如果业务涉及到多个数据库(多个服务)的写操作,我们需要保证多个数据库同时提交或回滚,这种夸多个数据库的事务操作叫分布式事务。
1.2、业务流程
1.3代码实现:
事务监听器:
package com.jd.transaction;
import com.jd.User;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.TransactionListener;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
/**
* 事务监听器
* */
public class MyTransactionCheckListener implements TransactionListener {
@Override
/*execute Local Transaction 执行本地事务
* o指的是本地事务执行的时候 需要操作数据库的数据封装对象
* /message就是消费者消费的消息
* 当用户的年龄大于20岁 那么这个事务就会执行失败*/
public LocalTransactionState executeLocalTransaction(Message message, Object d) {
User user = (User) d;
if (user.getAge()>20){
/*ROLLBACK_MESSAGE 回滚消息 */
return LocalTransactionState.ROLLBACK_MESSAGE ;
}
/*COMMIT_MESSAGE 提交信息*/
return LocalTransactionState.COMMIT_MESSAGE ;
}
/*checkLocalTransaction 检查本地事务*/
@Override
public LocalTransactionState checkLocalTransaction(MessageExt messageExt) {
/*COMMIT_MESSAGE 提交信息*/
System.out.println("checkLocalTransaction检查本地事务");
return LocalTransactionState.COMMIT_MESSAGE;
}
}
生产者:
package com.jd.transaction;
import com.jd.Constants;
import com.jd.User;
import io.netty.util.CharsetUtil;
import org.apache.rocketmq.client.producer.TransactionMQProducer;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.common.message.Message;
/**
* 事务消息生产者
* @author Eric Chen
* @since 2022/6/23
*/
public class TransactionProducer {
public static void main(String[] args) throws Exception {
/* 1. 创建事务MQ生产者组 */
TransactionMQProducer producer = new TransactionMQProducer("transaction-producer-group");
/*2. 设置NameServer地址 : 如果实在安装不上,可以使用这个地址:115.159.88.63:9876*/
producer.setNamesrvAddr(Constants.NAME_SERVER_URL);
/*3.指定事务监听器*/
producer.setTransactionListener(new MyTransactionCheckListener());
/* 5、设置事务消息监听*/
producer.start();
/*6、创建消息 topic主题 tags 标签 keys钥匙*/
Message message = new Message("transactionTopic",
"transactionTag", "事务消息".getBytes(CharsetUtil.UTF_8));
/*6.发送事务消息*/
TransactionSendResult result = producer.sendMessageInTransaction(message, User.builder().age(2).build());
System.out.println(result);
}
}
消费者:
package com.jd.transaction;
import com.jd.Constants;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import java.io.UnsupportedEncodingException;
import java.util.List;
public class TransactionConsumer {
public static void main(String[] args) throws Exception {
//1.创建消费者组
final DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("transaction-consumer-group");
//2.设置NameServer地址
consumer.setNamesrvAddr(Constants.NAME_SERVER_URL);
//3. 订阅消息,指定tag标签 subscribe订阅s
consumer.subscribe("transactionTopic","transactionTag");
//4.注册消息监听器 MessageListenerConcurrently并发消息监听器
consumer.registerMessageListener(new MessageListenerConcurrently(){
/*匿名内部类 ConsumeConcurrentlyStatus 消费并发状态*/
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt msg : list) {
try {
String s = new String(msg.getBody(), RemotingHelper.DEFAULT_CHARSET);
System.out.println(s);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}
}
/*CONSUME_SUCCES消费成功*/
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//启动消费者
consumer.start();
System.out.println("消费者启动成功");
}
}
2、延迟消息
2.1什么是延迟消息:
把消息写到Broker后需要延迟一定时间才能被消费 , 在RocketMQ中消息的延迟时间不能任意指定,而是由特定的等级(1 到 18)来指定:
messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
可以通过修改配置来增加级别,比如在mq安装目录的 broker.conf 文件中增加
messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h 2d 这个时候总共就有19个level。
2.3使用场景:
单支付超时关单,VIP会员超时提醒。但是使用定时任务来处理这些业务场景在数据量大的时候并不是一个很好的选择,会造成大量的空扫描浪费性能。我们可以考虑使用延迟消息来解决。
2.4延迟消息内部工作流程图
代码案列:
消费者:
package com.jd.delay;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.util.List;
public class DelayConsumer {
public static void main(String[] args) throws Exception {
//1.创建消费者组
final DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("delay-consumer-group");
//2.设置NameServer地址consumer
consumer.setNamesrvAddr("115.159.88.63:9876");
//3. 订阅消息,指定tag标签 subscribe订阅s
consumer.subscribe("TopicDelay","TagDelay");
//4.注册消息监听器 MessageListenerConcurrently并发消息监听器
consumer.registerMessageListener(new MessageListenerConcurrently() {
/*匿名内部类 ConsumeConcurrentlyStatus 消费并发状态*/
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt msg : list) {
try {
//获取生产者内容 RemotingHelper.DEFAULT_CHARSET 相当于UTF-8
System.out.println(new String(msg.getBody(), RemotingHelper.DEFAULT_CHARSET));
System.out.println(LocalDateTime.now().toString());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
/*CONSUME_SUCCES消费成功*/
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//启动消费者
consumer.start();
System.out.println("消费者启动成功");
}
}
2、生产者:
package com.jd.delay;
import com.jd.Constants;
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 java.time.LocalDateTime;
/**
* 生产者例子 延迟消息
* @author Eric Chen
* @since 2022/6/23
*/
public class DelayProducer {
public static void main(String[] args) throws Exception {
/* 1. 创建producer组 */
DefaultMQProducer producer = new DefaultMQProducer("delay-producer-group");
/*2. 设置NameServer地址 : 如果实在安装不上,可以使用这个地址:115.159.88.63:9876*/
producer.setNamesrvAddr("115.159.88.63:9876");
/*3. 启动生产者 startr生产者RemotingHelper*/
producer.start();
/* 4.创建消息 topic主题 tags 标签 keys钥匙*/
Message message = new Message("TopicDelay",
"TagDelay",
/*RemotingHelper.DEFAULT_CHARSET相当于UTAF-8*/
"延迟消息".getBytes(RemotingHelper.DEFAULT_CHARSET));
message.setDelayTimeLevel(3);
/*5. 发送消息获取结果*/
SendResult result = producer.send(message);
//打印当前时间
System.out.println(LocalDateTime.now().toString());
/*6. 结束producer */
System.out.println("发送完毕"+result);
//关闭资源
producer.shutdown();
}
}