从Reactor模式俯瞰Nginx,你会发现你与高手的差距就在设计模式上,不在内卷上

简介: 从Reactor模式俯瞰Nginx,你会发现你与高手的差距就在设计模式上,不在内卷上

640.png


深入了解Nginx 最终篇



前几篇文章主要是从Nginx特性和原理方面做了详细的讲解,让我们知道了Nginx是做什么的以及它为何如此高效,以至于全宇宙拿它来做负载均衡或者说web server。


但是如果你只是了解了使用和知道了原理就认为已经掌握了它,那只能说你肤浅了,原理和使用技能看看大家都知道了,没必要拿出去和别人拽,但凡你和别人说Nginx的epoll我清楚,Master-Worker是如何工作的,初级选手可能觉得你真牛,你真厉害,可是碰到高手了,你那最多只是熟悉了这个组件而已,你并没有多大的成长,而高手通过对Nginx的深入了解,他能发现其中的秘籍,这个秘籍可以帮助他触类旁通其他组件,从而用最短的时间掌握更多的技术,在互联网领域立足不败之地。


那么高手是如何通过对Nginx的学习使自己达到更高的境界呢?


设计模式

别笑,学会了设计模式,你就可以自豪的说出金庸先生在《倚天屠龙记》里九阳真经的口诀:他强由他强,清风拂山岗;他横由他横,明月照大江。他自狠来他自恶,我自一口真气足。懂得人都懂,不懂的人自悟。


原谅我说这么多,请开始正题Reactor模式


1. Reactor模式介绍


前面有写Reactor模式的文章,那篇文章主要是普及下概念,这次是重点介绍。

Reactor 模式是指通过一个或多个输入同时传递给服务处理器的服务请求的事件驱动处理模式服务端程序处理传入多路请求,并将它们同步分派给请求对应的处理线程Reactor 模式也叫 Dispatcher 模式


即I/O多路复用统一监听事件,收到事件后分发(Dispatch 给某进程),是编写高性能网络服务器的必备技术之一。


Reactor 模式中有 2 个关键组成:

  1. Reactor:Reactor在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序来对 IO 事件做出反应。它就像公司的电话接线员,它接听来自客户的电话并将线路转移到适当的联系人;
  2. Handlers:处理程序执行 I/O 事件要完成的实际事件,类似于客户想要与之交谈的公司中的实际官员。Reactor 通过调度适当的处理程序来响应 I/O 事件,处理程序执行非阻塞操作。


根据 Reactor 的数量和处理资源池线程的数量不同,有 3 种典型的实现:

  1. 单 Reactor 单线程
  2. 单 Reactor 多线程
  3. 主从 Reactor 多线程


下面详细介绍这 3 种实现方式。


2. 单 Reactor 单线程


640.png

其中,Select是前面 I/O 复用模型介绍的标准网络编程API,可以实现应用程序通过一个阻塞对象监听多路连接请求,其他方案示意图类似。


方案说明:

  1. Reactor 对象通过 Select 监控客户端请求事件,收到事件后通过 Dispatch 进行分发;
  2. 如果是建立连接请求事件,则由 Acceptor 通过 Accept 处理连接请求,然后创建一个 Handler 对象处理连接完成后的后续业务处理;
  3. 如果不是建立连接事件,则 Reactor 会分发调用连接对应的 Handler 来响应;
  4. Handler 会完成 Read→业务处理→Send 的完整业务流程。
  • 优点:模型简单,没有多线程、进程通信、竞争的问题,全部都在一个线程中完成(Reactor单线程)。
  • 缺点:性能问题,只有一个线程,无法完全发挥多核 CPU 的性能。Handler 在处理某个连接上的业务时,整个进程无法处理其他连接事件,很容易导致性能瓶颈。


3. 单 Reactor 多线程

640.png


方案说明:

  1. Reactor 对象通过 Select 监控客户端请求事件,收到事件后通过 Dispatch 进行分发;
  2. 如果是建立连接请求事件,则由 Acceptor 通过 Accept 处理连接请求,然后创建一个 Handler 对象处理连接完成后续的各种事件;
  3. 如果不是建立连接事件,则 Reactor 会分发调用连接对应的 Handler 来响应;
  4. Handler 只负责响应事件,不做具体业务处理,通过 Read 读取数据后,会分发给后面的 Worker 线程池进行业务处理;
  5. Worker 线程池会分配独立的线程完成真正的业务处理,最后将响应结果发给 Handler 进行处理;
  6. Handler 收到响应结果后通过 Send 将响应结果返回给 Client。
  • 优点:可以充分利用多核 CPU 的处理能力。
  • 缺点:多线程数据共享和访问比较复杂;Reactor 承担所有事件的监听和响应,在单线程中运行,高并发场景下容易成为性能瓶颈。


4. 主从 Reactor 多线程

640.png


针对单 Reactor 多线程模型中,Reactor 在单线程中运行,高并发场景下容易成为性能瓶颈,可以让 Reactor 在多线程中运行。


方案说明:

  1. Reactor 主线程 MainReactor 对象通过 Select 监控建立连接事件,收到事件后通过 Acceptor 接收,处理建立连接事件;
  2. Acceptor 处理建立连接事件后,MainReactor 将连接分配 Reactor 子线程给 SubReactor 进行处理;
  3. SubReactor 将连接加入连接队列进行监听,并创建一个 Handler 用于处理各种连接事件;
  4. 当有新的事件发生时,SubReactor 会调用连接对应的 Handler 进行响应;
  5. Handler 通过 Read 读取数据后,会分发给后面的 Worker 线程池进行业务处理;
  6. Worker 线程池会分配独立的线程完成真正的业务处理,最后将响应结果发给 Handler 进行处理;
  7. Handler 收到响应结果后通过 Send 将响应结果返回给 Client。
  • 优点:
  1. 父线程与子线程的数据交互简单职责明确,父线程只需要接收新连接,子线程完成后续的业务处理。
  2. 父线程与子线程的数据交互简单,Reactor 主线程只需要把新连接传给子线程,子线程无需返回数据。
  • 缺点:代码写起来太复杂了:即编程复杂度较高。

