持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情
背景
当系统发送的消息过多,每次发送消息都需要和MQ建立连接,对性能造成一定的影响,为了解决这个问题,RocketMq提供了批量发送消息机制,从而提升消息发送的性能。
概述
批量发送消息就是将消息进行打包批量发送,但是RocetMQ发送批量消息有如下的相关限制
- 1.所有批量发送的消息的topic必须相同,且消息不能为延时消息
- 2.一批批量发送消息的大小不能超过4M.
生产者消息大小计算
Producer发送消息Message的结构如下图:
生产者的消息由四部分组成:Topic、消息Body、消息日志 (占20字节)及用于描述消息的一堆属性key-value;这些属性中包含例如生产者地址、生产时间、 要发送的QueueId等,生产者通过send()方法将消息生成一个字符串发送给Broker。
具体实现
生产者
public void sendBatchMessage() throws UnsupportedEncodingException
{
List<Message> msgs = new ArrayList<>();{
for (int i = 0; i < 5; i++)
{
Message msg = new Message("test-batch-rocketmq",("Hello Message:" + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
msgs.add(msg);
}
rocketMQTemplate.convertAndSend("test-batch-rocketmq",msgs);
}
}
消费者
@Component
@RocketMQMessageListener(consumerGroup="test-batch-group",topic="test-batch-rocketmq",messageModel = MessageModel.CLUSTERING)
public class BatchConsumer implements RocketMQListener<String>
{
private Logger logger =LoggerFactory.getLogger(getClass());
@Override
public void onMessage(String message)
{
logger.info("send succss content is:{}", message);
}
}
测试结果
c.s.fw.mq.consumer.BatchConsumer - send succss content is:
[{
"topic": "test-batch-rocketmq",
"flag": 0,
"properties": {
"WAIT": "true"
},
"body": "SGVsbG8gTWVzc2FnZTow",
"transactionId": null,
"keys": null,
"tags": null,
"delayTimeLevel": 0,
"waitStoreMsgOK": true,
"buyerId": null
}]
说明:从输出结果来上看,批量发送消息已经成功。如果发送消息的大小超过了4M,则会报如下错误:
CODE: 13 DESC: the message body size over max value, MAX: 4096
解决方案
当发送的消息内容超过了4M的时候,可以采用如下方案:
- 修改MQ配置,调整生产者消费者发送消息的大小。
- 将消息切割成功多条消息发送。
修改MQ配置,调整生产者消费者发送消息的大小。
需要设置生产者和消费者maxMessageSize的大小即可
maxMessageSize: 5120
注意:maxMessageSize值设置的越大,Consumer每拉取一次需要的时间就会越长,且在网络上传输出现问题的可能性就越高;若在拉取过程中若出现了问题,那么本批次所有消息都需要全部重新拉取。对性能影响非常大,需要慎重考虑。
将消息切割成功多条消息发送。
消息切割工具类
public class MessageSplitUtil implements Iterator<List<Message>>
{
private final int MAX_SIZE = 4 * 1024 * 1024;
private final List<Message> messages;
//
private int currentIndex;
public MessageSplitUtil(List<Message> messages)
{
this.messages = messages;
}
@Override
public boolean hasNext()
{
return currentIndex < messages.size();
}
@Override
public List<Message> next()
{
int nextIndex = currentIndex;
// 记录当前要发送的这一小批次消息列表的大小
int totalSize = 0;
for (; nextIndex < messages.size(); nextIndex++)
{
// 获取当前遍历的消息
Message message = messages.get(nextIndex);
// 统计当前遍历的message的大小
int tmpSize = message.getTopic().getBytes().length + message.getBody().length;
Map<String, String> properties = message.getProperties();
for (Map.Entry<String, String> entry : properties.entrySet())
{
tmpSize += entry.getKey().getBytes().length + entry.getValue().length();
}
tmpSize = tmpSize + 20;
// 判断当前消息本身是否大于4M
if (tmpSize > MAX_SIZE) {
if (nextIndex - currentIndex == 0)
{
nextIndex++;
}
break;
}
// 当前消息的大小 + 之前统计要发送这一小批次消息列表的大小 》极限值4M
if (tmpSize + totalSize > MAX_SIZE)
{
break;
}
else
{
// 统计要发送这一小批次消息列表的大小
totalSize += tmpSize;
}
}
List<Message> subList = messages.subList(currentIndex, nextIndex);
currentIndex = nextIndex;
return subList;
}
}
说明:将消息进行切割成功多段返回。
生产者
public void sendBatchMessage2() throws UnsupportedEncodingException
{
List<Message> msgs = new ArrayList<>();{
for (int i = 0; i < 100; i++)
{
Message msg = new Message("test-batch-rocketmq",("Hello Message:" + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
msgs.add(msg);
}
MessageSplitUtil messageSplitUtil =new MessageSplitUtil(msgs);
while(messageSplitUtil.hasNext())
{
try
{
List<Message> messageItems=messageSplitUtil.next();
rocketMQTemplate.convertAndSend("test-batch-rocketmq",messageItems);
}
catch (Exception e)
{
logger.error("send batch message error",e);
}
}
}
}
消费者
@Component
@RocketMQMessageListener(consumerGroup="test-batch-group",topic="test-batch-rocketmq",messageModel = MessageModel.CLUSTERING)
public class BatchConsumer implements RocketMQListener<List<MessageExt>>
{
private Logger logger =LoggerFactory.getLogger(getClass());
@Override
public void onMessage(List<MessageExt> message)
{
for(MessageExt messageExt:message)
{
logger.info("message content:{}",messageExt);
}
}
}
总结
本文讲解了Spring Boot 整合RocketMq批量发送消息,结合实际的应用场景,批量发送消息能够提升消息发送的性能。