下载rocketmq压缩包,然后解压。
设置环境变量ROCKETMQ_HOME:解压后文件夹地址
设置环境变量NAMESRV_ADDR=localhost:9876
然后就可以测试是否可用。
可以在github上面找rocketmq-externals,打包里面的console项目,
然后运行起来,就可以在网页使用图形界面工具查看rocketmq运行情况(默认使用localhost:9876)
不同topic代表不同的mq通道,代表不同的消息队列
mq会把里面的消息复制多份,分别发送给各个group。也就是每个group都会获得mq的全部消息。(广播)
如果一个group里面有不同的消费者,那么消费者会一起消费该组收到的消息。(group内部负载均衡,不过可以通过设置模式,将负载均衡改成广播模式)
在测试的时候,如果先启动两个消费者,两个消费者同一个组,监听同一个topic,再启动生产者,生产10条消息,消息并不会推送给消费者,不知道为什么。
但是先启动生产者,再启动两个消费者,两个消费者都会收到完整的全部消息,说明广播模式确实生效了。(估计是消费者启动的时候会拉取一次消息)
批量消息注意点:
批量消息应该有相同的topic,相同的waitStoreMsgOK
不能是延时消息
消息内容总长度不超过4M
消费者可以通过tag来过滤消息
sql过滤默认是关闭的,需要在配置文件中添加enablePropertyFilter=true,然后再刷新broker配置。
Springboot整合RocketMQ
初始化一个Springboot项目,然后添加整合依赖。
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.4</version>
</dependency>
生产者:
在yaml配置文件里面配置name-server地址以及group组
在controller里面自动注入一个RocketMQTemplate变量。
往RocketMQ发消息就用rocketMQTemplate.convertAndSend(“topic10”,user);
消息对应的对象需要实现Serializable接口。
rocketMQTemplate对象有很多方法,分别对应着不同的发送方式,同步异步延时等等。
消费者:
使用service类,实现RocketMQListener<User>接口
onMessage方法就对应着消费者的业务逻辑
消费者就用注解@RocketMQMessageListener(topic = “topic10”,consumerGroup = “group1”)
在注解里面可以设置topic,group,过滤方式,传播方式
消息顺序存取
生产者生产消息的时候通过消息队列选择器,通过实现MessageQueueSelector接口,实现select方法,内部根据消息订单id取模来选择放到不同的消息队列,以确保订单步骤会按照正确顺序执行。
public class Producer {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
// 1.谁来发?
DefaultMQProducer producer = new DefaultMQProducer("group1");
// 2.发给谁?
producer.setNamesrvAddr("localhost:9876");
producer.start();
List<OrderStep> list=new ArrayList<>();
OrderStep orderStep=null;
orderStep=new OrderStep(1,"创建");
list.add(orderStep);
orderStep=new OrderStep(2,"创建");
list.add(orderStep);
orderStep=new OrderStep(1,"推送");
list.add(orderStep);
orderStep=new OrderStep(1,"完成");
list.add(orderStep);
orderStep=new OrderStep(2,"推送");
list.add(orderStep);
orderStep=new OrderStep(2,"完成");
list.add(orderStep);
for (final OrderStep step : list) {
Message message=new Message("topic12","tag1",orderStep.toString().getBytes());
producer.send(message, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> list, Message message, Object o) {
int size=list.size();
int orderId=(int) (step.getOrderId());
int i=orderId%size;
return list.get(i);
}
},null);
}
}
}
消费者通过consumer.registerMessageListener,里面的参数实现MessageListenerOrderly接口,实现顺序监听。
public class Consumer {
public static void main(String[] args) throws MQClientException {
//谁来收?
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1");
//从哪里收?
consumer.setNamesrvAddr("localhost:9876");
//设置消息模式(指同一个group内部的模式),默认是负载均衡MessageModel。CLUSTERING
//可以设置成广播模式MessageModel.BROADCASTING
consumer.setMessageModel(MessageModel.BROADCASTING);
//监听哪个消息队列?
consumer.subscribe("topic12","*");
//消费者顺序监听,一个线程只监听一个队列
consumer.registerMessageListener(new MessageListenerOrderly() {
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {
for (MessageExt messageExt : list) {
System.out.println(messageExt);
byte[] body= messageExt.getBody();
System.out.println(new String(body));
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
consumer.start();
System.out.println("消费者起来了");
}
}
事务(事务消息只与生产者有关):
生产者使用TransactionMQProducer,然后设置事务监听,最后发送消息
public class Producer {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
// 1.谁来发?
TransactionMQProducer producer = new TransactionMQProducer("group1");
// 2.发给谁?
producer.setNamesrvAddr("localhost:9876");
//设置事务监听
producer.setTransactionListener(new TransactionListener() {
//正常事务过程
public LocalTransactionState executeLocalTransaction(Message message, Object o) {
//把消息保存到数据库
//用if else来判断是否存入数据库
return null;
}
//事务补偿过程,主动去问事务完成了没有
public LocalTransactionState checkLocalTransaction(MessageExt messageExt) {
//
return null;
}
});
producer.start();
// 3.怎么发?
// 4.发什么?
String msg="hello world";
Message message = new Message("topic1", "tag1", msg.getBytes());
// 5.发的结果是什么?
TransactionSendResult transactionSendResult = producer.sendMessageInTransaction(message, null);
System.out.println(transactionSendResult);
//别关生产者
}
}
预选申请空间,然后进行顺序读写,效率高
零拷贝,不分用户态,这样少一次拷贝,效率更高(要求预留空间)
MQ数据存储区域:消费逻辑队列,索引,commitlog(内部有message queue)
异步刷盘:broker收到消息之后写入内容,然后直接返回ACK给生产者。
同步刷盘:生产者发送消息到MQ,MQ挂起生产者发消息的线程,MQ将消息写入内存,
内存数据写入硬盘,硬盘存储后返回success,MQ恢复挂起的生产者线程,再发送ACK给生产者。
高可用:
name-server无状态+全服务器注册
消息服务器主从架构
生产者将相同的topic绑定到多个group组,保证master挂了之后,其他master仍可用
消息消费,RocketMQ自身会根据master压力确认是否由master承担消息读取功能。繁忙的时候自动切换到slave程度读取工作。
负载均衡:
生产者用轮询机制去发送消息,发送给broker里面的message queue
同一个broker内轮询message queue,让每一个message queue只发给一个消费者进行消费,
顺序消息重试,每隔一分钟就会重试
无序消息重试只适合负载均衡模式,可以设置,重试的次数越多,时间间隔就越长。
等到次数达到16次之后就放弃,放弃的消息会放到死信队列中,然后让运维介入
消息幂等性,同一个订单,无论请求多少次,结果都是一样的。