简介
RocketMQ是一款分布式、队列模型的消息中间件,是阿里巴巴集团自主研发的专业消息中间件,借鉴参考了JMS规范的MQ实现,更参考了优秀的开源消息中间件KAFKA,实现了业务消峰、分布式事务的优秀框架。
为什么选择RocketMQ
在使用越来越多的队列和虚拟主题的情况下,ActiveMQ IO模块遇到了瓶颈。我们尽力通过节流,断路器或降级来解决此问题,但效果不佳。因此,我们那时开始关注流行的消息传递解决方案Kafka。不幸的是,Kafka不能满足我们的要求,特别是在低延迟和高可靠性方面。
在这种情况下,一个新的消息传递引擎来处理更广泛的用例集,从传统的发布/订阅方案到大批量实时零损失容忍交易系统。
RocketMQ中主要名词解释
Message:消息,在MQ中进行信息传递的载体。
Producer:生产者,负责消息的生产并发送消息。
Consumer:消费者,用户接收生产者发的消息。
RocketMq特性介绍
RockectMq三种消息发送方式
第一种同步消息发送:同步发送就是指 producer 发送消息后,会在接收到 broker 响应后才继续发下一条消息的通信方式。
java代码示例生产者发送:
/**
* 同步消息发送
* @data 2020/03/16
*/
public static void main(String[] args) throws Exception {
//用生产者组名称实例化
DefaultMQProducer producer = new DefaultMQProducer("Test");
//指定名称服务器地址
producer.setNamesrvAddr("127.0.0.1:9876");
//启动实例
producer.start();
//创建一个消息实例,指定主题,标签和消息正文
for (int i = 0; i < 100; i++) {
Message msg = new Message("TogicTest", "TagA", ("Hello RocketMq" + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
//呼叫发送消息以将消息传递给其中一个代理
SendResult sendResult = producer.send(msg);
}
//一旦不再使用生产者实例,请关闭
producer.shutdown();
}
第二种异步消息发送:异步发送是指 producer 发出一条消息后,不需要等待 broker 响应,就接着发送下一条消息的通信方式。需要注意的是,不等待 broker 响应,并不意味着 broker 不响应,而是通过回调接口来接收 broker 的响应。
java代码示例生产者发送:
/**
* 异步消息发送
* @param args
* @data 2020/03/16
*/
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("Jodie_Daily_test");
producer.start();
producer.setRetryTimesWhenSendAsyncFailed(0);
for (int i = 0; i < 10000000; i++) {
try {
final int index = i;
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) {
System.out.printf("%-10d OK %s %n", index, sendResult.getMsgId());
}
@Override
public void onException(Throwable e) {
System.out.printf("%-10d Exception %s %n", index, e);
e.printStackTrace();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
producer.shutdown();
}
第三种单向消息发送: producer 只负责发送消息,不等待 broker 发回响应结果。
java代码示例生产者发送:
/**
* 单向消息发送
* @data 2020/03/16
*/
public static void main(String[] args) throws Exception{
DefaultMQProducer producer = new DefaultMQProducer("Test");
producer.start();
for (int i = 0; i < 100; i++) {
//创建一个消息实例,指定主题,标签和消息正文。
Message msg = new Message("TopicTest" /* Topic */, "TagA" /* Tag */, ("Hello RocketMQ " +i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
);
//呼叫发送消息以将消息传递给其中一个代理。
producer.sendOneway(msg);
}
//一旦不再使用生产者实例,请关闭。
producer.shutdown();
}
java代码示例消费者接收:
/**
* 普通消费方式
* @data 2020/03/16
*/
public static void main(String[] args) throws Exception {
// 使用consumer group初始化DefaultMQPushConsumer
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name");
// 指定namesrv
consumer.setNamesrvAddr("localhost:9876");
// 订阅topic
consumer.subscribe("TopicTest", "*");
// 注册消费消息的回调
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;
}
});
// 启动consumer
consumer.start();
System.out.printf("Consumer Started.%n");
}
集群消费&广播消费
集群消费方式:Consumer实例平均分摊消费生产者发送的消息。
/**
* 广播消息发送
* @data 2020/03/16
*/
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("test");
producer.start();
for (int i = 0; i < 100; i++) {
Message msg = new Message("TopicTest" /* Topic */, "TagA" /* Tag */, ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET))/* Message body */;
producer.send(msg);
}
producer.shutdown();
}
顺序消息
/**
* 顺序发送消息
* @data 2020/03/16
*/
public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("test");
producer.start();
String[] tags = new String[]{"TagA", "TagB", "TagC", "TagD", "TagE"};
for (int i = 0; i < 100; i++) {
int orderId = i % 0;
Message msg = new Message("TopicTest" /* Topic */, "TagA" /* Tag */, ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET);
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);
}
producer.shutdown();
}
java代码示例消费者:
/**
* 顺序消息接收
* @data 2020/03/16
*/
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("test");
consumer.setNamesrvAddr("127.0.0.1:9876");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe("TopicTestjjj", "*");
consumer.registerMessageListener(new MessageListenerOrderly() {
AtomicLong consumeTimes = new AtomicLong(0);
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> mags, ConsumeOrderlyContext context) {
context.setAutoCommit(false);
return ConsumeOrderlyStatus.SUCCESS;
}
});
consumer.start();
System.out.printf("Consumer Started.%n");
}
常见问题列表
- 如果这个Consumer ID是第一次启动,则会忽略启动之前发送的消息,也就是忽略历史消息,从启动之后发送的消息开始消费。
- 如果这个Consumer ID是第二次启动,那么从上次消费的位置开始消费。
- 如果想从特定位置开始消费,可以通过MQ控制台的消费位点重置功能,指定到具体的时间开始消费。毎次重置只针对特定Consumer ID下的特定Topic,不会影响其他Consumer ID。
- 集群消费方式:消费业务逻辑代码如果返回RECONSUME_LATER,或者NULL,或者抛出异常,消息都会走重试流程,至多重试16次,如果重试16次后,仍然失败,则消息进入死信队列。可以通过调用message.getReconsumeTimesO方法来获取消息的重试次数。
- 广播消费方式:广播消费方式仍然能保证一条消息至少被消费一次,但消费失败后不做重试操作。
- 确定是否发送
- 使用Topic按时间范围进行查询,可以查询到一段时间内某Topic收到的所有消息
- 使用Topic和Message ID对消息进行精准査询
- 使用Topic和Message Key较为精准地査询具有相同Message Key的一类消息
- 如果消息存在,可以重推
- 如果不存在,客户端没有发送成功
- 绝大多数情况下,消息是不重复的。作为一款分布式消息中间件,在网络抖动、应用处理超时等异常情况下,无法保证消息不重复,但是能保证消息不丢失。
- 默认是4M
- 不建议频繁发送大于1M以上的消息,最大不要超过4M消息
- 更大的消息建议通过分布式文件存储保存,通过消息发送文件路径作为通