Reactor 模式的理论与实践
1. 引言
1.1 什么是 Reactor 模式?
Reactor 模式是一种用于处理高性能 I/O 的设计模式,专注于通过非阻塞 I/O 和事件驱动机制实现高并发性能。它的核心思想是将 I/O 操作的事件分离出来,通过事件分发器(Reactor)将事件传递给对应的事件处理器(Handler),从而避免了传统阻塞 I/O 模式下线程资源的浪费。
Reactor 模式的主要特点包括:
- 事件驱动:所有 I/O 操作都由事件触发并处理。
- 非阻塞:操作不会因为 I/O 而挂起,避免了线程等待的开销。
- 高效资源利用:通过少量线程处理大量并发连接,提升性能。
1.2 Reactor 模式的背景和应用场景
1.2.1 背景
在传统的阻塞 I/O 模型中,每个连接需要分配一个线程来处理请求,容易导致资源瓶颈。随着互联网应用的流量和并发量迅速增长,单线程或多线程阻塞模型的局限性愈发显著:
- 线程资源浪费:线程上下文切换开销大,尤其在高并发场景中。
- I/O 阻塞问题:单线程可能因一个阻塞操作而影响整体吞吐量。
- 扩展性差:线程数受限于服务器硬件,难以支持超大规模并发。
为了克服这些问题,非阻塞 I/O 模型(如 Java NIO)和基于事件驱动的设计模式(Reactor 模式)逐渐成为解决高并发需求的重要选择。
1.2.2 应用场景
Reactor 模式在以下场景中表现优异:
- 高性能网络服务器:例如 HTTP 服务器、FTP 服务器,常用 Reactor 模式实现高并发处理。
- 实时通信系统:如聊天应用、推送服务。
- 分布式消息队列:如 Kafka 等底层实现。
- 游戏服务器:需要处理大量的玩家连接与实时互动。
- 异步处理系统:需要快速响应并异步处理复杂任务的系统。
1.3 为什么选择 Java 实现?
1.3.1 强大的标准库支持
- Java 自 JDK 1.4 引入了 NIO(Non-blocking I/O)框架,为非阻塞式编程提供了基础设施,如
Selector
、Channel
和Buffer
。 - JDK 11 后进一步优化了异步 I/O 支持(如 AsynchronousChannel)。
1.3.2 成熟的生态系统
- Java 拥有 Netty 这样的高性能网络框架,已经将 Reactor 模式运用得炉火纯青。
- 大量社区资源和丰富的文档支持开发者学习和实践。
1.3.3 跨平台性和企业级应用支持
- Java 的“写一次,多平台运行”特性使得它适合在各种平台上实现高性能服务器。
- Java 在企业级应用中广泛使用,其生态(如 Spring Framework)能够与 Reactor 模式良好集成。
1.3.4 线程模型优化
- Java 提供了强大的线程池和并发工具包(如
ExecutorService
和ForkJoinPool
),便于优化多线程环境下的 Reactor 模式实现。
1.3.5 稳定性和安全性
- Java 的类型安全和异常处理机制使得开发非阻塞、高并发程序更安全可靠。
2. Reactor 模式的基础概念
2.1 同步与异步
在计算机科学中,同步与异步是指程序处理任务时的方式:
-
同步:调用方在发出请求后,必须等待请求处理完成才能继续执行后续操作。典型特征是调用阻塞,任务按顺序执行。
- 示例:线程读取文件内容,线程会阻塞直到文件读取完成。
-
异步:调用方在发出请求后无需等待,可以立即继续执行其他操作。任务完成后,系统会通过回调或事件通知调用方。
- 示例:异步读取文件内容,读取完成后通过事件机制通知线程处理结果。
在高并发系统中,异步方式更具优势,因为它避免了资源的长时间等待,提高了系统吞吐量。
2.2 阻塞与非阻塞
阻塞与非阻塞描述的是任务执行过程中调用方的状态:
-
阻塞:调用方发出请求后,如果任务未完成,会停在当前调用点等待,无法继续执行其他任务。
- 示例:使用
InputStream
读取网络数据时,调用会阻塞直到数据完全返回。
- 示例:使用
-
非阻塞:调用方发出请求后,如果任务未完成,会立即返回,可以继续执行其他任务。调用方需定期检查任务状态或通过回调获取结果。
- 示例:使用
Selector
监听多个Channel
,在没有 I/O 事件时,线程可以继续处理其他任务。
- 示例:使用
非阻塞通常与异步方式结合使用,能够避免资源浪费,提高系统性能。
2.3 单线程与多线程模型
在处理并发任务时,线程模型是一个重要的设计维度:
-
单线程模型:所有任务都在一个线程中顺序执行,设计简单,但在高并发场景下容易成为性能瓶颈。
- 示例:单线程 HTTP 服务器。
-
多线程模型:为每个任务分配一个线程并行执行,能够提高吞吐量,但线程数量过多可能导致资源开销大,线程上下文切换频繁。
- 示例:每个用户请求分配一个线程的传统 Web 服务器。
-
多线程优化模型:结合线程池与任务队列,通过有限线程处理大量并发任务,既提升性能又避免资源浪费。
- 示例:线程池管理的高性能 Web 服务器。
Reactor 模式多使用优化后的多线程模型,通过主从 Reactor 或线程池的方式分配任务。
2.4 I/O 多路复用
I/O 多路复用是一种高效处理多任务的技术,允许一个线程同时监听多个 I/O 事件。常见的多路复用机制包括:
- Selector(Java NIO 提供):通过一个线程监听多个通道的状态,任何通道有事件时都会被通知。
- Poll:Linux 提供的系统调用,监听一组文件描述符,检查是否有事件发生。
- Epoll:Linux 内核的高性能多路复用机制,支持更大规模的文件描述符监控。
I/O 多路复用的优点:
- 避免为每个 I/O 任务分配独立线程。
- 提高系统资源利用率,尤其是在处理大量并发连接时。
在 Java 中,Selector
是基于 I/O 多路复用的核心工具,广泛用于实现 Reactor 模式。
2.5 Reactor 模式与其他模式的对比
-
Reactor 模式:
- 采用非阻塞 I/O 和事件驱动。
- 更适合高并发和低延迟场景。
- 示例:基于 Java NIO 的服务器、Netty。
-
Proactor 模式:
- 核心是异步 I/O,I/O 操作由操作系统完成,完成后通知应用层。
- 更适合底层支持异步 I/O 的场景。
- 示例:Windows 下的 IOCP(I/O Completion Port)。
特性 | Reactor 模式 | Proactor 模式 |
---|---|---|
I/O 模型 | 非阻塞 I/O | 异步 I/O |
应用层责任 | 负责 I/O 调度和处理 | 仅负责处理完成的事件 |
适用场景 | 大多数高并发网络应用 | 操作系统支持异步 I/O 时使用 |
Reactor 模式由于其灵活性和对现有平台的兼容性,成为 Java 中实现高性能网络程序的主流选择。
3. Reactor 模式的核心设计
3.1 核心组成
Reactor 模式通过一套清晰的架构设计,实现了事件的高效分发和处理,其核心组件包括以下部分:
3.1.1 Reactor(事件分发器)
- 职责:负责监听 I/O 事件并将这些事件分发给相应的事件处理器(Handler)。
- 实现方式:通常基于 I/O 多路复用技术(如 Java 的
Selector
)。 - 核心特性:
- 非阻塞式地监听多个 I/O 通道(Channel)。
- 高效管理事件循环,确保低延迟和高吞吐量。
3.1.2 Acceptor(事件接收器)
- 职责:负责接收新的客户端连接,并为这些连接分配相应的 Handler。
- 实现方式:
- 在服务端监听套接字(ServerSocketChannel)上等待新的连接事件。
- 接收连接后,将其交由 Reactor 和具体 Handler 处理。
- 作用:
- 解耦客户端连接的接受与后续业务处理。
3.1.3 Handlers(事件处理器)
- 职责:处理具体的业务逻辑,如读取数据、写回响应。
- 实现方式:绑定到具体的 I/O 事件(如 READ、WRITE)并处理数据。
- 种类:
- 读处理器:处理 READ 事件,从通道中读取数据。
- 写处理器:处理 WRITE 事件,将数据写入通道。
- 业务处理器:完成业务逻辑处理,并将结果写入缓冲区。
3.2 单 Reactor 模型
3.2.1 模型结构
- Reactor 在一个线程中完成所有任务:
- 监听 I/O 事件(如连接、读写事件)。
- 分发事件给相应的 Handler。
- Handler 同步处理业务逻辑。
3.2.2 模型示意图
3.2.3 优点
- 架构简单,适合轻量级任务和低并发场景。
- 易于实现和调试。
3.2.4 缺点
- 单线程无法充分利用多核 CPU。
- 事件处理和业务逻辑可能会阻塞整个 Reactor。
3.3 多 Reactor 模型
3.3.1 主从 Reactor 结构
- 主 Reactor:
- 专注于接收客户端连接(Acceptor)。
- 将新的连接分发给子 Reactor。
- 子 Reactor:
- 负责监听和处理分配给它的 I/O 事件。
- 可以分配到多个线程,提高并发能力。
3.3.2 模型示意图
3.3.3 优点
- 利用多线程实现 I/O 和业务逻辑的并发处理。
- 主从分离,避免了单线程的瓶颈。
3.3.4 缺点
- 增加了系统复杂度。
- 子 Reactor 的负载均衡需要精心设计。
3.4 Reactor 模式与线程模型
Reactor 模式可以结合不同的线程模型,以适应不同的应用场景:
3.4.1 单线程模型
- 单线程负责 I/O 和业务处理。
- 简单但性能有限,适合低并发场景。
3.4.2 多线程模型
- Reactor 使用线程池处理任务。
- 每个事件处理由不同线程并行执行。
- 提高性能,但需要管理线程池和避免数据竞争。
3.4.3 主从多线程模型
- 主 Reactor 处理连接事件,分发任务给子线程。
- 子线程池负责业务逻辑处理,进一步提升并发能力。
- 适合高并发场景。
3.5 Reactor 模式的核心流程
3.5.1 流程描述
- 事件监听:Reactor 通过 I/O 多路复用监听多个通道。
- 事件分发:当有事件发生时,Reactor 将事件分发给对应的 Handler。
- 事件处理:Handler 执行业务逻辑,例如读取数据、处理请求、写入响应。
3.5.2 流程示意图
3.6 Reactor 模式的优势
- 高效性:基于非阻塞 I/O 和事件驱动,充分利用系统资源。
- 灵活性:支持多种线程模型,适应不同场景。
- 可扩展性:通过主从 Reactor 模型或线程池扩展并发能力。
4. Reactor 模式在 Java 中的实现
4.1 基于 Java NIO 的实现
4.1.1 Java NIO 核心组件
Java NIO 提供了实现 Reactor 模式的关键组件:
- Selector:支持 I/O 多路复用,允许一个线程监控多个通道的事件(如 READ、WRITE)。
- Channel:双向通信的抽象,分为
SocketChannel
和ServerSocketChannel
。 - Buffer:用于存储数据的内存区域,支持高效的数据读写操作。
4.1.2 简单 Reactor 模型实现
以下是基于 Java NIO 的单 Reactor 模型的实现步骤:
-
创建 ServerSocketChannel
- 配置为非阻塞模式。
- 将其注册到 Selector 并监听
OP_ACCEPT
事件。
-
初始化 Selector
- 使用
Selector.open()
创建 Selector。 - 通过
register()
将通道与事件绑定。
- 使用
-
事件循环
- 不断调用
select()
检查通道上是否有事件发生。 - 对每个事件调用相应的处理逻辑。
- 不断调用
-
事件处理
- 对
OP_ACCEPT
,接受新的连接并注册到 Selector。 - 对
OP_READ
,读取数据并进行业务处理。 - 对
OP_WRITE
,将响应写回客户端。
- 对
4.1.3 示例代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;public class ReactorServer {public static void main(String[] args) throws IOException {// 创建 Selector 和 ServerSocketChannelSelector selector = Selector.open();ServerSocketChannel serverChannel = ServerSocketChannel.open();serverChannel.bind(new InetSocketAddress(8080));serverChannel.configureBlocking(false);serverChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("Server started on port 8080");while (true) {// 等待事件selector.select();Iterator<SelectionKey> keys = selector.selectedKeys().iterator();while (keys.hasNext()) {SelectionKey key = keys.next();keys.remove();if (key.isAcceptable()) {handleAccept(key);} else if (key.isReadable()) {handleRead(key);}}}}private static void handleAccept(SelectionKey key) throws IOException {ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();SocketChannel clientChannel = serverChannel.accept();clientChannel.configureBlocking(false);clientChannel.register(key.selector(), SelectionKey.OP_READ);System.out.println("Accepted connection from " + clientChannel.getRemoteAddress());}private static void handleRead(SelectionKey key) throws IOException {SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(256);int bytesRead = clientChannel.read(buffer);if (bytesRead == -1) {clientChannel.close();System.out.println("Connection closed by client");return;}buffer.flip();String message = new String(buffer.array(), 0, buffer.limit());System.out.println("Received: " + message);// Echo back the messageclientChannel.write(ByteBuffer.wrap(("Echo: " + message).getBytes()));}
}
4.2 Netty 框架中的 Reactor 模式解析
4.2.1 Netty 的核心设计
Netty 是基于 Java NIO 的高性能网络框架,其内部实现了多线程多 Reactor 模型。关键点包括:
- EventLoopGroup:线程池,管理事件循环(Reactor)。
- ChannelPipeline:处理器链,负责 I/O 事件的流式处理。
- ChannelHandler:具体的业务逻辑处理器。
4.2.2 Netty 多 Reactor 模型结构
- 主 Reactor:监听连接请求,分发到子 Reactor。
- 子 Reactor:负责处理 I/O 读写事件。
- 业务处理线程池:执行复杂的业务逻辑,避免阻塞 Reactor。
4.2.3 使用 Netty 实现服务器
以下是使用 Netty 实现一个简单 Echo 服务器的示例:
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;public class NettyServer {public static void main(String[] args) throws InterruptedException {// 主 Reactor 线程组EventLoopGroup bossGroup = new NioEventLoopGroup(1);// 子 Reactor 线程组EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) {socketChannel.pipeline().addLast(new EchoServerHandler());}});System.out.println("Netty server started on port 8080");ChannelFuture future = bootstrap.bind(8080).sync();future.channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}static class EchoServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {ctx.writeAndFlush(msg); // Echo back the received message}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close();}}
}
4.3 单 Reactor 和多 Reactor 的对比
特性 | 单 Reactor 模型 | 多 Reactor 模型 |
---|---|---|
复杂度 | 简单 | 较高 |
并发性能 | 低 | 高 |
适用场景 | 低并发、简单业务逻辑 | 高并发、复杂业务场景 |
实现示例 | Java NIO 示例 | Netty 示例 |
5. Java 中 Reactor 模式的性能优化
在使用 Reactor 模式开发高性能系统时,性能优化是关键。以下是基于 Java 的 Reactor 模式优化策略,从线程管理、内存使用、网络通信到业务处理的多方面进行详细说明。
5.1 线程模型优化
5.1.1 使用线程池管理线程
- 问题:为每个连接分配一个线程可能导致大量线程切换,增加 CPU 开销。
- 优化:
- 使用
ExecutorService
或自定义线程池管理线程。 - 限制线程池的大小,避免线程资源过载。
- 示例:在 Netty 中,
EventLoopGroup
是一个高效的线程池模型,专门用于管理 I/O 和任务。
- 使用
ExecutorService threadPool = Executors.newFixedThreadPool(10);
// 在处理任务时提交到线程池
threadPool.submit(() -> processConnection(connection));
5.1.2 主从多线程模型
- 将连接处理(Acceptor)与 I/O 事件处理分离。
- 使用一个线程处理连接接入,多个线程处理读写事件,从而提高系统吞吐量。
- Netty 中的
bossGroup
和workerGroup
模型实现了这一思路。
5.2 I/O 操作优化
5.2.1 减少 I/O 操作次数
- 问题:频繁的 I/O 操作可能导致性能瓶颈。
- 优化:
- 使用
ByteBuffer
批量读取或写入数据。 - 合并多次写操作,减少系统调用。
- 使用
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (channel.read(buffer) > 0) {buffer.flip();process(buffer);buffer.clear();
}
5.2.2 使用直接内存(Direct Buffer)
- 问题:
HeapBuffer
会进行多次内存拷贝,影响性能。 - 优化:使用
ByteBuffer.allocateDirect()
分配直接内存,减少从堆到操作系统的内存拷贝。
ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);
channel.read(directBuffer);
5.3 网络通信优化
5.3.1 调整 Socket 参数
- 问题:默认的 Socket 参数可能限制网络性能。
- 优化:
- 调整
SO_RCVBUF
和SO_SNDBUF
缓冲区大小。 - 启用 TCP 的
NODELAY
参数,减少延迟。
- 调整
SocketChannel socketChannel = ServerSocketChannel.open().accept();
socketChannel.socket().setTcpNoDelay(true);
socketChannel.socket().setReceiveBufferSize(64 * 1024);
socketChannel.socket().setSendBufferSize(64 * 1024);
5.3.2 使用零拷贝技术
- 问题:传统数据传输需要多次拷贝数据。
- 优化:使用 Java NIO 的
FileChannel.transferTo()
实现零拷贝,大幅减少内核态和用户态之间的数据拷贝。
FileChannel fileChannel = new FileInputStream("data.txt").getChannel();
fileChannel.transferTo(0, fileChannel.size(), socketChannel);
5.4 内存管理优化
5.4.1 避免频繁的对象创建
- 问题:频繁分配和销毁对象可能导致垃圾回收压力增加。
- 优化:
- 使用对象池复用常用对象,如
ByteBuffer
或连接对象。 - Netty 中的
ByteBuf
提供了内存池支持。
- 使用对象池复用常用对象,如
5.4.2 减少垃圾回收影响
- 调整 JVM 垃圾回收器参数,根据业务负载优化 GC 行为。
- 对于延迟敏感的应用,推荐使用 G1 或 ZGC 垃圾回收器。
5.5 延迟与吞吐量优化
5.5.1 数据批量处理
- 问题:逐个事件处理可能导致性能低下。
- 优化:将多个事件批量处理,减少事件切换的开销。
List<SelectionKey> events = new ArrayList<>();
for (int i = 0; i < MAX_BATCH_SIZE && keys.hasNext(); i++) {events.add(keys.next());
}
events.forEach(this::processEvent);
5.5.2 减少上下文切换
- 问题:线程频繁切换会影响性能。
- 优化:使用
epoll
模式监听事件,减少线程竞争。 - 在 Java NIO 中,确保使用优化的操作系统底层实现(如
EpollSelector
)。
5.6 业务逻辑优化
5.6.1 异步处理长耗时任务
- 问题:阻塞 I/O 线程可能导致性能下降。
- 优化:将长耗时任务提交到独立线程池,避免阻塞事件处理线程。
CompletableFuture.runAsync(() -> processBusinessLogic(data), businessThreadPool);
5.6.2 使用高效的数据结构
- 根据业务场景选择合适的数据结构,如
ConcurrentHashMap
替代HashMap
,以提升并发性能。
5.7 Netty 框架的性能优化
Netty 已内置许多优化机制,以下是常用的优化策略:
-
使用 Pooled ByteBuf
- Netty 默认使用内存池,大幅提升了
ByteBuf
的分配效率。
- Netty 默认使用内存池,大幅提升了
-
调整线程数
- 根据服务器的核心数设置合理的
workerGroup
线程数,例如Runtime.getRuntime().availableProcessors() * 2
。
- 根据服务器的核心数设置合理的
-
启用 Epoll 模式
- 在 Linux 环境下,Netty 支持
EpollEventLoop
,比默认的NioEventLoop
性能更高。
- 在 Linux 环境下,Netty 支持
EventLoopGroup group = new EpollEventLoopGroup();
- 优化 ChannelPipeline
- 精简处理链路,仅保留必要的
ChannelHandler
,避免过多拦截器导致性能下降。
- 精简处理链路,仅保留必要的
5.8 性能测试与监控
5.8.1 负载测试工具
- 使用工具如
wrk
或JMeter
进行负载测试,评估优化效果。
5.8.2 性能监控
- 集成 APM 工具(如 Prometheus 或 Elastic APM),监控系统指标(如延迟、吞吐量和资源使用)。
6. Reactor 模式的典型应用场景
Reactor 模式以其高效的事件驱动机制和非阻塞 I/O 特性,广泛应用于需要高并发和低延迟的系统中。以下列举了几个典型的应用场景,并详细说明其在这些场景中的具体使用方式。
6.1 高性能网络服务器
6.1.1 应用场景
- HTTP 服务器:支持高并发的静态文件服务或动态内容服务。
- FTP 服务器:处理大量并发文件上传、下载的请求。
6.1.2 使用方式
- 连接管理:通过 Reactor 模式管理客户端的连接请求,避免为每个连接分配线程,提升系统并发能力。
- 数据处理:通过非阻塞 I/O 实现高效的数据读写。
6.1.3 案例
- Netty:一个高性能网络框架,广泛用于实现 HTTP、WebSocket 等网络协议的服务器。
- Tomcat:作为 Java 应用服务器,其底层使用 NIO 实现了高效的请求处理。
6.2 实时通信系统
6.2.1 应用场景
- 即时消息应用:如聊天软件(WhatsApp、微信)。
- 推送服务:如股票行情、天气预警。
6.2.2 使用方式
- 长连接管理:通过非阻塞 Socket 维护数百万级别的长连接。
- 事件触发:当有新消息到达或需要推送时,通过事件机制立即触发消息发送。
6.2.3 案例
- Netty:用于实现实时通信协议(如 MQTT、WebSocket)。
- Kafka:通过类似 Reactor 的事件驱动机制管理消息发布与订阅。
6.3 分布式消息队列
6.3.1 应用场景
- 消息传递系统:在分布式系统中用于异步通信。
- 任务队列:在后台任务处理系统中使用。
6.3.2 使用方式
- 事件驱动消费:Reactor 模式用于高效处理消息的读写操作。
- 高并发管理:通过 I/O 多路复用实现对大量生产者和消费者的支持。
6.3.3 案例
- Kafka:底层通过高效的 I/O 多路复用处理生产者和消费者的请求。
- RocketMQ:基于 NIO 实现了高性能的消息传递。
6.4 游戏服务器
6.4.1 应用场景
- 多人在线游戏:如 MMORPG 游戏(World of Warcraft)。
- 实时对战游戏:如 FPS 游戏(Counter-Strike)。
6.4.2 使用方式
- 事件循环处理玩家操作:通过 Reactor 模式监听玩家的实时操作(如移动、攻击)。
- 实时消息广播:将游戏状态实时推送给多个客户端。
6.4.3 案例
- Netty:被广泛用于构建高性能游戏服务器。
- Unity 后端服务:通过 Reactor 模式支持大规模玩家并发。
6.5 异步处理系统
6.5.1 应用场景
- 日志采集系统:如实时日志收集和分析。
- 流式处理系统:如物联网数据处理。
6.5.2 使用方式
- 非阻塞 I/O:通过 Reactor 模式高效读取和处理输入流数据。
- 事件触发处理:当数据到达时触发相应的处理器,无需轮询。
6.5.3 案例
- Flink:一个流处理框架,底层部分功能依赖类似 Reactor 的事件驱动机制。
- Logstash:实时日志采集工具,采用事件驱动机制处理日志。
6.6 微服务架构中的网关
6.6.1 应用场景
- API 网关:作为微服务架构的流量入口,负责路由、负载均衡和鉴权。
- 服务代理:如处理服务之间的通信和请求转发。
6.6.2 使用方式
- 高效路由:通过非阻塞 I/O 实现快速的请求转发。
- 并发处理:通过多线程 Reactor 模型处理大规模并发请求。
6.6.3 案例
- Spring Cloud Gateway:基于 Reactor 模式的非阻塞网关。
- Nginx:底层实现使用了类似 Reactor 的事件驱动模型。
6.7 物联网(IoT)系统
6.7.1 应用场景
- 设备连接管理:如智能家居设备与云端的交互。
- 实时数据传输:如传感器数据的采集与传输。
6.7.2 使用方式
- 高效连接管理:通过 Reactor 模式维护海量设备连接。
- 实时数据处理:通过事件触发机制处理设备上传的数据。
6.7.3 案例
- MQTT 服务器:如 HiveMQ 和 EMQX。
- AWS IoT Core:支持基于 Reactor 的事件驱动数据传输。
6.8 数据流系统
6.8.1 应用场景
- 流式 ETL(Extract-Transform-Load):实时数据的抽取、转换和加载。
- 事件流处理:如监控系统、告警系统。
6.8.2 使用方式
- 事件驱动处理:Reactor 模式可以将事件高效分发给不同的处理器。
- 非阻塞处理:通过非阻塞 I/O 实现流数据的实时处理。
6.8.3 案例
- Apache Storm:采用类似 Reactor 的事件处理机制。
- Apache Kafka Streams:基于 Kafka 构建的流式处理框架。
应用场景 | 典型使用方式 | 案例 |
---|---|---|
高性能网络服务器 | 非阻塞 I/O,事件驱动请求处理 | Netty、Tomcat |
实时通信系统 | 长连接管理,实时消息推送 | WhatsApp、Kafka |
分布式消息队列 | 高并发消息处理 | Kafka、RocketMQ |
游戏服务器 | 事件驱动玩家操作,实时消息广播 | Netty、Unity 后端服务 |
异步处理系统 | 非阻塞 I/O,事件触发 | Flink、Logstash |
微服务网关 | 高效路由,大规模并发处理 | Spring Cloud Gateway、Nginx |
物联网系统 | 高效连接管理,实时数据传输 | HiveMQ、AWS IoT Core |
数据流系统 | 实时事件处理,流式数据处理 | Apache Storm、Kafka Streams |
Reactor 模式通过其高效的事件驱动架构,适应了现代软件系统中的多种高并发场景,是高性能服务开发的核心设计之一。
7. 代码示例
以下代码示例展示了如何使用 Java NIO 和 Netty 实现基于 Reactor 模式的网络服务。代码分为两个部分:一个简单的 Java NIO Echo Server 和一个基于 Netty 的高性能服务器实现。
7.1 基于 Java NIO 的 Echo Server
这是一个简单的单 Reactor 模型实现的 Echo Server:
完整代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;public class NioEchoServer {public static void main(String[] args) throws IOException {// 创建 SelectorSelector selector = Selector.open();// 创建 ServerSocketChannel 并绑定端口ServerSocketChannel serverChannel = ServerSocketChannel.open();serverChannel.bind(new InetSocketAddress(8080));serverChannel.configureBlocking(false);// 将 ServerSocketChannel 注册到 SelectorserverChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("Echo server started on port 8080");while (true) {// 等待事件selector.select();Iterator<SelectionKey> keys = selector.selectedKeys().iterator();while (keys.hasNext()) {SelectionKey key = keys.next();keys.remove();if (key.isAcceptable()) {handleAccept(key);} else if (key.isReadable()) {handleRead(key);}}}}private static void handleAccept(SelectionKey key) throws IOException {ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();SocketChannel clientChannel = serverChannel.accept();clientChannel.configureBlocking(false);// 注册读事件clientChannel.register(key.selector(), SelectionKey.OP_READ);System.out.println("Accepted connection from " + clientChannel.getRemoteAddress());}private static void handleRead(SelectionKey key) throws IOException {SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(256);int bytesRead = clientChannel.read(buffer);if (bytesRead == -1) {clientChannel.close();System.out.println("Connection closed by client");return;}buffer.flip();String message = new String(buffer.array(), 0, buffer.limit());System.out.println("Received: " + message);// Echo 回消息clientChannel.write(ByteBuffer.wrap(("Echo: " + message).getBytes()));}
}
说明
- Selector:用于监听多个通道的 I/O 事件。
- ServerSocketChannel:非阻塞模式的服务端通道,用于接收连接。
- SocketChannel:非阻塞模式的客户端通道,用于数据读写。
- ByteBuffer:用于存储数据的缓冲区,支持高效的读写操作。
7.2 基于 Netty 的 Echo Server
Netty 是一个基于 Java NIO 的高性能网络框架,它提供了多线程多 Reactor 模型的实现,适合开发高性能的网络应用。
完整代码
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;public class NettyEchoServer {public static void main(String[] args) throws InterruptedException {// 创建两个线程组:bossGroup 用于接收连接,workerGroup 处理 I/O 事件EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 主 ReactorEventLoopGroup workerGroup = new NioEventLoopGroup(); // 子 Reactortry {ServerBootstrap bootstrap = new ServerBootstrap();bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // 使用 NIO 通道.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) {// 配置处理器链socketChannel.pipeline().addLast(new EchoServerHandler());}});System.out.println("Netty server started on port 8080");// 绑定端口并启动服务器ChannelFuture future = bootstrap.bind(8080).sync();future.channel().closeFuture().sync(); // 阻塞直到服务器关闭} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}// 具体的业务处理逻辑static class EchoServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) {// 收到数据后直接回写(Echo)System.out.println("Received: " + msg);ctx.writeAndFlush(msg);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {cause.printStackTrace();ctx.close(); // 发生异常时关闭连接}}
}
说明
- EventLoopGroup:用于管理线程池。
bossGroup
:监听客户端连接。workerGroup
:处理 I/O 事件。
- ChannelPipeline:责任链模式,用于管理多个处理器(
ChannelHandler
)。 - EchoServerHandler:业务逻辑处理器,处理读写操作。
7.3 单 Reactor 与多 Reactor 模型对比
特性 | Java NIO 实现 | Netty 实现 |
---|---|---|
模型 | 单 Reactor 模型 | 多 Reactor 模型 |
开发复杂度 | 手动实现事件循环 | 框架封装,易于扩展 |
性能 | 中等性能,适合中低并发 | 高性能,适合高并发 |
代码量 | 较多,需手动管理事件分发与处理 | 较少,业务逻辑聚焦 |
7.4 扩展示例:多 Reactor 模型的改进
如果需要更高并发性能,可以基于 Netty 的 bossGroup
和 workerGroup
实现主从 Reactor 模型,同时优化业务处理逻辑。
EventLoopGroup bossGroup = new NioEventLoopGroup(1); // 接收连接
EventLoopGroup workerGroup = new NioEventLoopGroup(8); // I/O 处理
通过分配更多 workerGroup
线程,可以处理更多并发连接,同时保证任务的高效执行。
- Java NIO 示例:适合学习和理解 Reactor 模式的原理。
- Netty 示例:用于生产级应用开发,支持高性能和复杂场景。
- 扩展性:通过优化线程池、事件分发机制,可以扩展为更复杂的多线程或多 Reactor 模型。
你可以根据业务需求选择合适的实现方式,确保性能和代码维护性达到最佳平衡。
8. Reactor 模式的局限性
尽管 Reactor 模式在处理高并发场景时表现出色,但其设计和实现过程中也存在一些局限性。以下从技术实现、场景适用性以及开发复杂度等角度分析 Reactor 模式的主要缺点。
8.1 开发复杂度高
-
事件驱动编程的复杂性:
- Reactor 模式采用事件驱动机制,代码逻辑通常是非线性的。
- 开发者需要设计复杂的事件分发和处理机制,对初学者不够友好。
-
错误处理和调试困难:
- 非阻塞 I/O 和多线程模型的结合可能导致问题分散在多个事件处理器中,调试起来难度较大。
- 异步事件处理的异常往往不容易追踪,容易导致潜在问题隐藏较深。
8.2 阻塞式操作难以避免
-
阻塞任务影响性能:
- 在事件处理器(Handler)中执行耗时或阻塞操作(如数据库查询、文件读取)可能导致 Reactor 的事件循环受阻,从而降低系统性能。
-
需要引入异步任务处理:
- 为了解决阻塞问题,往往需要引入线程池或消息队列进行异步任务处理,这会进一步增加系统复杂性。
8.3 高并发时的瓶颈
-
单线程 Reactor 的限制:
- 在单线程 Reactor 模型中,一个线程同时处理所有 I/O 和业务逻辑,无法充分利用多核 CPU 的性能。
- 如果事件处理逻辑较复杂,容易导致事件积压。
-
多线程模型的代价:
- 多线程 Reactor 模型通过线程池分担负载,但线程的上下文切换会带来额外的性能开销。
- 当线程池任务耗尽时,系统吞吐量会下降。
8.4 适用场景的局限性
-
不适合简单任务:
- 对于并发连接较少的系统(如小型工具或脚本),Reactor 模式的复杂性和资源开销可能不值得。
-
对业务逻辑复杂性有限制:
- 如果业务逻辑高度复杂,依赖多个外部服务或需要大量计算,Reactor 模式可能不是最佳选择。
8.5 依赖操作系统特性
-
底层 I/O 实现依赖操作系统:
- Reactor 模式通常依赖操作系统提供的非阻塞 I/O(如
epoll
、kqueue
)。在不支持高效非阻塞 I/O 的环境中,性能可能受到限制。
- Reactor 模式通常依赖操作系统提供的非阻塞 I/O(如
-
跨平台差异:
- 不同操作系统的 I/O 多路复用机制(如 Linux 的
epoll
和 Windows 的IOCP
)特性各异,可能需要额外适配工作。
- 不同操作系统的 I/O 多路复用机制(如 Linux 的
8.6 学习成本较高
- 对开发者的要求高:
- 开发者需要理解非阻塞 I/O、多线程编程以及事件驱动架构,学习成本较高。
- 除了技术知识外,还需要具备较强的调试能力和系统优化经验。
8.7 系统扩展性设计挑战
-
负载均衡难度:
- 在多线程 Reactor 模型中,如何高效地将任务分发给多个子 Reactor 是一大挑战。
- 如果负载均衡策略不佳,可能会导致某些线程过载。
-
资源管理复杂:
- Reactor 模式在处理大量连接时,需要有效管理内存、线程和文件描述符等资源,稍有不慎可能导致资源泄漏或系统崩溃。
解决方案与优化建议
尽管 Reactor 模式存在上述局限性,但通过以下方式可以缓解其问题:
-
使用高性能框架:
- 借助 Netty 等框架,减少开发者实现复杂事件驱动逻辑的负担。
-
引入异步任务处理:
- 使用线程池或异步框架(如 CompletableFuture)处理阻塞任务。
-
合理分配线程:
- 根据系统负载调整 Reactor 和线程池的大小,避免资源浪费或过载。
-
优化事件分发:
- 在多线程模型中,使用高效的负载均衡算法(如哈希分发)均衡任务。
局限性 | 影响 | 解决建议 |
---|---|---|
开发复杂度高 | 事件驱动代码难以维护 | 使用框架(如 Netty)降低复杂度 |
阻塞操作影响性能 | 阻塞任务阻塞事件循环 | 引入异步任务处理机制 |
高并发下的瓶颈 | 单线程模型无法利用多核 CPU | 采用多 Reactor 模型并优化线程池 |
操作系统特性依赖 | 不同平台支持的非阻塞 I/O 不同 | 针对目标平台进行优化 |
学习成本高 | 理解非阻塞 I/O 和事件驱动编程门槛高 | 提供清晰的文档与示例,逐步提升开发能力 |
负载均衡设计难度 | 任务分配不均可能导致性能瓶颈 | 使用高效的负载均衡策略 |
Reactor 模式尽管存在局限性,但通过合理的架构设计与优化方案,仍然是高性能、高并发系统开发中的重要工具。
9. 实践建议
在开发基于 Reactor 模式的高性能系统时,为了充分发挥其优势并规避局限性,开发者需要遵循一些最佳实践。以下从设计、实现、优化和运维等方面提供了实践建议。
9.1 设计阶段的建议
9.1.1 明确使用场景
- 适用场景:Reactor 模式适用于高并发、低延迟的场景,如实时通信、游戏服务器、微服务网关等。
- 不适用场景:对于低并发或阻塞操作较多的场景,不建议直接使用 Reactor 模式。
9.1.2 选择合适的模型
- 单 Reactor 模型:适合并发量低、逻辑简单的应用。
- 多 Reactor 模型:适合高并发场景,通过线程池分担任务。
- 主从 Reactor 模型:推荐用于需要高性能和高扩展性的分布式系统。
9.1.3 考虑扩展性
- 设计时预留负载均衡策略,例如使用一致性哈希将任务均匀分配到多个子 Reactor。
- 考虑横向扩展能力,如在系统负载增加时增加子 Reactor 或服务实例。
9.2 实现阶段的建议
9.2.1 使用成熟框架
- Netty:提供了完整的 Reactor 模式实现,封装了复杂的 I/O 操作和事件处理机制。
- Spring WebFlux:基于 Reactor 模式的响应式编程框架,适合构建微服务。
9.2.2 优化事件处理
- 避免在事件处理器中执行长时间的阻塞操作,例如数据库查询或外部服务调用。
- 引入异步任务处理机制,将耗时操作交由独立线程池处理。
9.2.3 高效使用资源
- 使用非阻塞 I/O 和直接内存(Direct Buffer)提高 I/O 性能。
- 限制线程池大小,避免线程过多导致上下文切换。
9.3 性能优化建议
9.3.1 优化 I/O 操作
- 使用批量数据读写减少系统调用的次数。
- 配置合适的 Socket 参数,例如调整接收/发送缓冲区大小 (
SO_RCVBUF
和SO_SNDBUF
),启用TCP_NODELAY
以减少延迟。
9.3.2 使用零拷贝技术
- 在需要传输大文件时,使用
FileChannel.transferTo
或类似的零拷贝技术,减少用户态与内核态之间的数据拷贝。
9.3.3 监控和调优
- 配置 JVM 的垃圾回收器参数(如 G1 或 ZGC)以降低延迟。
- 定期监控系统的吞吐量、延迟、CPU 使用率以及线程池状态,及时发现和优化瓶颈。
9.4 异常处理建议
9.4.1 捕获异常
- 在事件处理器中捕获所有可能的异常,防止异常传播导致整个 Reactor 停止工作。
try {// 处理事件
} catch (Exception e) {log.error("Error occurred during event handling", e);
}
9.4.2 资源清理
- 确保连接关闭时清理相关资源,例如关闭
SocketChannel
和释放ByteBuffer
。
9.4.3 监控未处理的异常
- 集成日志或 APM 工具(如 Prometheus 或 ELK Stack)以记录和追踪未处理的异常。
9.5 测试阶段的建议
9.5.1 进行负载测试
- 使用工具如 wrk、JMeter 或 Gatling 对系统进行压力测试,评估其在高并发场景下的性能。
9.5.2 模拟真实场景
- 模拟高并发场景下的各种情况,例如连接突然增加、网络抖动或长时间未响应的客户端。
9.5.3 测试边界条件
- 测试最大连接数、极限数据传输速率和极端延迟情况下的系统行为。
9.6 运维阶段的建议
9.6.1 实时监控
- 使用 APM 工具(如 SkyWalking、Pinpoint)监控系统性能,包括线程池使用率、事件处理延迟等。
- 定期检查系统日志,关注连接状态和异常堆栈。
9.6.2 弹性扩展
- 配置负载均衡器(如 Nginx、HAProxy),将负载分摊到多个服务实例。
- 结合容器化技术(如 Docker 和 Kubernetes),动态调整服务实例数量以应对流量变化。
9.6.3 设置报警机制
- 配置 CPU、内存、连接数的监控阈值,超过阈值时自动报警,便于快速响应。
阶段 | 实践建议 |
---|---|
设计阶段 | 明确适用场景,选择合适的模型,考虑扩展性 |
实现阶段 | 使用成熟框架,优化事件处理,高效使用资源 |
性能优化 | 优化 I/O 操作,使用零拷贝技术,监控系统性能 |
异常处理 | 捕获异常,清理资源,记录未处理的异常 |
测试阶段 | 进行负载测试,模拟真实场景,测试边界条件 |
运维阶段 | 实时监控系统,弹性扩展,设置报警机制 |
10. 总结与展望
10.1 总结
Reactor 模式作为一种高性能、高并发的设计模式,以其事件驱动和非阻塞 I/O 的特点,在现代网络应用中得到了广泛应用。本篇文章全面介绍了 Reactor 模式的基础概念、核心设计、Java 中的实现以及典型应用场景,同时分析了其局限性,并提出了具体的实践建议。
核心要点:
- 高效并发:通过事件驱动和 I/O 多路复用机制,Reactor 模式能够高效处理大规模并发连接。
- 灵活性:支持单线程、多线程以及主从多 Reactor 模型,可根据需求选择合适的实现方式。
- 生态支持:在 Java 中,Netty 等成熟框架的支持降低了开发复杂度,提升了系统开发效率。
- 优化与实践:通过线程管理、I/O 优化、资源使用和监控,Reactor 模式可以进一步提升性能,满足复杂场景需求。
尽管如此,Reactor 模式也存在一些局限性,如开发复杂度高、阻塞任务难以避免等。合理选择场景和实现方式,结合最佳实践,可以有效规避这些问题。
10.2 展望
随着网络规模和并发需求的进一步增长,Reactor 模式在未来的网络系统开发中依然会占据重要地位。以下是未来可能的发展方向和趋势:
10.2.1 与新技术的结合
- 响应式编程:
- Reactor 模式的事件驱动机制与响应式编程理念高度契合。未来,Reactor 模式可能会更紧密地与响应式框架(如 Spring WebFlux、Project Reactor)结合,简化开发者处理异步数据流的流程。
- 微服务架构:
- 在微服务架构中,Reactor 模式可用于构建高效的 API 网关和异步通信机制,进一步推动分布式系统的性能优化。
10.2.2 硬件优化
- 利用现代硬件特性:
- 随着硬件性能的提升(如更快的网络接口卡、支持 RDMA 的网络设备),Reactor 模式可以结合零拷贝和直接 I/O 技术,进一步提升数据传输效率。
- 支持高并发处理器:
- 针对多核 CPU 和 NUMA 架构优化的多线程 Reactor 模型将成为高性能网络服务的标配。
10.2.3 工具与框架的演进
- 框架自动化优化:
- 像 Netty 这样的框架可能会进一步增强对底层硬件的支持,例如智能选择最佳的 I/O 模式(
epoll
、kqueue
等)。
- 像 Netty 这样的框架可能会进一步增强对底层硬件的支持,例如智能选择最佳的 I/O 模式(
- 简化开发体验:
- 提供更高级别的抽象层,让开发者专注于业务逻辑,而无需过多关心底层细节。
10.2.4 多模式混合架构
- Reactor 与 Proactor 结合:
- 未来可能出现混合模式,将 Reactor 的灵活性与 Proactor 的高性能异步处理机制结合,用于更高吞吐量和低延迟的场景。
- 结合 AI 优化调度:
- 使用人工智能动态优化事件分发和资源调度,以最大化系统性能。
10.2.5 新兴应用场景
- 物联网:
- 随着物联网设备的快速增长,Reactor 模式可以在支持海量设备连接、实时数据处理方面发挥更大作用。
- 边缘计算:
- 在分布式边缘计算环境中,Reactor 模式可用于高效的节点间通信和分布式任务调度。
10.3 结束语
Reactor 模式已经从一种设计理念发展成为实际工程中不可或缺的工具。在高并发、大规模、实时响应的系统中,它为开发者提供了一种高效的解决方案。展望未来,随着硬件性能的提升、新框架的涌现和技术生态的扩展,Reactor 模式将继续为高性能网络服务提供动力,并在更多创新场景中发挥作用。
相关文章:

Reactor 模式的理论与实践
1. 引言 1.1 什么是 Reactor 模式? Reactor 模式是一种用于处理高性能 I/O 的设计模式,专注于通过非阻塞 I/O 和事件驱动机制实现高并发性能。它的核心思想是将 I/O 操作的事件分离出来,通过事件分发器(Reactor)将事…...
vim 一次注释多行 的几种方法
在 Vim 中一次注释多行是一个常见操作。可以使用以下方法根据你的具体需求选择合适的方式: 方法 1:手动插入注释符 进入正常模式: 按 Esc 确保进入正常模式。 选择需要注释的多行: 移动到第一行,按下 Ctrlv 进入可视块…...

问题记录-Java后端
问题记录 目录 问题记录1.多数据源使用事务注意事项?2.mybatis执行MySQL的存储过程?3.springBoot加载不到nacos配置中心的配置问题4.服务器产生大量close_wait情况 1.多数据源使用事务注意事项? 问题:在springBoot项目中多表处理数…...

李春葆《数据结构》-课后习题代码题
一:假设不带权有向图采用邻接矩阵 g 存储,设计实现以下功能的算法: (1)求出图中每个顶点的入度。 代码: void indegree(MatGraph g){int i,j,n;printf("各个顶点的入度:\n");for(i…...

51c~C语言~合集2
我自己的原文哦~ https://blog.51cto.com/whaosoft/12652943 一、嵌入式开发中的C语言编译器 如果你和一个优秀的程序员共事,你会发现他对他使用的工具非常熟悉,就像一个画家了解他的画具一样。----比尔.盖茨1 不能简单的认为是个工具 嵌入式程序开发…...
【Python】构建事件驱动架构:用Python实现实时应用的高效系统
解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 事件驱动架构(Event-Driven Architecture,EDA)是一种基于事件流动进行系统设计的模式,广泛应用于游戏开发、实时监控和分布式系统中。它通过解耦事件的生产者和消费者,提升系统的可扩展性和灵活性。本文章从…...

Git(一)基本使用
目录 一、使用git -v 查看安装git版本 二、使用mkdir 创建一个文件,并使用 git init 在该目录下创建一个本地仓库, 三、通过git clone命令接入线上仓库 四、使用git status查看仓库状态信息 五、利用echo写入一个文件 并使用cat进行查看 【Linux】e…...
HarmonyOS应用开发者基础认证,Next版本发布后最新题库(10月8日更新题库未收录)
笔者会尽量找到答案的出处,力求答案准确无误。有些题目答案可能有错,也有一些笔者实在找不到出处,也不知道答案的,如果读者发现错误或有补充建议,欢迎评论或私信笔者。您的每一条反馈都是宝贵的,能够帮助笔…...

【PGCCC】Postgresql BRIN 索引原理
前言 postgresql 提供了块级索引(简称 BRIN),主要适用于类似时序数据之类的,有着天然的顺序,而且都是添加写的场景。相比于 btree 索引,它的体积小得多,非常适用于大数据量的场景。 原理 pos…...

腾讯云 AI 代码助手:产品研发过程的思考和方法论
一、文章摘要 本文将详细阐述 腾讯云 AI 代码助手的历史发展形态与产品整体架构,并从技术、研发方法论的角度分别阐述了产品的研发过程。 全文阅读约 5~8 分钟。 二、产品布局 AI 代码助手产品经历了三个时代的发展 第一代诸如 Eclipse、Jetbrains、V…...

Matlab 深度学习 PINN测试与学习
PINN 与传统神经网络的区别 与传统神经网络的不同之处在于,PINN 能够以微分方程形式纳入有关问题的先验专业知识。这些附加信息使 PINN 能够在给定的测量数据之外作出更准确的预测。此外,额外的物理知识还能在存在含噪测量数据的情况下对预测解进行正则…...
【Angular】async详解
在 Angular 中,async 关键字用于定义异步函数,通常与 await 一起使用来处理 Promise。这使得异步代码看起来更像同步代码,从而更容易理解和维护。 基本用法 定义异步函数:使用 async 关键字。等待 Promise 解析:使用…...

抖音SEO矩阵系统:开发技术分享
市场环境剖析 短视频SEO矩阵系统是一种策略,旨在通过不同平台上的多个账号建立联系,整合同一品牌下的各平台粉丝流量。该系统通过遵循每个平台的规则和内容要求,输出企业和品牌形象,以矩阵形式增强粉丝基础并提升商业价值。抖音作…...

SpringBoot集成minio,并实现文件上传
SpringBoot集成minio 什么是minioSpringBoot集成minio1、引入minio依赖2、配置Minio相关参数3、在代码里读取自定义的minio配置4、在minio配置类里,注册ConfigurationProperties实现文件上传到minio1、利用SpringMVC实现接口的异常全局处理2、返回文件路径给前端3、返回文件流…...
centos为用户赋予sudo权限
在CentOS系统中,要为用户test赋予sudo权限,你需要按照以下步骤操作: 确保sudo包已安装: 如果系统中没有安装sudo,你可以通过yum(CentOS 7及以下)或dnf(CentOS 8及以上)来…...

SAP 零售方案 CAR 系统的介绍与研究
前言 当今时代,零售业务是充满活力和活力的业务领域之一。每天,由于销售运营和客户行为,它都会生成大量数据。因此,公司迫切需要管理数据并从中检索见解。它将帮助公司朝着正确的方向发展他们的业务。 这就是为什么公司用来处理…...
Android Framework AudioFlinge 面试题及参考答案
目录 请解释什么是 AudioFlinger? AudioFlinger 在 Android 系统中的位置是什么? AudioFlinger 的主要职责有哪些? AudioFlinger 如何管理音频流? 在 AudioFlinger 中,什么是音频会话? 请简述 AudioFlinger 的工作流程。 AudioFlinger 是如何与硬件交互的? 在 A…...
嵌入式系统与单片机工作原理详解
随着现代科技的发展,嵌入式系统已经深入到我们日常生活中的方方面面。无论是智能家居、汽车电子,还是工业控制、医疗设备,都离不开嵌入式系统的支持。而单片机作为嵌入式系统的核心组件,是实现这些功能的关键之一。本文将详细介绍…...

Diving into the STM32 HAL-----Timers笔记
嵌入式设备会按时间执行某些活动。对于真正简单且不准确的延迟,繁忙的循环可以执行任务,但是使用 CPU 内核执行与时间相关的活动从来都不是一个聪明的解决方案。因此,所有微控制器都提供专用的硬件外设:定时器。定时器不仅是时基生…...
对比 MyBatis 批处理 BATCH 模式与 INSERT INTO ... SELECT ... UNION ALL 进行批量插入
前言 在开发中,我们经常需要批量插入大量数据。不同的批量插入方法有不同的优缺点,适用于不同的场景。本文将详细对比两种常见的批量插入方法: MyBatis 的批处理模式。使用 INSERT INTO ... SELECT ... UNION ALL 进行批量插入。 MyBatis …...

网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
【决胜公务员考试】求职OMG——见面课测验1
2025最新版!!!6.8截至答题,大家注意呀! 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:( B ) A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
Mysql8 忘记密码重置,以及问题解决
1.使用免密登录 找到配置MySQL文件,我的文件路径是/etc/mysql/my.cnf,有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
32单片机——基本定时器
STM32F103有众多的定时器,其中包括2个基本定时器(TIM6和TIM7)、4个通用定时器(TIM2~TIM5)、2个高级控制定时器(TIM1和TIM8),这些定时器彼此完全独立,不共享任何资源 1、定…...
[USACO23FEB] Bakery S
题目描述 Bessie 开了一家面包店! 在她的面包店里,Bessie 有一个烤箱,可以在 t C t_C tC 的时间内生产一块饼干或在 t M t_M tM 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC,tM≤109)。由于空间…...