我理解的RocketMq

1.简介

1.1. 什么是消息中间件

简单来说就是存在于客户端和服务端一种存储技术

1.2. 消息中间件的作用是什么

存储海量的消息(比如每当双十一、双十二这时用户访问服务器的会比较频繁、这时服务器压力会很大,短时间可能处理不了那么多的请求,这时消息中间件就站了出来他会把所有消息存储在自己身上,服务器每次消费多少消息就从消息中间件这里取多少消息,从而达到限流、削峰的效果)。

比如注册时注册邮件和发送短信同时发送(异步化提高了效率)

2. 那么多的消息队列我们为什么要使用RocketMq

2.1 ActiveMQ

最早出来的消息中间件,在很多年前他很流行,但现在他已经过时了。

1.它的社区很不活跃,如果出现bug可能要过很长时间才能解决。

2.它在及其偶然的情况下可能会丢消息。

3.它的吞吐量以及他的可用性都比不上其他消息中间件

2.2 RabbitMQ

1.Erlang语言开发,社区很活跃,出问题能很快解决。
2.支持多线程、图像化界面(运维爱用)。
3.处理能力较好但是可用性的话可能比不上RocketMQ

2.3 Kafka

1.大数据用的比较多(批处理)

2.支持分布式处理能力很强但它的功能单一

3.RocketMQ为什么会这么强大(内存模型)

3.1 基本概念

  • Producer: 消息生产者,负责消息的产生,由业务系统负责产生
  • Consumer:消息消费者,负责消息消费,由后台业务系统负责异步消费
  • Topic:消息的逻辑管理单位

这三者是Rocket最基本的概念Producer负责消息的生产、Consumer负责消息的消费、Topic负责消息的存储(三者相辅相成)。体来说是Producer将消息发往具体的TopicConsumer订阅Topic,主动拉取或被动接受消 如果Consumer消费消息失败则默认会重试16

666

 3.2 深入了解

  • Broker:负责消息的中转功能(邮局) ,里面有多个Topic。
  • MessageQueue: 消息的物理管理单位,一个Topic下有多个Queue,默认一个Topic创建时会创 四个MessageQueue。

  • ConsumerGroup具有同样消费逻辑消费同样消息的Consumer,可以归并为一个group。

  • ProducerGroup: 具有同样属性的一些Producer可以归并为同一个Group。

    同样属性是指:发送同样Topic类型的消息。

  • Nameserver 注册中心( 每个Broker启动的时候会向namesrv注册、Producer发送消息的时候根据Topic获取路由到Broker里面Broker的信息、Consumer根据TopicNamesrv 获取topic的路由到Broker的信息)  。

3.3 更上一层楼(部署模型)

执行步骤:

1、注册中心Nameserver

2、消息中转服务Broker启动

  •     启动的时候会去创建Topic并创建对应的MessageQueue
  •     启动的时候会去注册中心注册,把自己的地址以及负责的Topic告诉注册中心
  •     BrokerNameserver之间通过心跳机制来检测对方是否存活
3、消息生产者 Produer 启动            

启动时:

  • 单个生产者者和一台nameserver保持长连接,定时查询topic配置信息
  • 单个生产者和该生产者关联的所有broker保持长连接。 
  • 运行时:
  •              默认情况下,生产者每隔30秒从nameserver获取所有topic的最新队列情况
  •              发送消息时,根据从nameserver获取的路由信息,根据发送消息的Topic和目标 Broker建立连接
  •             默认情况下,生产者每隔30秒向所有broker发送心跳,该时间由DefaultMQProducerheartbeatBrokerInterval参数决定,可手动配置。broker每隔10秒钟(此时间无法更改), 扫描所有还存活的连接,若某个连接2分钟内(当前时间与最后更新时间差值超过2分钟,此时间无法更改)没有发送心跳数据,则关闭连接
  • 4. 消息消费者 Consumer 启动
  • 启动时:
    单个消费者和一台 nameserver 保持长连接,定时查询 topic 配置信息
    单个消费者和该消费者关联的所有 broker 保持长连接。
    运行时 :
    默认情况下,消费者每隔 30 秒从 nameserver 获取所有 topic 的最新队列情况
    默认情况下,消费者每隔 30 秒向所有 broker 发送心跳,该时间由 DefaultMQPushConsumer
    heartbeatBrokerInterval 参数决定,可手动配置。 broker 每隔 10 秒钟(此时间无法更
    改),扫描所有还存活的连接,若某个连接 2 分钟内(当前时间与最后更新时间差值超过 2
    钟,此时间无法更改)没有发送心跳数据,则关闭连接,并向该消费者分组的所有消费者发
    出通知,分组内消费者重新分配队列继续消费

    3.4 刷盘策略和复制策略

  •  
    同步刷盘与异步刷盘
    RocketMQ 的消息是存储到磁盘上的,这样既能保证断电后恢复,又可以让存储的消息量超出内
    存的限制。
    RocketMQ 为了提高性能,会尽可能地保证磁盘的顺序写。消息在通过 Producer 写入 RocketMQ
    时候,有两种写磁盘方式:
    异步刷盘 :在返回写成功状态时,消息可能只是被写入了内存中,写操作的返回快,
    吞吐量大;当内存里的消息量积累到一定程度时,统一触发写磁盘操作,快速写入
    同步刷盘 :在返回写成功状态时,消息已经被写入磁盘。具体流程是,消息写入内存后,立
    刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,返回消息
    写成功的状态。
    同步刷盘还是异步刷盘,是通过 Broker 配置文件里的 flushDiskType 参数设置的,这个参数被设置
    SYNC_FLUSH ASYNC_FLUSH
    中的一个
  • 同步复制与异步复制
    如果一个 broker 组有 Master Slave ,消息需要从 Master 复制到 Slave 上,有同步和异步两种复
    制方式。
    同步复制是等 Master Slave 均写成功后才反馈给客户端写成功状态;异步复制方式是只要 Master
    写成功即可反馈给客户端写成功状态
    同步复制和异步复制是通过 Broker 配置文件里的 brokerRole 参数进行设置的,这个参数可以被设
    置成
    ASYNC_MASTER SYNC_MASTER SLAVE 三个值中的一个

