面试官:谈谈你对Reactor模型的理解?

Reactor 模型是一种事件驱动的高性能网络编程模型,主要用于处理高并发的网络 I/O 请求。其核心思想是通过一个或多个线程监听事件,并将事件分发给相应的处理程序,从而实现高效的并发处理。

为什么要有Reactor模型?

在高并发下,只有多路复用技术,以及 NIO 是不行的,因为 NIO 只是 Java 提供的非阻塞 I/O 库,它是通过轮询的方式检测事件变化的,所以效率不算太高,而 Reactor 模型可以通过监听事件(非轮询的方式,例如 Linux 下的 epoll 技术)来实现更高效的 IO 编程。

特征

Reactor 模型的主要特征如下:

  1. 事件驱动:所有 I/O 操作都由事件触发并处理。
  2. 非阻塞:操作不会因为 I/O 而挂起,避免了线程等待的开销。
  3. 高效资源利用:通过少量线程处理大量并发连接,提升性能。
  4. 组件分离:将事件监听(Reactor)、事件分发(Dispatcher)和事件处理(Handler)解耦,使代码结构更清晰。

组成

Reactor 模型的核心组件包括:

  1. Reactor:负责监听和分发事件,通常基于 I/O 多路复用技术,如 epoll。
  2. Acceptor:负责接收新的客户端连接,并将其注册到 Reactor 中。
  3. Handler:负责处理具体的 I/O 事件和业务逻辑。

实现模式

Reactor 模型实现模式总共有以下三种:

  1. 单线程 Reactor 模型:所有操作在一个线程完成,适用于低并发场景。
  2. 多线程 Reactor 模型:主线程处理连接,子线程池处理 I/O 和业务。
  3. 主从 Reactor 模型:主线程池处理连接,子线程池处理 I/O(进一步优化资源分配)。

它们的具体区别如下。

单线程 Reactor

alt

执行流程

  1. Reactor 通过监听客户端请求事件(如连接、读、写)。
  2. 如果是连接事件,Acceptor 通过 accept 接受连接,并注册到 Reactor 中,之后创建一个 Handler 处理后续事件。
  3. 如果是读写事件,Reactor 调用对应的 Handler 处理,完成 read -> 业务处理 -> send 的完整流程。

优点

模型简单,没有多线程、进程通信和竞争问题,所有操作在一个线程中完成。

缺点

  • 性能瓶颈:只有一个线程,无法充分利用多核 CPU 的性能。当 Handler 处理业务时,无法处理其他连接事件。
  • 可靠性问题:如果线程意外终止或进入死循环,整个系统将不可用。

适用场景

客户端数量有限,业务处理非常快速。

多线程 Reactor

alt

执行流程

  1. Reactor 通过监听客户端请求事件。
  2. 如果是连接事件,Acceptor 通过 accept 接受连接,并注册到 Reactor 中,之后创建一个 Handler 处理后续事件。
  3. 如果是读写事件,Reactor 调用对应的 Handler 处理。
  4. Handler 只负责读取数据,将业务处理交给 Worker 线程池。
  5. Worker 线程池 完成业务处理,将结果返回给 Handler,由 Handler 发送给客户端。

优点

充分利用多核 CPU 的性能,提高任务处理能力。

缺点

  • 多线程数据共享和访问复杂,涉及线程同步问题。
  • Reactor 仍然是单线程,高并发时可能成为性能瓶颈。

适用场景

• 并发需求较高,但任务处理逻辑简单的场景。

主从 Reactor 多线程

alt

执行流程

  1. MainReactor(主线程)通过监听连接建立事件,由 Acceptor 处理连接请求。
  2. MainReactor 将新连接分配给 SubReactor(子线程)。
  3. SubReactor 将连接加入监听队列,并创建 Handler 处理后续事件。
  4. 当事件发生时,SubReactor 调用对应的 Handler 处理。
  5. Handler 读取数据后,将业务处理交给 Worker 线程池。
  6. Worker 线程池 完成业务处理,将结果返回给 Handler,由 Handler 发送给客户端。

优点

  • 职责明确:主线程只负责接收新连接,子线程负责业务处理。
  • 性能优化:主线程和子线程交互简单,适合高并发场景。

缺点

编程复杂度较高。

