1.基础知识准备
1.1 为什么要使用队列
- 解耦:通过队列结构,降低各个系统之间的耦合度
- 冗余备份:比如 MySQL 的 Binlog 复制,可以同步多个只读数据库
- 削峰填谷:流量洪峰可快速处理,避免调用链过长导致系统变慢
- 高可用性:多个系统通过消息进行处理,即使有某个系统挂掉也不会影响整体结果
- 流式数据处理:最经典的就是 Kafka 加 Storm 的组合了
1.2 MQ 的主要基本概念
- Broker:消息服务器,作为 Server 提供消息核心服务
- 生产者:消息生产者,业务的发起方,负责生产消息传输给 Broker
- 消费者:消息消费者,业务的处理方,负责从 Broker 获取消息并进行业务逻辑处理
- Topic:主题,发布订阅模式下的消息统一汇集地,不同生产者向 Topic 发送消息,由 MQ 服务器分发到不同的订阅者
- Queue:队列,PTP 模式下,特定生产者向特定 Queue发送消息,消费者订阅特定的 Queue 完成指定消息的接收
- Message:消息体,根据不同通信协议定义的固定格式进行编码的数据包,来封装业务数据,实现消息的传输
- 模式之点对点:Queue 中的数据由多个消费者消费,每个消费者消费的是不同的数据

- 模式之发布订阅:消息在每个订阅者之间都会被分发一份

以上的概念其实是 JMS 消息的定义,实际上所有的 MQ 中间件都是由这些组件定义成,只不过大部分会做更适合自己的一些扩展。
1.3 常见的消息中间件
产品 |
实现语言 |
效率 |
ActiveMQ |
Java |
一般 |
RabbitMQ |
Erlang |
超快 |
Kafka |
Scala |
超快 |
RocketMQ |
Java |
超快 |
Redis |
C |
超快 |
如上表,我们可见如今市面上的 MQ 中间件,以 Java 语言居多(我们姑且认为 Scala 也属于 Java 系,因为它也在 JVM 中运行,并且在实际实现过程中也确实还是应用的 Java 的底层实现机制),因此我们有必要熟悉并且了解 Java 实现高性能队列的底层原理。
1.4 Java 文件读写的几种方式
//一条数据 大小
private static String data = "1234abcd";
//文件总大小
private static long size = 10 * 1024 * 1024;
//循环次数 = 总大小 / 一条数据大小
private static long loop = 10 * 1024 * 1024 / data.getBytes().length;
1.4.1 随机读写文件
随机文件读写 RandomAccessFile 类,之所以叫随机是可以任意访问文件中的位置,性能较低。
long start = System.currentTimeMillis();
//文件路径
String fileName = PATH + "random.dat";
//创建随机读写文件类 , 第二个参数 rw 标识可读写
RandomAccessFile rw = new RandomAccessFile(fileName, "rw");
for (int i = 0; i < loop; i++) {
//写入数据
rw.writeBytes(data);
}
System.out.println(System.currentTimeMillis() - start);
1.4.2 普通读写文件
我们最常见的 Java 文件读写,适合小文件加载和保存读取。
long start = System.currentTimeMillis();
String fileName = PATH + "normalFile.dat";
File file = new File(fileName);
//创建输出流
FileOutputStream fileOutputStream = new FileOutputStream(file);
for (int i = 0; i < loop; i++) {
//输出流中写入数据
fileOutputStream.write(data.getBytes());
}
//关闭流
fileOutputStream.close();
System.out.println(System.currentTimeMillis() - start);
1.4.3 Buffer 读写文件
Buffer 顾名思义就是提供一层缓存加载,其主要思想就是想减少程序与 IO 磁盘的交互,来提高性能。
long start = System.currentTimeMillis();
String fileName = PATH + "bufferFile.dat";
File file = new File(fileName);
//创建输出流
FileOutputStream fileOutputStream = new FileOutputStream(file);
// 创建缓冲输出流
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
byte[] bytes = new byte[10240];
for (int i = 0; i < loop; i++) {
bufferedOutputStream.write(data.getBytes());
}
//刷新缓冲中的数据
bufferedOutputStream.flush();;
System.out.println(System.currentTimeMillis() - start);
1.4.4 MappedFileBuffer
将文件映射到内存中,无需加载操作磁盘文件,以此来提高性能。
long start = System.currentTimeMillis();
String fileName = PATH + "mapped.dat";
///创建 随机读写文件类
RandomAccessFile rw = new RandomAccessFi