4. 安装使用

4.1 下载

官网地址: http://rocketmq.apache.org
下载地址: http://rocketmq.apache.org/release_notes/release-notes-4.4.0/

4.2 安装配置

按照上述步骤,下载,解压

配置

4.3 启动

为了防止内存不足(配置内存大小)打开bin目录下的这两个文件

 修改NameServerBroker配置

启动

Windows

  • 启动注册中心nameServer,默认启动在9876端口,打开cmd命令窗口,进入bin目录,执行
    命令

start mqnamesrv.cmd

Linux执行

sh ./mqnamesrv

出现以下日志表示启动成功

  启动RocketMQ服务,也就是broker进入bin目录,执行命令:

Windows

start mqbroker.cmd -n 127 .0.0.1:9876 autoCreateTopicEnable = true

Linux

sh ./mqbroker -n 127 .0.0.1:9876 autoCreateTopicEnable = true

出现以下日志表示启动成功

4.4 配置文件

我们在 windows 下安装 RocketMQ ,消息数据默认会被存储在 c 盘,如果有同学 c 盘已经满了,那么此时 我们可以修改数据的存储路径到任意位置,通过在配置文件中添加如下配置即可:这里稍微注意下,如 果是windows 的路径,那么目录与目录之间用 / 而不是 \ 分割,比如 d:/store/...
# 存储路径
storePathRootDir = /usr/local/rocketmq/store
#commitLog 存储路径
storePathCommitLog = /usr/local/rocketmq/store/commitlog
# 消费队列存储路径存储路径
storePathConsumeQueue = /usr/local/rocketmq/store/consumequeue
# 消息索引存储路径
storePathIndex = /usr/local/rocketmq/store/index
#checkpoint 文件存储路径
storeCheckpoint = /usr/local/rocketmq/store/checkpoint
#abort 文件存储路径
abortFile = /usr/local/rocketmq/store/abort

5. 项目中使用

5.1 导包

<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.4.0</version>
</dependency>

5.2 普通消息

  • 消息生产者
public static void main(String[] args){ 
// 新增消息生产者 
DefaultMQProucer producer = new DefaultMQProucer("producer_group"); 
// 配置注册中心 
producer.setNamesrvAddr("localhost:9876"); 
// 启动 
producer.start(); 
// 新建消息对象 
Message message = new Message("topicA","message".context.getBytes(Charset.forName("utf-8"))); 
// 发送消息 
producer.send(message); 
}
  • 消息消费者