适用场景

高并发场景,例如 Nginx、Netty。

结论

模型优点缺点适用场景
单线程 Reactor 模型简单,无多线程竞争问题 性能瓶颈,可靠性差 客户端少,业务处理快
多线程 Reactor 充分利用多核 CPU 性能 多线程数据共享复杂,Reactor 单线程可能成为瓶颈 并发需求高,任务处理简单
主从 Reactor 职责明确,性能优化,适合高并发 编程复杂度高 高并发场景(如 Nginx、Netty)

使用场景

Reactor 模型应用在 Nginx、Netty、Kafka 以及 Redis 等框架中。

小结

Reactor 模型是一种实现思路(也是一种设计模式),它是通过事件驱动和非阻塞 I/O 机制,实现了高并发、高性能的网络编程。其核心思想是通过一个或多个线程监听事件,并将事件分发给相应的处理程序,从而实现高效的并发 IO 处理。

#java##八股文#
Java面试精讲 文章被收录于专栏

Java常见面试题、场景题、企业真题精讲。

全部评论

相关推荐

11-13 12:33
已编辑
武汉大学 Java
1. 为什么要走 java 这条路2. 是否有考研计划3. 自学的学习路线是怎么样的4. 平时学习和自学的时间是怎么安排的5. 介绍项目6. 浏览器发起请求到后端发生校验整体的执行流程是怎么样的7. sa-token 进行注解式权限校验的原理是怎么样的8. 假设现在有一个请求是需要将一个图片共享出去,给一个没有登陆的人来访问,你会考虑怎么实现,并且限制共享的时间为一个小时,基于你现有的权限框架,你会怎么扩展9. 假设我在 59 分的时候拿到你共享出来的这个链接了,61分的时候还能继续用吗,结合你刚说的 satoken 框架的token 刷新机制来说一下10. 你说你选择使用临时token 的方案,如果每个用户都是用这个临时token, 那么会不会显得太笨重了,你有没有什么其他的方案11. 你说你使用了模板方法和门面模式,你的抽象类和子类之间的方法都有哪些,你的抽象类的骨架是怎么样的,有哪些抽象方法,作用分别是什么12. 为什么没有考虑不直接在前端上传文件13. 你说你通过手动事务的方式来解决云存储上传成功但是数据库更新失败从而产生孤儿文件的情况,你通过回滚来删除云存储已经上传大的文件信息,那么这样对于用户体验是否友好,用户明明点了上传,但是提示上传失败,你可以考虑怎么优化,我答的用重试机制来代替回滚14. 断点续传是怎么做的,在网络不好的时候是怎么解决的15. 在你当初设计者模块的时候是怎么考虑的,有没有从用户的角度去思考,我答了异步上传来优化体验,又追问要是前端用户刷新了浏览器,那文件上传不就断开了吗,这里确实没有考虑到16. 前面你提到了 redis key 的过期策略,讲一下 redis 中过期key 是怎么被删除的17. 当时发现首页图片加载慢的瓶颈是在哪里,怎么定位到的18. 为什么把图片数据缓存在了jvm 缓存或者是 redis 中,二进制的数据不适合做缓存,你有考虑过吗19. 假设现在你在写代码的过程中突然告诉你缓存不能这么用,你会如何调整或者重构你的方案,你的优化思路是什么20. 当时为什么选择使用云存储来存储图片而不是使用数据库来存储数据信息21. 多人协同编辑是怎么实现的,是否可以查看编辑轨迹,是否支持回滚,如果要你支持,你怎么实现22. 是否每一次编辑都会度图片进行存储,答只有最后一个人退出编辑才会将数据存回云存储,追问怎么知道哪一个人是最后一次退出编辑的,怎么监听的,答通过 websocket 的断开连接回调函数23. 你的项目是否支持水平扩展24. mysql 什么是最左匹配原则25. 慢 sql 排查思路26. mybatis 如何防止 sql 注入27. 分页查询中mybatis 是如何映射到实际的sql 语句的28. 在使用框架的时候是否有看过源码,对哪些源码印象比较深刻29. 算法 LRU,本地 ide,15分钟内完成全程一个半小时,结束后一小时约二面
查看29道真题和解析
点赞 评论 收藏
分享
评论
点赞
11
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务