Java 高性能队列底层到底是什么?

现任某互联网公司架构组资深开发工程师,以前干过传统行业,呆过第三方支付公司, 目前负责公司的监控系统,底层基础服务架构开发等工作。

文章正文

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
                        
作者正在撰写中...
隐藏内容 支付可见
内容互动
写评论
加载更多
评论文章
¥3.99 购买
× 订阅 Java 精选频道
¥ 元/月
订阅即可免费阅读所有精选内容