首页 文章 精选 留言 我的

精选列表

搜索[java],共10000篇文章
优秀的个人博客,低调大师

java nio,netty,kafka 中经常提到“零拷贝”到底是什么?

零拷贝技术 Zero-Copy 是指计算机执行操作时,可以直接从源(如文件或网络套接字)将数据传输到目标缓冲区, 而不需要 CPU 先将数据从某处内存复制到另一个特定区域,从而减少上下文切换以及 CPU 的拷贝时间。 1 I/O 中断原理 在 DMA 技术出现之前,应用程序与磁盘之间的 I/O 操作都是通过 CPU 的中断完成的。 用户进程向 CPU 发起 read 系统调用读取数据,由用户态切换为内核态,然后一直阻塞等待数据的返回。 CPU 在接收到指令以后对磁盘发起 I/O 请求,将磁盘数据先放入磁盘控制器缓冲区。 数据准备完成以后,磁盘向 CPU 发起 I/O 中断。 CPU 收到 I/O 中断以后将磁盘缓冲区中的数据拷贝到内核缓冲区,然后再从内核缓冲区拷贝到用户缓冲区。 用户进程由内核态切换回用户态,解除阻塞状态,然后等待 CPU 的下一个执行时间钟。 可以看到,整个数据的传输过程,都要需要 CPU 亲自参与搬运数据的过程,而且这个过程,CPU 是不能做其他事情的。 假如通过千兆网卡或者磁盘传输大量数据时,使用 CPU 来拷贝的话,对 CPU 资源是种极大的消耗。 2 DMA 方式 DMA 的全称是直接内存访问(Direct Memory Access),是一种硬件设备绕开 CPU 独立直接访问内存的机制。 目前支持 DMA 的硬件包括:网卡、声卡、显卡、磁盘控制器等。 基于 DMA 访问方式,系统主内存于硬盘或网卡之间的数据传输可以绕开 CPU 的全程调度,数据搬运的工作交给 DMA 控制器,在传输过程中,CPU 可以继续处理其他的工作,提升系统的资源利用率。 用户进程向 CPU 发起 read 系统调用读取数据,用户态切换为内核态,然后一直阻塞等待数据的返回。 CPU 在接收到指令以后对 DMA 磁盘控制器发起调度指令。 DMA 磁盘控制器对磁盘发起 I/O 请求,将磁盘数据先拷贝到磁盘控制器缓冲区,CPU 全程不参与此过程。 数据读取完成后,DMA 磁盘控制器会接受到磁盘的通知,将数据从磁盘控制器缓冲区拷贝到内核缓冲区。 DMA 磁盘控制器向 CPU 发出数据读完的信号,由 CPU 负责将数据从内核缓冲区拷贝到用户缓冲区。 用户进程由内核态切换回用户态,解除阻塞状态,然后等待 CPU 的下一个执行时间钟。 3 传统 I/O 方式 为了理解零拷贝技术的思路,首先了解一下传统 I/O 方式存在的问题。 Linux 系统中,传统的访问方式是通过 write() 和 read() 两个系统调用实现的,通过 read() 函数读取文件到到缓存区中,然后通过 write() 方法把缓存中的数据输出到网络端口 。 下图分别对应传统 I/O 操作的数据读写流程,整个过程涉及 4 次上下文切换、 2 次 CPU 拷贝、2 次 DMA 拷贝总共 4 次拷贝。 4 次上下文切换 因为发生了两次系统调用,一次是 read() ,一次是 write(),用户程序向内核发起系统调用时,CPU 将用户进程从用户态切换到内核态;当系统调用返回时,CPU 将用户进程从内核态切换回用户态。 4 次 数据拷贝 第一次 DMA 拷贝,将磁盘上的数据拷贝到操作系统内核的缓冲区 ; 第二次 CPU 拷贝,将内核缓冲区的数据拷贝到用户的缓冲区里; 第三次 CPU 拷贝,将用户缓冲区的数据,拷贝到内核 Socket 缓冲区; 第四次 DMA 拷贝,将内核 Socket 缓冲区的数据拷贝到网卡的缓冲区。 综上,想要提升文件传输的性能,因此我们需要减少「上下文切换 」和「数据拷贝」的次数。 3 零拷贝方式 零拷贝技术实现的方式通常有 2 种:mmap + write 、sendfile、sendfile + DMA scatter-gather 。 3.1 mmap + write mmap 是 Linux 提供的一种内存映射文件的机制,它实现了将内核中读缓冲区地址与用户空间缓冲区地址进行映射,从而实现内核缓冲区与用户缓冲区的共享。 基于 mmap + write 系统调用的零拷贝方式,整个拷贝过程会发生 4 次上下文切换,1 次 CPU 拷贝和 2 次 DMA 拷贝。 用户程序读写数据的流程如下: 用户进程通过 mmap() 函数向内核发起系统调用,上下文从用户态切换为内核态。 将用户进程的内核空间的读缓冲区与用户空间的缓存区进行内存地址映射。 CPU 利用 DMA 控制器将数据从主存或硬盘拷贝到内核空间的读缓冲区。 上下文从内核态切换回用户态,mmap 系统调用执行返回。 用户进程通过 write() 函数向内核发起系统调用,上下文从用户态切换为内核态。 CPU 将读缓冲区中的数据拷贝到的网络缓冲区。 CPU 利用 DMA 控制器将数据从网络缓冲区(socket buffer)拷贝到网卡进行数据传输。 上下文从内核态切换回用户态,write 系统调用执行返回。 mmap 的拷贝虽然减少了 1 次 CPU 拷贝,提升了效率,但也存在一些隐藏的问题。 当 mmap 一个文件时,如果这个文件被另一个进程所截获,那么 write 系统调用会因为访问非法地址被 SIGBUS 信号终止,SIGBUS 默认会杀死进程并产生一个 coredump,服务器可能因此被终止。 3.2 sendfile sendfile 系统调用在 Linux 内核版本 2.1 中被引入,目的是简化通过网络在两个通道之间进行的数据传输过程。 通过 sendfile 系统调用,数据可以直接在内核空间内部进行 I/O 传输,从而省去了数据在用户空间和内核空间之间的来回拷贝。 sendfile 系统调用的引入,不仅减少了 CPU 拷贝的次数,还减少了上下文切换的次数,它的伪代码如下: 基于 sendfile 系统调用的零拷贝方式,整个拷贝过程会发生 2 次上下文切换,1 次 CPU 拷贝和 2 次 DMA 拷贝。 用户程序读写数据的流程如下: 用户进程通过 sendfile() 函数向内核发起系统调用,上下文从用户态切换为内核态。 CPU 利用 DMA 控制器将数据从主存或硬盘拷贝到内核空间的读缓冲区。 CPU 将读缓冲区中的数据拷贝到的网络缓冲区。 CPU 利用 DMA 控制器将数据从网络缓冲区拷贝到网卡进行数据传输。 上下文从内核态切换回用户态,sendfile 系统调用执行返回。 相比较于 mmap 内存映射的方式,sendfile 少了 2 次上下文切换,但是仍然有 1 次 CPU 拷贝操作。 3.3 sendfile + DMA gather copy Linux 2.4 版本的内核对 sendfile 系统调用进行修改,为 DMA 拷贝引入了 gather 操作。 它将内核空间的读缓冲区中对应的数据描述信息(内存地址、地址偏移量)记录到相应的网络缓冲区中,由 DMA 根据内存地址、地址偏移量将数据批量地从读缓冲区拷贝到网卡设备中,这样就省去了内核空间中仅剩的 1 次 CPU 拷贝操作。 在硬件的支持下,sendfile 拷贝方式不再从内核缓冲区的数据拷贝到 socket 缓冲区,取而代之的仅仅是缓冲区文件描述符和数据长度的拷贝,这样 DMA 引擎直接利用 gather 操作将页缓存中数据打包发送到网络中即可,本质就是和虚拟内存映射的思路类似。 基于 sendfile + DMA gather copy 系统调用的零拷贝方式,整个拷贝过程会发生 2 次上下文切换、0 次 CPU 拷贝以及 2 次 DMA 拷贝,用户程序读写数据的流程如下: 用户进程通过 sendfile() 函数向内核发起系统调用,上下文从用户态切换为内核态。 CPU 利用 DMA 控制器将数据从主存或硬盘拷贝到内核空间的读缓冲区。 CPU 把读缓冲区的文件描述符(file descriptor)和数据长度拷贝到网络缓冲区。 基于已拷贝的文件描述符和数据长度,CPU 利用 DMA 控制器的 gather/scatter 操作直接批量地将数据从内核的读缓冲区拷贝到网卡进行数据传输。 上下文从内核态切换回用户态,sendfile 系统调用执行返回。 5 写到最后 无论是传统 I/O 拷贝方式还是引入零拷贝的方式,2 次 DMA 拷贝是都少不了的,因为两次 DMA 都是依赖硬件完成的。 | 拷贝方式 | CPU拷贝 | DMA拷贝 | 系统调用 | 上下文切换 | | -------------------------- | ------- | ------- | ------------ | ---------- | | 传统方式(read + write) | 2 | 2 | read / write | 4 | | 内存映射(mmap + write) | 1 | 2 | mmap / write | 4 | | sendfile | 1 | 2 | sendfile | 2 | | sendfile + DMA gather copy | 0 | 2 | sendfile | 2 | RocketMQ 选择了 mmap + write 这种零拷贝方式,适用于业务级消息这种小块文件的数据持久化和传输; 而 Kafka 采用的是 sendfile 这种零拷贝方式,适用于系统日志消息这种高吞吐量的大块文件的数据持久化和传输。 但是值得注意的一点是,Kafka 的索引文件使用的是 mmap + write 方式,数据文件使用的是 sendfile 方式。 询问AI

