一.RocketMQ教程-MQ概述&RocketMQ安装

1. RocketMQ概述

1.1.为什么使用MQ

MQ可以解决:业务同步问题,使用了MQ,业务一定是异步的
如果项目使用MQ,可参考书籍: 《RocketMQ技术内幕》

举个现实生活的例子:地铁扫码出站
1634803086238.png
假如天府通出站API 和 支付系统 是不同的子系统 (两个Tomcat远程通信)

上下班高峰期使用天府通刷码的人非常多,并发量很高,一个出站请求到后台需要做费用结算,或者积分赠送等业务。由于并发很高,并且费用结算和积分等业务本来就耗时,况且支付服务也不一定能承担那么大的请求量。

当服务器线程耗尽,后续请求会等待变慢,再加上高并发请求就会导致后续请求越来越慢,请求长时间等待,导致大量请求超时。并发太高,可能会导致服务器的内存上升,CPU使用率急速上升,甚至导致服务器宕掉。
解决方案:使用MQ消峰,效果如下
1634803119986.png
加入MQ后的效果

  • 高并发请求在MQ中排队,达到了消除峰值的目的,不会有大量的请求同时怼到支付系统
  • 服务异步调用,“天府通出站API” 把结算消息放入MQ就可以返回“出站成功,费用稍后结算”给用户,响应时间很快
  • 服务彻底解耦,即使支付服务挂掉,也不影响“天府通出站API”正常工作,当支付系统再启动仍然可以继续消费MQ中的消息。

1.2.MQ是什么

MQ 指的是消息队列(Message Queue),是一种异步通信方式,通过在消息生成者和消息消费者之间引入一个消息队列来传递消息。该消息队列将消息暂存直到被处理,消费者可以异步地从队列中拉取消息并进行处理。

MQ 的主要特性包括:

异步通信:MQ 采用异步通信方式,消息生成者和消费者之间不直接通信,而是通过中间的消息队列进行通信,从而解耦了应用程序之间的依赖性。

可靠性:MQ 提供多种机制保证消息的可靠性,如消息持久化、事务机制、消息确认机制等,确保消息在发送和接收过程中不会丢失或重复。

高扩展性:MQ 可以轻松地扩展,支持多个消费者同时消费消息,同时还支持消息分发、负载均衡等高级特性。

多语言支持:MQ 支持多种编程语言和平台,可以在 Java、Python、Node.js 等多个语言和平台上使用。

实时性:MQ 通常可以实现低延迟的消息传递,适合于需要快速响应的应用场景。

可视化监控:MQ 通常提供可视化的监控界面,可以实时监控消息的生产和消费情况,方便进行性能优化和故障排查。

常见的 MQ 实现包括 Apache ActiveMQ、RabbitMQ、Kafka、RocketMQ 等。不同的 MQ 实现有其各自的特点和优劣势,开发人员可以根据具体的需求选择合适的 MQ 实现。

1.3.MQ使用场景

  • 限流削峰
    MQ可以将系统的超量请求暂存其中,以便系统后期可以慢慢进行处理,从而避免了请求的丢失或系统 被压垮。
  • 异步通信&解耦
    上游系统对下游系统的调用若为同步调用,则会大大降低系统的吞吐量与并发度,且系统耦合度太高。 而异步调用则会解决这些问题。所以两层之间若要实现由同步到异步的转化,一般性做法就是,在这两层间添加一个MQ层。 即使消费者挂掉也不影响生产者工作,只要把消息放入队列即可,消费者重启后自己消费即可。
  • 数据收集
    分布式系统会产生海量级数据流,如:业务日志、监控数据、用户行为等。针对这些数据流进行实时或 批量采集汇总,然后对这些数据流进行大数据分析,这是当前互联网平台的必备技术。通过MQ完成此 类数据收集是最好的选择。
  • 大数据处理
    比如我们的平台向“三方平台”获取数据,一次请求了大量数据回来要进行处理,由于数据较多处理不过来,那么就可以放入MQ,再创建一些消费者进行数据处理即可。

注意】如下情况不太适合MQ

  • 小项目,体量不大,并发量低的使用MQ会太过笨重 - 你可以考虑使用Redis做一个消息队列,或者Redis发布订阅机制
  • 对数据的一致性有要求(强一致性)的的场景不适合使用MQ,因为MQ是异步的是弱一致性。

1.4.使用MQ的好处

  • 提高系统响应速度
    任务异步处理。 将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理。提高了应用程序的响应时间。
  • 提高系统稳定性
    一是并发被消峰后,系统不容易被高并发打垮,二是系统挂了也没关系,操作内容放到消息队列不丢失,后续重新消费者一样能消费做业务处理。
  • 排序保证 FIFO
    遵循队列先进先出的特点,能够保证消息按照添加的数据被消费。

