文章目录
RocketMQ
什么是RocketMQ?
消息队列 RocketMQ 版是基于 Apache RocketMQ 构建的低延迟、高并发、高可用、高可靠的分布式消息中间件。
RoekcetMQ的核心概念
- 生产者:
生产消息 订单服务- 消费者:
消费消息 积分服务- RocketMQ内部服务:
broker:多个,集群,启动时信息放置在nameServer
nameSercer:broker的列表信息放在nameServer当中,根据broker地址找到对应的broker,消费中取 信息,(管理 broker的ip信息)- topic:一类的消息放在一个主题下面
- 标签:开始给消息打一个标签,消费时标签必须对应上,相当于过滤作用
- Massage消息:用于封装数据,是数据的载体
- broker是RocketMQ的核心,大部分工作都在Broker中完成的,包括接受请求,处理消费,消费持久,消息的HA以及服务端过滤等都在这里面完成
//每个topic中还有多个队列MassageQuenen,每个队列中包含topic的名称,brokername,队列的id
//生产者发送一个信息给mq,经过broker封装成一个topic,然后消费者根据topic的名称可以获取到这个信息
//生产者和消费者都可以根据绑定的nameserver地址来获取对应的broker列表的ip信息,根据ip去找到对应的信息即可
应用场景呢?
解耦
异步解耦:
1.提高的了性能(异步)
不使用mq组件:注册业务需要花费1s,然后根据注册信息去发送短信和发送邮件共花费10s,那么一共花费11s使用mq
组件:因为发送短信和邮件的业务不需要立马发送的事情,因为我们可以使用mq消息组件,将注册业务后的信息放置在mq组件中,开启两个线程进行mq拿取需要信息即可,这样主线程的时间就只需要6s了
2.解耦便于维护(解耦)
假设有一个纸黄金系统,一个黄金系统,我们需要得到黄金的行情数据得到交易价格,然后控制开市和闭市
后台管理系统就需要直接调用纸黄金交易系统和黄金交易系统接口,告诉两个系统要开市还是要闭市,
如果在开设一个白银系统,那么就要修改后台管理的代码,调用白银系统的接口来告诉她开市,如果在减少一个系统呢?
维护起来是很麻烦的。
解决办法:mq
那么如何解耦?在后台管理系统和其他系统之间增加一层mq,无论是开市指令还是闭市指令,都直接发送到队列中,然后其他系统需要开闭市操作的就要订阅监听这个队列,这样无论是增加其他的业务系统还是减少业务系统,都不需要修改后台管理系统,负责后台管理系统的同学就轻松多了,其他的系统只要接收到指令进行相应的操作就可以了
削峰填谷
例如抢红包等活动,可能造成流量突然增长,我们可以增加一个mq,作为缓冲,mq支持的单机10w吞吐量,服务器在根据mq的消息队列中获取信息进行操作即可
分布式缓存同步/消息分发
天猫会场需要感知商品价格的变化,大量的并发会导致页面的响应时间过长,我们可以将商家的商品信息放置在mq中,然后需要的会场服务从中获取信息即可
基本使用
入门
1.导入依赖
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.4.0</version>
</dependency>
2.发送信息
public class Producer {
public static void main(String[] args) throws Exception{
//1 创建一个生产者对象, 并且指定一个生产者组
DefaultMQProducer producer = new DefaultMQProducer("wolfcode-producer");
//2 设置名字服务的地址
producer.setNamesrvAddr("127.0.0.1:9876");
//3 启动生产者
producer.start();
//4 创建一个消息
Message message = new Message("01-hello", "tagA", "hello,rocketmq".getBytes("utf-8"));
//5 发送消息
producer.send(message);
//6 关闭连接
producer.shutdown();
}
}
3.消费信息
public class Consumer {
public static void main(String[] args) throws Exception{
//创建一个拉取消息的消费者对象
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("wolfcode-consumer");
//设置名字地址
consumer.setNamesrvAddr("127.0.0.1:9876");
//绑定消息的主题
consumer.subscribe("01-hello", "*");
//消费者监听处理消息方法
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
for (MessageExt msg : msgs) {
System.out.println("消费线程:"+Thread.currentThread().getName()+",消息ID:"+msg.getMsgId()+",消息内容:"+new String(msg.getBody()));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//启动消费者
consumer.start();
}
}
发送信息的方式
同步
package cn.wolfcode.send_way;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
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.exception.RemotingException;
/**
* @author Lin
* @version 1.0
* @date 2021/3/2 15:02
* 1.发送消息的方式:
* 同步(生产者发送信息到broker中需要等broker将信息持久化到磁盘才能返回接口来执行后面的操作)
* 2.什么broker呢?
* broker是RocketMQ的核心,大部分工作都在Broker中完成的,包括接受请求,处理消费,消费持久,消息的HA以及服务端过滤等都在这里面完成
* 因为信息有HA,所以需要进行磁盘的持久化操作
*/
public class rocketmqProducter_同步 {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
//1.创建生产者对象
DefaultMQProducer mqProducer = new DefaultMQProducer("rocket_product_group");
//2.绑定nameserver地址
mqProducer.setNamesrvAddr("localhost:9876");
//3.启动生产者
mqProducer.start();
for (int i = 0; i <100 ; i++) {
//4.创建信息对象
Message message = new Message("02-hellow", ("hellowword"+i).getBytes());
//5.生产者发送信息
SendResult sendResult = mqProducer.send(message);
System.out.println(sendResult.getMsgId());
}
//6.关闭资源
mqProducer.shutdown();
}
}
异步
/**
* @author Lin
* @version 1.0
* @date 2021/3/2 15:02
* 1.发送消息的方式:
* 异步(生产者发送信息到broker中不需要等broker将信息持久化到磁盘才能返回接口来执行后面的操作)
* 2.什么broker呢?
* broker是RocketMQ的核心,大部分工作都在Broker中完成的,包括接受请求,处理消费,消费持久,消息的HA以及服务端过滤等都在这里面完成
* 因为信息有HA,所以需要进行磁盘的持久化操作
*3.异步发送的时候,使用SendCallback:相当于时回调函数,成功后执行,可以查看执行的结果
*
* 结果1:全部发送失败,因为异步,所以不需要持久化读写到磁盘上,所以还没读写完就直接程序shuntdown了,程序终止了
* 如何想使用异步,不持久化信息到磁盘呢?该如何解决呢?
* 解决办法:
* 使用jUI(用于处理线程)的countdownlatch类(某一线程在开始运行前等待n个线程执行完毕),
* 它允许一个或多个线程等待直到在其他线程中一组操作执行完成
* 该类是一个同步类工具,不涉及锁定,指涉及了线程的通信的时候,使用她较为合适
* 利用构造器进行初始化count的值,当count为0时,则当前线程继续运行,否则不运行
* 注意:使用await方法进行阻塞,countDown,计时器-1,可以唤醒线程
* 例子:公交车上有100个空的位置,上来一个人则位置减少一个,只有公交车上没有空位置时,则才开车
*
*/
public class rocketmqProducter_异步 {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
//1.创建生产者对象
DefaultMQProducer mqProducer = new DefaultMQProducer("rocket_product_group");
//2.绑定nameserver地址
mqProducer.setNamesrvAddr("localhost:9876");
//3.启动生产者
mqProducer.start();
//创建一个CountDownLatch对象,等待一百个线程执行完毕后在往下继续
final CountDownLatch countDownLatch = new CountDownLatch(100);
for (int i = 0; i <100 ; i++) {
//4.创建信息对象
Message message = new Message("03-sendway", ("hellowword"+i).getBytes());
//5.生产者发送信息,
mqProducer.send(message, new SendCallback() {
public void onSuccess(SendResult sendResult) {
countDownLatch.countDown();
System.out.println("发送成功");
}
public void onException(Throwable throwable) {
countDownLatch.countDown();
System.out.println("发送失败");
}
});
}
countDownLatch.await();
//6.关闭资源
mqProducer.shutdown();
}
}
一次性信息
/**
* @author Lin
* @version 1.0
* @date 2021/3/2 15:02
* 1.发送消息的方式:
* 一次性发送(把信息发送到broker中,不管返回结果,早晚都会执行)场景:登录逻辑后的添加登录日志
* 2.什么broker呢?
* broker是RocketMQ的核心,大部分工作都在Broker中完成的,包括接受请求,处理消费,消费持久,消息的HA以及服务端过滤等都在这里面完成
* 因为信息有HA,所以需要进行磁盘的持久化操作
*/
public class rocketmqProducter_一次性发送 {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
//1.创建生产者对象
DefaultMQProducer mqProducer = new DefaultMQProducer("rocket_product_group");
//2.绑定nameserver地址
mqProducer.setNamesrvAddr("localhost:9876");
//3.启动生产者
mqProducer.start();
for (int i = 0; i <100 ; i++) {
//4.创建信息对象
Message message = new Message("producter_way04", ("hellowword"+i).getBytes());
//5.生产者发送信息
SendResult sendResult = mqProducer.send(message);
//发送信息到同一个broker中
mqProducer.sendOneway(message);
}
//6.关闭资源
mqProducer.shutdown();
}
}
消费模型
集群模式
package model;
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.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import java.util.List;
/**
* @author Lin
* @version 1.0
* @date 2021/3/2 15:09
*
* 集群模型:一个信息只给一个消费者使用,多个消费者承担一个消费者的压力
* 操作,打开两个消费者线程,监听一个主题
* 打开一个生产者,进行生产信息,
*/
public class rocketmqConsumer_集群 {
public static void main(String[] args) throws MQClientException {
//1.创建消费者对象
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("rocket_consumer_group");
//2.绑定nameserver地址
consumer.setNamesrvAddr("localhost:9876");
//3.监听消费者的topic
consumer.subscribe("01-hellow", "*");
//4.消费者执行业务逻辑
//设置集群
consumer.setMessageModel(MessageModel.CLUSTERING);
consumer.registerMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt ext : list) {
System.out.println("收到信息:"+new String(ext.getBody()));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//5.启动消费者不需要关闭因为需要一直监听
consumer.start();
}
}
广播模式
package model;
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.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
import java.util.List;
/**
* @author Lin
* @version 1.0
* @date 2021/3/2 15:09
*
* 集群模型:一个信息只会发送给所有消费者,主要是一个信息需要不同的处理,例如用户的信息,需要用来短信和邮件的发送
* 操作,打开两个消费者线程,监听一个主题
* 打开一个生产者,进行生产信息,
*/
public class rocketmqConsumer_广播 {
public static void main(String[] args) throws MQClientException {
//1.创建消费者对象
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("rocket_consumer_group");
//2.绑定nameserver地址
consumer.setNamesrvAddr("localhost:9876");
//3.监听消费者的topic
consumer.subscribe("01-hellow", "*");
//4.消费者执行业务逻辑
//设置广播
consumer.setMessageModel(MessageModel.BROADCASTING);
consumer.registerMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt ext : list) {
System.out.println("收到信息:"+new String(ext.getBody()));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//5.启动消费者不需要关闭因为需要一直监听
consumer.start();
}
}
消费方式
推送消费
/**
* @author Lin
* @version 1.0
* @date 2021/3/2 15:09
*/
public class rocketmqConsumer_推送消费 {
public static void main(String[] args) throws MQClientException {
//1.创建消费者对象
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("rocket_consumer_group");
//2.绑定nameserver地址
consumer.setNamesrvAddr("localhost:9876");
//3.监听消费者的topic
//设置标签过滤
consumer.subscribe("01-hellow", MessageSelector.bySql("a > 12"));
//4.消费者执行业务逻辑
consumer.registerMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt ext : list) {
System.out.println("收到信息:"+new String(ext.getBody()));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//5.启动消费者
consumer.start();
}
}
拉取消费
/**
* @author Lin
* @version 1.0
* @date 2021/3/2 15:09
* 拉取消费:只是从rocketmq中拉取某个信息队列的信息,
* 推送消费:需要生产者进行生产信息放置在消息队列上,进行监听信息获取信息
*
*/
public class rocketmqConsumer_拉取消费 {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
//1.创建消费者对象
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("rocket_consumer_group2");
//2.绑定nameserver地址
consumer.setNamesrvAddr("localhost:9876");
//3.开启消费
consumer.start();
//4.获取对应的消息队列(话题名称,broker名称,队列的id)
PullResult pullResult = consumer.pull(new MessageQueue("producter_way04", "broker-a", 0), "*", 0, 1);
for (MessageExt messageExt : pullResult.getMsgFoundList()) {
System.out.println("消费线程:"+Thread.currentThread().getName()+"消费id"+messageExt.getMsgId()+"消费信息:"+new String(messageExt.getBody()));
}
//5.关闭消费者
consumer.shutdown();
}
}
延迟信息
生产者
package cn.wolfcode.delay;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
/**
* @author Lin
* @version 1.0
* @date 2021/3/2 15:02
*/
public class rocketmqProducter_延迟生产 {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
//1.创建生产者对象
DefaultMQProducer mqProducer = new DefaultMQProducer("rocket_product_group");
//2.绑定nameserver地址
mqProducer.setNamesrvAddr("localhost:9876");
//3.启动生产者
mqProducer.start();
//4.创建信息对象,指定对应的topic,因为topic用来存储一类的信息放在这个主题下面
Message message = new Message("delay_product2", "hhhhh".getBytes());
//设置延迟的等级时间为3,表示10秒后进行生产
message.setDelayTimeLevel(3);
//5.生产者发送信息
mqProducer.send(message);
System.out.println("发送成功");
//6.关闭资源
mqProducer.cloneClientConfig();
}
}
消费者
package method;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.MessageSelector;
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.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
/**
* @author Lin
* @version 1.0
* @date 2021/3/2 15:09
*/
public class rocketmqConsumer_推送消费 {
public static void main(String[] args) throws MQClientException {
//1.创建消费者对象
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("rocket_consumer_group");
//2.绑定nameserver地址
consumer.setNamesrvAddr("localhost:9876");
//3.监听消费者的topic
//设置标签过滤
//consumer.subscribe("01-hellow", MessageSelector.bySql("a > 12"));
//4.消费者执行业务逻辑
consumer.registerMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt ext : list) {
System.out.println("收到信息:"+new String(ext.getBody()));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//5.启动消费者
consumer.start();
}
}
消息过滤
SQL92过滤
/**
* @author Lin
* @version 1.0
* @date 2021/3/2 15:02
*/
public class rocketmqProducter {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
//1.创建生产者对象
DefaultMQProducer mqProducer = new DefaultMQProducer("rocket_product_group");
//2.绑定nameserver地址
mqProducer.setNamesrvAddr("localhost:9876");
//3.启动生产者
mqProducer.start();
//4.创建信息对象,指定对应的topic,因为topic用来存储一类的信息放在这个主题下面
//设置标签
Message message = new Message("01-hellow2", "TagA","hellowword".getBytes());
message.putUserProperty("num", String.valueOf(10));
//5.生产者发送信息
mqProducer.send(message);
System.out.println("延迟生产delay");
//6.关闭资源
mqProducer.cloneClientConfig();
}
}
package method;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.MessageSelector;
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.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import java.util.List;
/**
* @author Lin
* @version 1.0
* @date 2021/3/2 15:09
*/
public class rocketmqConsumer_推送消费 {
public static void main(String[] args) throws MQClientException {
//1.创建消费者对象
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("rocket_consumer_group");
//2.绑定nameserver地址
consumer.setNamesrvAddr("localhost:9876");
//3.监听消费者的topic
//设置标签过滤
consumer.subscribe("01-hellow", MessageSelector.bySql("a > 12"));
//4.消费者执行业务逻辑
consumer.registerMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
for (MessageExt ext : list) {
System.out.println("收到信息:"+new String(ext.getBody()));
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//5.启动消费者
consumer.start();
}
}
只有使用push模式的消费者才能用使用SQL92标准的sql语句,接口如下:
public void subscribe(finalString topic, final MessageSelector messageSelector)注意: 在使用SQL过滤的时候, 需要配置参数enablePropertyFilter=true