优秀的个人博客,低调大师

binlog4j 1.3.0 发布,Java 轻量级 binlog 客户端

Binlog4j 轻量级 Mysql Binlog 客户端, 提供宕机续读, 高可用集群等特性 项目特性 集群模式, 通过集群部署的方式,保证服务高可用。 宕机续读, 避免宕机期间造成数据丢失。 数据转换, 基于泛型封装 binlog Event 的序列化数据。 兼容 传统项目 与 Spring Boot / Cloud 项目。 兼容 Spring Boot 2.x 与 Spring Boot 3.x 版本。 更新日志(1.3.0) [新增] LocalDate 类型字段兼容。[新增] LocalDateTime 类型字段兼容。[新增] BinlogEvent 类 timestamp 字段,发生时间。[新增] BinlogClient 类 registerEventHandler(IBinlogEventHandler eventHandler) 方法 下载安装 <dependency> <groupId>com.gitee.Jmysy</groupId> <artifactId>binlog4j-core</artifactId> <version>latest.version</version> </dependency> 或 implementation group: 'com.gitee.Jmysy', name: 'binlog4j-core', version: 'latest.version' 简单使用 通过 BinlogClient 创建 binlog 客户端, IBinlogEventHandler 用于接受 binlog 事件通知, 该接口允许使用泛型, 数据将遵循驼峰规则进行封装。 public class BootStrap { public static void main(String[] args) { BinlogClientConfig clientConfig = new BinlogClientConfig(); clientConfig.setHost("127.0.0.1"); clientConfig.setPort(3306); clientConfig.setUsername("root"); clientConfig.setPassword("taoren@123"); clientConfig.setServerId(1990); IBinlogClient binlogClient = new BinlogClient(clientConfig); binlogClient.registerEventHandler("database", "table", new IBinlogEventHandler() { @Override public void onInsert(BinlogEvent event) { System.out.println("插入数据:{}", event.getData()); } @Override public void onUpdate(BinlogEvent event) { System.out.println("修改数据:{}", event.getData()); } @Override public void onDelete(BinlogEvent event) { System.out.println("删除数据:{}", event.getData()); } }); binlogClient.connect(); } } 高级特性 通过 Persistence 配置为 true 启用宕机续读功能, Binlog4j 会将 binlog 的 filename 与 position 记录到 redis, 所以同时你需要设置 Redis 配置。 public class BootStrap { public static void main(String[] args) { RedisConfig redisConfig = new RedisConfig(); redisConfig.setHost("127.0.0.1"); redisConfig.setPort(6379); redisConfig.setPassword("taoren@123"); BinlogClientConfig clientConfig = new BinlogClientConfig(); clientConfig.setHost("127.0.0.1"); clientConfig.setPort(3306); clientConfig.setUsername("root"); clientConfig.setPassword("taoren@123"); clientConfig.setServerId(1990); // Client 编号 clientConfig.setRedisConfig(redisConfig); // Redis 配置 clientConfig.setPersistence(true); // 启用持久化 (宕机重启后, 从上次读取的位置开始) clientConfig.setMode(BinlogClientMode.cluster); // 高可用集群 BinlogClient binlogClient = new BinlogClient(clientConfig); binlogClient.registerEventHandler("database", "table", new IBinlogEventHandler<User>() { @Override public void onInsert(BinlogEvent<User> event) { System.out.println("插入数据:{}", event.getData()); } @Override public void onUpdate(BinlogEvent<User> event) { System.out.println("修改数据:{}", event.getData()); } @Override public void onDelete(BinlogEvent<User> event) { System.out.println("删除数据:{}", event.getData()); } }); binlogClient.connect(); } } Spring Boot Starter <dependency> <groupId>com.gitee.Jmysy</groupId> <artifactId>binlog4j-spring-boot-starter</artifactId> <version>latest.version</version> </dependency> 或 implementation group: 'com.gitee.Jmysy', name: 'binlog4j-spring-boot-starter', version: 'latest.version' 首先, 在 application.yml 中填写 BinlogClient 配置 spring: binlog4j: redis-config: host: 127.0.0.1 port: 6379 password: taoren@123 client-configs: master: username: root password: taoren@123 host: 127.0.0.1 port: 3306 serverId: 1990 单表监听 使用 @BinlogSubscriber 注解, 指定 IBinlogEventHandler 需要注册到哪个客户端, 并且指定监听的 database 与 table。 @BinlogSubscriber(clientName = "master", database = "pear-admin", table ="sys_user") public class UserEventHandler implements IBinlogEventHandler<User> { @Override public void onInsert(BinlogEvent<User> event) { System.out.println("插入数据:" + event.getData()); } @Override public void onUpdate(BinlogEvent<User> event) { System.out.println("修改数据:" + event.getData()); } @Override public void onDelete(BinlogEvent<User> event) { System.out.println("删除数据:" + event.getData()); } } 多表监听 @BinlogSubscriber 注解 database 与 table 属性支持 Pattern 匹配, IBinlogEventHandler 在不指定泛型的情况下, event.getData() 为 Map<String, Object> 类型, 用于兼容不同表的数据结构。 @BinlogSubscriber(clientName = "master", database = ".*", table ="sys.*") public class UserEventHandler implements IBinlogEventHandler { @Override public void onInsert(BinlogEvent event) { System.out.println("数据库:" + event.getDatabase()); System.out.println("数据表:" + event.getTable()); System.out.println("新数据:" + event.getData()); } @Override public void onUpdate(BinlogEvent event) { System.out.println("数据库:" + event.getDatabase()); System.out.println("数据表:" + event.getTable()); System.out.println("原数据:" + event.getOriginalData()); System.out.println("新数据:" + event.getData()); } @Override public void onDelete(BinlogEvent event) { System.out.println("数据库:" + event.getDatabase()); System.out.println("数据表:" + event.getTable()); System.out.println("新数据:" + event.getData()); } } 监听 master 数据源所有数据库中所有表的数据变化【最灵活】 @BinlogSubscriber(clientName = "master") 监听 master 数据源 pear-admin 数据库中所有表的数据变化 @BinlogSubscriber(clientName = "master", database="pear-admin") 监听 master 数据源 pear-admin 数据库中 user 表的数据变化 @BinlogSubscriber(clientName = "master", database="pear-admin", table="user") 监听 master 数据源所有数据库中 user 表的数据变化 @BinlogSubscriber(clientName = "master", table="user") 监听 master 数据源所有数据库中以 user 开头的表的数据变化 @BinlogSubscriber(clientName = "master", table="user.*")

优秀的个人博客,低调大师

binlog4j 1.1.1 发布,Java 轻量级 binlog 客户端

Binlog4j 轻量级 Mysql Binlog 客户端, 提供宕机续读, 高可用集群等特性 项目特性 集群模式, 通过集群部署的方式,保证服务高可用。 宕机续读, 避免宕机期间造成数据丢失。 数据转换, 基于泛型封装 binlog Event 的序列化数据。 兼容 传统项目 与 Spring Boot / Cloud 项目。 兼容 Spring Boot 2.x 与 Spring Boot 3.x 版本。 更新日志(1.1.1) [移除] fast-json 依赖,使用 gson 代替。[修复] IBinlogEventHandler 接口 data 与 originalData 相反的问题。 下载安装 <dependency> <groupId>com.gitee.Jmysy</groupId> <artifactId>binlog4j-core</artifactId> <version>latest.version</version> </dependency> 或 implementation group: 'com.gitee.Jmysy', name: 'binlog4j-core', version: 'latest.version' 简单使用 通过 BinlogClient 创建 binlog 客户端, IBinlogEventHandler 用于接受 binlog 事件通知, 该接口允许使用泛型, 数据将遵循驼峰规则进行封装。 public class BootStrap { public static void main(String[] args) { BinlogClientConfig clientConfig = new BinlogClientConfig(); clientConfig.setHost("127.0.0.1"); clientConfig.setPort(3306); clientConfig.setUsername("root"); clientConfig.setPassword("taoren@123"); clientConfig.setServerId(1990); IBinlogClient binlogClient = new BinlogClient(clientConfig); binlogClient.registerEventHandler("database", "table", new IBinlogEventHandler() { @Override public void onInsert(BinlogEvent event) { System.out.println("插入数据:{}", event.getData()); } @Override public void onUpdate(BinlogEvent event) { System.out.println("修改数据:{}", event.getData()); } @Override public void onDelete(BinlogEvent event) { System.out.println("删除数据:{}", event.getData()); } }); binlogClient.connect(); } } 高级特性 通过 Persistence 配置为 true 启用宕机续读功能, Binlog4j 会将 binlog 的 filename 与 position 记录到 redis, 所以同时你需要设置 Redis 配置。 public class BootStrap { public static void main(String[] args) { RedisConfig redisConfig = new RedisConfig(); redisConfig.setHost("127.0.0.1"); redisConfig.setPort(6379); redisConfig.setPassword("taoren@123"); BinlogClientConfig clientConfig = new BinlogClientConfig(); clientConfig.setHost("127.0.0.1"); clientConfig.setPort(3306); clientConfig.setUsername("root"); clientConfig.setPassword("taoren@123"); clientConfig.setServerId(1990); // Client 编号 clientConfig.setRedisConfig(redisConfig); // Redis 配置 clientConfig.setPersistence(true); // 启用持久化 (宕机重启后, 从上次读取的位置开始) clientConfig.setMode(BinlogClientMode.cluster); // 高可用集群 BinlogClient binlogClient = new BinlogClient(clientConfig); binlogClient.registerEventHandler("database", "table", new IBinlogEventHandler<User>() { @Override public void onInsert(BinlogEvent<User> event) { System.out.println("插入数据:{}", event.getData()); } @Override public void onUpdate(BinlogEvent<User> event) { System.out.println("修改数据:{}", event.getData()); } @Override public void onDelete(BinlogEvent<User> event) { System.out.println("删除数据:{}", event.getData()); } }); binlogClient.connect(); } } Spring Boot Starter <dependency> <groupId>com.gitee.Jmysy</groupId> <artifactId>binlog4j-spring-boot-starter</artifactId> <version>latest.version</version> </dependency> 或 implementation group: 'com.gitee.Jmysy', name: 'binlog4j-spring-boot-starter', version: 'latest.version' 首先, 在 application.yml 中填写 BinlogClient 配置 spring: binlog4j: redis-config: host: 127.0.0.1 port: 6379 password: taoren@123 client-configs: master: username: root password: taoren@123 host: 127.0.0.1 port: 3306 serverId: 1990 使用 @BinlogSubscriber 注解, 指定 IBinlogEventHandler 需要注册到哪个客户端, 并且指定监听的 database 与 table。 @BinlogSubscriber(clientName = "master", database = "pear-admin", table ="sys_user") public class UserEventHandler implements IBinlogEventHandler<User> { @Override public void onInsert(BinlogEvent<User> event) { System.out.println("插入数据:" + event.getData()); } @Override public void onUpdate(BinlogEvent<User> event) { System.out.println("修改数据:" + event.getData()); } @Override public void onDelete(BinlogEvent<User> event) { System.out.println("删除数据:" + event.getData()); } } 多表监听, database 与 table 使用 Pattern 匹配, 泛型不应该再被使用, data 默认为 Map<String, Object> 类型 @BinlogSubscriber(clientName = "master", database = ".*", table ="sys.*") public class UserEventHandler implements IBinlogEventHandler<User> { @Override public void onInsert(BinlogEvent<User> event) { System.out.println("数据库:" + event.getDatabase()); System.out.println("数据表:" + event.getTable()); System.out.println("新数据:" + event.getData()); } @Override public void onUpdate(BinlogEvent<User> event) { System.out.println("数据库:" + event.getDatabase()); System.out.println("数据表:" + event.getTable()); System.out.println("原数据:" + event.getOriginalData()); System.out.println("新数据:" + event.getData()); } @Override public void onDelete(BinlogEvent<User> event) { System.out.println("数据库:" + event.getDatabase()); System.out.println("数据表:" + event.getTable()); System.out.println("新数据:" + event.getData()); } }

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册