RocketMQ的使用
消费方式
<dependencies>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.8.0</version>
</dependency>
</dependencies>
生产者
/**
* 生产者
*/
public class SenderTest {
//演示消息同步发送
public static void main(String[] args) throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
//生产者
DefaultMQProducer producer = new DefaultMQProducer("hello-producerGroup");
//设置name server地址
producer.setNamesrvAddr("127.0.0.1:9876");
//设置队列数量为2,有4个,根据情况设置
producer.setDefaultTopicQueueNums(2);
//启动
producer.start();
// for (int i = 0 ; i < 16 ; i++){
Message message = new Message();
//消息主题
message.setTopic("hello-topic");
//消息标签
message.setTags("sms");
//添加内容
message.setBody(("我是消息").getBytes(CharsetUtil.UTF_8));
//执行发送
SendResult result = producer.send(message);
//打印结果
System.out.println(result);
// }
producer.shutdown();
}
}
//消息发送者
public class ConsumerTest {
public static void main(String[] args) {
try {
// 实例化消息生产者Producer
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer
("hello-consumergroup");
// 设置NameServer的地址
consumer.setNamesrvAddr("127.0.0.1:9876");
//设置消费模式
//consumer.setMessageModel(MessageModel.CLUSTERING); //默认是集群
consumer.setMessageModel(MessageModel.BROADCASTING); //默认是集群
//从最开始的位置开始消费
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
// 订阅一个或者多个Topic,以及Tag来过滤需要消费的消息
//和发送者保持一致才能搜到消息
consumer.subscribe("hello-topic", "sms");
// 注册回调实现类来处理从broker拉取回来的消息
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
msgs.forEach(message->{
System.out.println(message+" ; "+new String(message.getBody(), CharsetUtil.UTF_8));
});
//System.out.printf("%s 成功搜到消息: %s %n", Thread.currentThread().getName(), msgs);
// 标记该消息已经被成功消费 ack机制
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
// 启动Producer实例
consumer.start();
}catch (Exception e){
e.printStackTrace();
}
}
}
运行两个consumer,分别测试集群和广播消费模式
1.普通消息
1.1.同步发送
同步消息是发送者发送消息,需要等待结果的返回,才能继续发送第二条消息,这是一种阻塞式模型,虽然消息可靠性高,但是阻塞导致性能低。API : SendResult result = producer.send(message); 代码示例:
发送者代码
public class Producer {
//演示消息同步发送
public static void main(String[] args) throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
//生产者
DefaultMQProducer producer = new DefaultMQProducer("syn-producerGroup");
//设置name server地址
producer.setNamesrvAddr("127.0.0.1:9876");
//设置队列数量为2,默认为4,根据情况设置
producer.setDefaultTopicQueueNums(2);
//启动
producer.start();
for (int i = 0 ; i < 16 ; i++){
Message message = new Message();
//消息主题
message.setTopic("syn-topic");
//消息标签
message.setTags("sms");
//添加内容
message.setBody((i+"我是消息").getBytes(CharsetUtil.UTF_8));
//执行发送
SendResult result = producer.send(message);
//打印结果
System.out.println(result);
}
producer.shutdown();
} }
同步发送使用 SendResult result = producer.send(message); 方法即可,马上可以拿到返回值。SendResult 结果如下
SendResult [
sendStatus=SEND_OK, msgId=C0A8006516B018B4AAC270EF9D940000,offsetMsgId=C0A8006500002A9F0000000000008E1C,
messageQueue=MessageQueue [topic=syn-topic, brokerName=LAPTOP-20VLGCRC, queueId=3], queueOffset=0]
-
SendStatus : 状态OK
-
msgId: 发送者生成的ID
-
OffsetMsgId : 由Broker生成的消息ID
-
MessageQueue :队列信息
1.2.异步发送
异步消息是发送者发送消息,无需等待发送结果就可以再发送第二条消息,它是通过回调的方式来获取到消息的发送结果,消息可靠性高,性能也高。API : producer.send(message,SendCallback) 示例代码:
. . . 省略. . .
producer.send(
//创建消息对象
new Message("asyn-topic", "sms", "我是消息".getBytes(CharsetUtil.UTF_8)),
//添加发送回调
new SendCallback() {
//发送成功结果处理
@Override
public void onSuccess(SendResult sendResult) {
System.out.println(sendResult);
}
//发送异常结果处理
@Override
public void onException(Throwable throwable) {
System.out.println("发送异常:"+throwable.getMessage());
}
}
);
SendCallback 是消息发送结果回调。如果:sendResult.getSendStatus() == SendStatus.SEND_OK 表示成功
1.3.单向发送
这种方式指的是发送者发送消息后无需等待Broker的结果返回,Broker也不会返回结果,该方式性能最高,但是消息可靠性低。API : producer.sendOneway(message) 示例代码:
... 省略...
Message message = new Message("asyn-topic", "sms", "我是消息".getBytes(CharsetUtil.UTF_8));
producer.sendOneway(message);
sendOneway 单向发送是没有返回结果值的。
1.5.总结
下面对三种发送方式做一个对比
-
可靠性最高: 同步发送 > 异步发送 > 单向发送
-
性能最高:单向发送 > 异步发送 > 同步发送使用场景建议如下
-
如果是比较重要的不可丢失的消息,且对时效性要求不高建议使用同步发送,如转账消息
-
如果是不重要的可失败的消息,比如日志消息,建议使用单向发送
-
如果对时效性要求比较高,且消息不能丢失,可以尝试使用异步发送
2.顺序消息
在某些业务场景下是需要消息按照顺序进行消费,比如一个账户的加钱,减钱的动作必须按照时间先后去执行,否则就会发生金额不够导致操作失败。
按照发送的顺序进行消费就是顺序消息,遵循(FIFO), 默认生产者以Round Robin轮询方式把消息发送到不同的Queue分区队列;消费者从多个队列中消费消息,这种情况没法保证顺序。
2.1.全局有序
全局有序是一个topic下的所有消息都要保证顺序,如果要保证消息全局顺序消费,就需要保证使用一个队列存放消息,一个消费者从这一个队列消费消息就能保证顺序,即:单线程执行,消费完了再去拉取producer.setDefaultTopicQueueNums(1);来指定队列数量。
1.发送者
可以通过代码指定创建1个队列即可
producer.setDefaultTopicQueueNums(1);
2.消费者
使用一个线程,一次只拉取一个消息 , 使用 MessageListenerOrderly 有序的消费消息。
//最大线程1个
defaultMQPushConsumer.setConsumeThreadMax(1);
defaultMQPushConsumer.setConsumeThreadMin(1);
//同时只拉取一个消息
defaultMQPushConsumer.setPullBatchSize(1);
.