5. Reactor小结


3种模式可以用个比喻来理解:(餐厅常常雇佣接待员负责迎接顾客,当顾客入坐后,侍应生专门为这张桌子服务)

  1. 单 Reactor 单线程,接待员和侍应生是同一个人,全程为顾客服务;
  2. 单 Reactor 多线程,1 个接待员,多个侍应生,接待员只负责接待;
  3. 主从 Reactor 多线程,多个接待员,多个侍应生。


Reactor 模式具有如下的优点:

  • 响应快,不必为单个同步时间所阻塞,虽然 Reactor 本身依然是同步的;
  • 编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;
  • 可扩展性,可以方便的通过增加 Reactor 实例个数来充分利用 CPU 资源;
  • 可复用性,Reactor 模型本身与具体事件处理逻辑无关,具有很高的复用性。


6. Nginx利用的就是主从Reactor模式


但它和主从reactor模式又有一定的区别,区别主要就是这个master进程,这个master进程不同于一般的主从式reactor(一般的主从式reactor设计会是主reactor负责将连接accept下来,然后再将连接fd挂载到子reactor中),这个master进程的主要任务就是监听信号的,也就是对nginx的一些命令做处理,然后再将这些处理通过sockerpair()或者信号等方式通知给worker进程,master进程同时监控worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。同时,这个master进程负责listen这个整个服务器的监听fd,然后worker进程通过竞争accept_mutex互斥锁来将连接从全连接队列里取出来,然后进行后续的事件循环处理。


也就是说除了Master与主从Reactor中的主线程Reactor不同以外,Worker的处理流程和子线程Reactor的处理流程几乎一摸一样,用陈硕老师的话来说就是reactors in process


7. 小结


知道了Reactor模式之后再回头想想看看哪些你了解的服务或者中间件使用了此模式,要学会触类旁通才能更胜一筹,进而成为高手。这个模式还有一个最精妙的地方在于把复杂的问题通过"中间层"的方式简单化。计算机界不是有句老话:“凡是服务不能通过现有常规技术手段解决的,就加一个中间层来解决”。此话真的一语点醒梦中人,Reactor主从模式把频繁的accept过程,其他fd的并发IO处理过程以及业务处理逻辑部分分层,通过加层的方式让整个模式快速而高效的运行起来,这就是智慧,人类的智慧。


参考:

https://www.kancloud.cn/luoyoub/network-programming/2234086https://www.cxymm.net/article/m0_46201444/107969911http://ruiy.leanote.com/tag/%E5%A4%9A%E7%BA%BF%E7%A8%8B

相关文章
|
19天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:提升代码的可维护性与扩展性在软件开发过程中,设计模式是开发者们经常用到的工具之一。它们提供了经过验证的解决方案,可以帮助我们解决常见的软件设计问题。本文将介绍PHP中常用的设计模式,以及如何利用这些模式来提高代码的可维护性和扩展性。我们将从基础的设计模式入手,逐步深入到更复杂的应用场景。通过实际案例分析,读者可以更好地理解如何在PHP开发中应用这些设计模式,从而写出更加高效、灵活和易于维护的代码。
本文探讨了PHP中常用的设计模式及其在实际项目中的应用。内容涵盖设计模式的基本概念、分类和具体使用场景,重点介绍了单例模式、工厂模式和观察者模式等常见模式。通过具体的代码示例,展示了如何在PHP项目中有效利用设计模式来提升代码的可维护性和扩展性。文章还讨论了设计模式的选择原则和注意事项,帮助开发者在不同情境下做出最佳决策。
|
22天前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
15天前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
36 1
|
1月前
|
设计模式 Java Kotlin
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
本教程详细讲解Kotlin语法,适合希望深入了解Kotlin的开发者。对于快速学习Kotlin语法,推荐查看“简洁”系列教程。本文重点介绍了构建者模式在Kotlin中的应用与改良,包括如何使用具名可选参数简化复杂对象的创建过程,以及如何在初始化代码块中对参数进行约束和校验。
22 3
|
2月前
|
设计模式 算法 安全
设计模式——模板模式
模板方法模式、钩子方法、Spring源码AbstractApplicationContext类用到的模板方法
设计模式——模板模式
|
2月前
|
设计模式 数据库连接 PHP
PHP中的设计模式:如何提高代码的可维护性与扩展性在软件开发领域,PHP 是一种广泛使用的服务器端脚本语言。随着项目规模的扩大和复杂性的增加,保持代码的可维护性和可扩展性变得越来越重要。本文将探讨 PHP 中的设计模式,并通过实例展示如何应用这些模式来提高代码质量。
设计模式是经过验证的解决软件设计问题的方法。它们不是具体的代码,而是一种编码和设计经验的总结。在PHP开发中,合理地使用设计模式可以显著提高代码的可维护性、复用性和扩展性。本文将介绍几种常见的设计模式,包括单例模式、工厂模式和观察者模式,并通过具体的例子展示如何在PHP项目中应用这些模式。
|
2月前
|
设计模式 Java Spring
spring源码设计模式分析-代理设计模式(二)
spring源码设计模式分析-代理设计模式(二)
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
36 0
|
2月前
|
设计模式 Java
Java设计模式-工厂方法模式(4)
Java设计模式-工厂方法模式(4)
下一篇
无影云桌面