1.5.常见MQ产品

  • ActiveMQ
    ActiveMQ是使用Java语言开发一款MQ产品。早期很多公司与项目中都在使用。但现在的社区活跃度已 经很低。现在的项目中已经很少使用了。
  • RabbitMQ
    RabbitMQ是使用ErLang语言开发的一款MQ产品。其吞吐量较Kafka与RocketMQ要低,且由于其不是 Java语言开发,所以公司内部对其实现定制化开发难度较大。
  • Kafka
    Kafka是使用Scala/Java语言开发的一款MQ产品。其最大的特点就是高吞吐率,常用于大数据领域的实时计算、日志采集等场景。其没有遵循任何常见的MQ协议,而是使用自研协议。对于Spring Cloud Netflix,其仅支持RabbitMQ与Kafka。
  • RocketMQ
    RocketMQ是使用Java语言开发的一款MQ产品。经过数年阿里双11的考验,性能与稳定性非常高。其没有遵循任何常见的MQ协议,而是使用自研协议。对于Spring Cloud Alibaba,其支持RabbitMQ、 Kafka,但提倡使用RocketMQ

1634803515356.png
技术选型建议:

  • 业务场景简单,允许数据丢失,想要快速上线,推荐使用Redis
  • 大数据场景,日志收集,实时性要求高,推荐Kafka
  • 金融领域,不能接受消息丢失或重复,推荐使用RabbitMQ或者RocketMQ

1.6.MQ常见协议

  • AMQP协议
    AMQP是一套公开的消息队列协议,最早在2003年被提出,它旨在从协议层定义消息通信数据的标准格式, 为的就是解决MQ市场上协议不统一的问题。基于此协议的客户端与消息中间件可传递 消息,并不受客户端/中间件不同产品,不同开发语言等条件的限制,RabbitMQ就是遵循AMQP标准协议开发的MQ服务。 官方:http://www.amqp.org
  • JMS协议
    JMS是Java消息服务,是java提供的一套消息服务API标准,其目的是为所有的java应用程序提供统一的消息通信的标准,类似java的 jdbc,只要遵循jms标准的应用程序之间都可以进行消息通信。它和AMQP有什么 不同,jms是java语言专属的消 息服务标准,它是在api层定义标准,并且只能用于java应用;而AMQP是在协议层定义的标准,是跨语言的 。
  • STOMP
    STOMP,Streaming Text Orientated Message Protocol(面向流文本的消息协议),是一种MOM设计
    的简单文本协议。STOMP提供一个可互操作的连接格式,允许客户端与任意STOMP消息代理(Broker)进行交互。ActiveMQ是该协议的典型实现,RabbitMQ通过插件可以支持该协议
  • MQTT
    MQTT,Message Queuing Telemetry Transport(消息队列遥测传输),是IBM开发的一个即时通讯协 议,是一种二进制协议,主要用于服务器和低功耗IoT(物联网)设备间的通信。该协议支持所有平 台,几乎可以把所有联网物品和外部连接起来,被用来当做传感器和致动器的通信协议。 RabbitMQ通 过插件可以支持该协议。

1.7.RocketMQ介绍

RocketMQ是一个统一消息引擎、轻量级数据处理平台。 

RocketMQ是⼀款阿⾥巴巴开源的消息中间件,双十一承载了万亿级消息的流转,2016年11⽉,阿⾥巴巴向 Apache 软件基⾦会捐赠 RocketMQ,成为 Apache 孵化项⽬,2017 年 9 ⽉ ,Apache 宣布 RocketMQ孵化成为 Apache 顶级项⽬(TLP )成为国内⾸个互联⽹中间件在 Apache 上的顶级项⽬。
- 支持集群模型、负载均衡、水平扩展能力 
- 亿级别消息堆积能力 

- 采用零拷贝的原理,顺序写盘,随机读 

- 底层通信框架采用Netty NIO 
- NameServer(注册中心)代替Zookeeper,实现服务寻址和服务协调 

- 消息失败重试机制、消息可查询 

- 强调集群无单点,可扩展,任意一点高可用,水平可扩展 

- 经过多次双十一的考验 

2.RocketMQ安装

2.1.下载RocketMQ

下载地址:https://rocketmq.apache.org/
下载后解压

  • Bin : 可执行文件目录
  • Conif:配置文件目录
  • Lib : 依赖库,一堆Jar包

image.png

2.2.配置ROCKETMQ_HOME

解压压缩包,配置 ROCKETMQ_HOME
image.png
image.png

2.3.启动MQ

  1. 启动NameServer

Cmd命令框执行进入至MQ文件夹\bin下,然后执行 start mqnamesrv.cmd,启动NameServer。
image.png

  1. 启动Broker

进入至MQ文件夹\bin下,修改Bean目录下的 runbroker.cmd 中JVM占用内存大小
image.png
CMD执行start mqbroker.cmd -n 127.0.0.1:9876 autoCreateTopicEnable=true ,启动Broker。

2.4.RocketMQ存储结构

RocketMQ安装好之后会在用户目录下产生一个store目录用来存储相关数据:
image.png

  • Commitlog : 消息是存储,在commitlog目录中,以mapperdFile文件顺序存储消息。
  • Config : 存放运行期间的配置文件
  • Consumerqueue : 该目录中存放的是队列,consume queue存放着commitlog中的消息的索引位置
  • Index :存放着消息索引文件 indexFile,用来实现根据key进行消息的快速查询
  • Abort : 该文件在broker启动后自动创建,正常关闭abort会消失
  • Checkpoint :记录 Commitlog ,Consumerqueue 和index 文件的最后刷盘时间戳