public static void main(String[] args) throws MQClientException { 
DefaultMQPushConsumer mqConsumer = new DefaultMQPushConsumer("consumer_group"); mqConsumer.setNamesrvAddr("localhost:9876"); 
mqConsumer.subscribe("topicA", "*"); 
// 设置消息监听器 
mqConsumer.registerMessageListener(new MessageListenerConcurrently() { @Override public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { MessageExt message = msgs.get(0); 
//获取消息内容 
byte[] body = message.getBody(); }); 
mqConsumer.start();

5.3 延迟消息

  • 消息生产者
  • public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException, MQBrokerException { // 1. 创建Producer对象 
    DefaultMQProducer produce = new DefaultMQProducer("delay_producer_group"); producer.setNamesrvAddr("127.0.0.1:9876"); 
    producer.start(); 
    // 准备消息 Message message = new Message(); 
    message.setTopic("test_delay"); 
    message.setBody("hello,delay".getBytes("utf-8")); 
    // 非常简单 延迟级别 
    // 1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h 
    // 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 
    message.setDelayTimeLevel(2);
    // 发送 
    SendResult send = producer.send(message); 
    System.out.println(send); }
    • 消息消费者 ( 和普通的消息消费者没有区别 )

    5.4 项目中使用

    在项目中,我们基于 RocketMQ 的延迟消息,实现订单超时自动取消的功能
    • 在下单的最后一步,即SendMessageHandler中,发送延时消息(消息的内容主要包含订单id),消 息的延迟时间,即订单支付超时的时间(比如半小时)
    • 在定义一个消费者,专门消费这个延迟消息,在会在订单超时时间到了之后,获取到消息中的订单 id,检查订单的状态,若未支付则,取消订单
    • 取消订单对应的逻辑,即修改订单的状态为取消订单,并根据订单中购买的各订单商品条目的数 量,扣减锁定库存,增加可售卖库存(即还原库存)
    • 因为每一个成功下单的的订单,都会对应一个这样的延迟消息,所以这意味着对于每一个订单,都会做超时自动取消的检查

 5.5 项目中实现

在下单流程的最后一个 Handler(Handler 是指 Pipeline 设计模式中的 Handler) ,即
SendMessageHandler 中,发送针对订单的延迟消息
@Component 
@Slf4j 
public class SendMessageHandler extends AbstractTransHandler { 
// 注入我们自己定义的Producer对象 
@Autowired 
DelyOrderCancelProducer producer; 
@Override
public boolean isAsync() { 
return false; }
@Override 
public boolean handle(TransHandlerContext context) { 
// 当代码执行到这里,下单已经成功了,针对已经下单成功的订单,发送延迟消息(延迟时间就是 订单的超时时间) 
CreateOrderContext createOrderContext = (CreateOrderContext) context; 
// 从上下文对象中获取订单Id 
String orderId = createOrderContext.getOrderId(); 
// 将orderId,放入延迟消息中发送(调用我们自己定义的producer对象) 
return producer.sendOrderCancelMessage(orderId); 
} 
 }
我们自己定义的Producer代码如下:
@Component 
@Slf4j 
public class DelyOrderCancelProducer { 
private DefaultMQProducer producer; 
@PostConstruct 
public void init() { 
// 1. 创建Producer对象 
producer = new DefaultMQProducer("order_delay_cancel_producer"); 
// 2. 设置nameserv地址 
producer.setNamesrvAddr("127.0.0.1:9876"); 
try {
// 启动 producer.start(); 
} catch (MQClientException e) { 
e.printStackTrace(); } 
}
// 返回值表示延迟消息发送的结果 
public boolean sendOrderCancelMessage(String orderId) { 
// 千万不要忘了添加日志 
// 准备待发送的消息 Message message = new Message(); message.setTopic("delay_order_cancel"); message.setBody(orderId.getBytes(Charset.forName("utf-8"))); 
// 设置延迟消息的延迟级别 
//message.setDelayTimeLevel(16); 
message.setDelayTimeLevel(3); 
SendResult sendResult = null; 
Exception ex = null;
try {
sendResult = producer.send(message); 
} catch (MQClientException e) { 
e.printStackTrace(); ex = e; } 
catch (RemotingException e) { 
e.printStackTrace(); ex = e; 
} catch (MQBrokerException e) 
{ e.printStackTrace(); ex = e; 
} catch (InterruptedException e) { 
e.printStackTrace(); ex = e; 
}
if (ex != null) { 
// 发送时出现异常,发送失败 return false; }
if (sendResult != null && SendStatus.SEND_OK.equals(sendResult.getSendStatus())) { 
// 消息发送成功 
return true; }
// 消息发送失败 
return false; 
} 
}
同时,我们还得定义一个消费者,来消费我们的延迟消息,在订单支付超时时间到的时候,针对未支付 的订单负责完成,订单超时自动取消的功能。
@Component 
public class DelayOrderCancelConsumer { 
private DefaultMQPushConsumer consumer; 
@PostConstruct 
public void init() { 
consumer = new DefaultMQPushConsumer("order_delay_cancel_consumer"); consumer.setNamesrvAddr("127.0.0.1:9876"); 
consumer.setMessageListener(new MessageListenerConcurrently() { 
@Override 
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) { 
// 实现,订单超时自动取消的业务逻辑 
MessageExt message = msgs.get(0); 
// 1. 取出orderId byte[] body = message.getBody(); 
try {String orderId = new String(body, 0, body.length, "utf-8");
//2. 去数据库根据OrderId,查询订单转态 
//3. 如果订单已经支付,或者已经取消,什么都不做 
//4. 如果订单仍然是初始化转态,未支付成功,执行自动取消的逻辑 
// a. 修改订单的状态为已取消 
// b. 访问tb_order_item,查询订单中包含的所有订单商品条目,根据 订单条目中商品购买的数量 +stockCount -lockCount 
} catch (UnsupportedEncodingException e) { 
e.printStackTrace(); }return null; } }); 
try {consumer.subscribe("delay_order_cancel", "*"); 
consumer.start(); } 
catch (MQClientException e) 
{ e.printStackTrace(); 
} 
} 
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值