[]RocketMQ数据存储在磁盘会影响性能吗?
不会,RocketMQ的性能在所有的MQ中是比较高的,主要是因为RocketMQ使用了mmap零拷贝技术,consumequeue中的数据是顺序存放的,还引入了PageCache的预读取机制,使得对 consumequeue文件的读取几乎接近于内存读取,即使在有消息堆积情况下也不会影响性能。

mmap零拷贝(mmap zero copy)技术是一种高效的文件读写技术,它可以减少数据在内核和用户空间之间的数据传输次数,从而提高数据传输的效率。该技术通过将文件映射到内存中,并利用操作系统提供的copy-on-write机制来实现零拷贝,从而避免了在数据传输过程中内核和用户空间的数据拷贝。

具体来说,mmap零拷贝技术主要分为以下三个步骤:

使用mmap()函数将文件映射到内存中,得到一个指向内存起始位置的指针。

通过对指针所指向的内存区域进行读写操作,实现文件的读写。由于操作系统会将内存区域标记为保护状态,所以任何试图访问这个区域的软件都会触发一个页错误异常,从而导致操作系统将磁盘上的数据读取到内存中并复制到指定的内存区域。

当需要将修改后的数据写回磁盘时,操作系统会利用copy-on-write技术将内存区域中发生变化的数据复制到一个临时的缓冲区中。然后,操作系统会将这个缓冲区中的数据写回磁盘,从而实现文件的写入。在这个过程中,由于copy-on-write机制的存在,操作系统会尽可能减少数据复制的次数和开销,从而实现零拷贝。

mmap零拷贝技术可以大幅提高文件读写操作的效率,减少读写操作对CPU和内存的占用,从而提升应用程序的整体性能。但需要注意的是,在使用该技术时也要考虑到一些潜在的问题,例如线程安全、缓存一致性和内存管理等问题。

2.5.RocketMQ插件

安装一个可视化插件
RocketMQ可视化管理插件下载地址:https://github.com/apache/rocketmq-externals/releases

解压后,修改配置:src/main/resource/application.properties ,这里需要指向Name Server 的地址和端口 如下:
image.png
打包插件,这个插件是个SpringBoot项目
回到安装目录(pom.xml所在目录),执行: mvn clean package -Dmaven.test.skip=true ,然后会在target目录生成打包后的jar文件

启动插件
进入 target 目录,CMD执行 java -jar rocketmq-console-ng-1.0.0.jar , 访问 http://localhost:8088

编写个批处理文件:
image.png

启动成功:
image.png

3.RocketMQ消息种类

RocketMQ消息种类
	1.单向消息:不响应任何结果,发送消息发了就发了没有结果通知
	2.同步消息:发送消息的那一句代码一定要等到MQ返回发送消息的结果
	3.异步消息:发送消息的那一句代码的执行结果跟后面的代码的执行是异步的,消息的发送结果也是在回调中获取,类似于Axios异步请求
	4.延迟消息:可以用作定时器,你发送的消息不会立马执行,在你指定的时间后才会执行
	5.事务消息:是为了保证分布式事务所提供的消息

3.1.参考文档

https://github.com/apache/rocketmq/blob/master/docs/cn/RocketMQ_Example.md

image.png

3.2.同步消息

package com.alibaba.sync;

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.common.RemotingHelper;

public class Producer {
    public static void main(String[] args) throws Exception {
        // 实例化消息生产者Producer
        DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
        // 设置NameServer的地址
        producer.setNamesrvAddr("localhost:9876");
        // 启动Producer实例
        producer.start();
        for (int i = 0; i < 100; i++) {
            // 创建消息,并指定Topic,Tag和消息体
            Message msg = new Message("TopicTest" /* Topic */,
                    "TagA" /* Tag */,
                    ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
            );
            // 发送消息到一个Broker
            SendResult sendResult = producer.send(msg);
            // 通过sendResult返回消息是否成功送达
            System.out.printf("%s%n", sendResult);
        }
        // 如果不再发送消息,关闭Producer实例。
        producer.shutdown();
    }
}

package com.alibaba.sync;


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 java.nio.charset.StandardCharsets;
import java.util.List;

public class Consumer {
    public static void main(String[] args) throws InterruptedException, MQClientException {

        // 实例化消费者
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name");

        // 设置NameServer的地址
        consumer.setNamesrvAddr("localhost:9876");

        // 订阅一个或者多个Topic,以及Tag来过滤需要消费的消息
        consumer.subscribe("TopicTest", "*");
        // 注册回调实现类来处理从broker拉取回来的消息
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                System.out.println(new String(msgs.get(0).getBody(), StandardCharsets.UTF_8));
                // System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
                // 标记该消息已经被成功消费
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        // 启动消费者实例
        consumer.start();
        System.out.printf("Consumer Started.%n");
    }
}

image.png
image.png

4.面试题

可以参考:https://blog.csdn.net/weixin_44378507/article/details/131216058

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值