Egg 框架简述 (四):Worker 和 高效负载均衡

  1. 简单的骨架认知
  2. 插件使用(Plugins)
  3. 持久层方案(egg-sequelize)
  4. Worker 和 高效负载均衡

4-1. CPU、操作系统和进程
4-2. 进程和线程
4-3. 单线程多进程模型
4-4. Master 和 Worker

  1. Agent 代理角色
  2. 定时任务

笔者的其他文章推荐: 《JS 函数式编程思维简述》

前言

       Worker 是 Egg 中对于多进程模型的具体实现。因此,在介绍 Worker 前,我们先浅谈一下什么是多进程模型。

CPU、操作系统和进程

       CPU 是计算机中的计算单元,其能力和行为也非常单纯:接收到指令进行计算,将计算结果返回。而并不关注计算谁,由谁来进行调度。
       操作系统 用于调度计算资源,每个操作系统都会遵循一定的规则,来调度分配,谁当前能够得到计算机的计算资源,也就是在当前的这一刻,CPU 来计算哪个程序分配的什么任务。
       应用程序 是一系列用于完成某种特定功能的代码段集合,在尚未执行时,存储于计算机的磁盘之中。当开始运行时,则会加载到内存之中。
       进程 则是表示一个正在运行中的应用程序

Egg 框架简述 (四):Worker 和 高效负载均衡_第1张图片
image

       上图是一个 CPU 与进程之间交互的模拟示例。
       假设这里是一个业务柜台,我们开设了三条通道以便表示 正在运行中的三个程序(进程)。每一个通道(进程)中,都可能会有数个任务等待被 CPU运算执行结果。我们假设 CPU是一个头脑非常好的业务人员,业务熟练度非常高,办事效率极快。但依然有一个问题就是: 同一时间,一个CPU只能处理一件事
       我们假设 CPU业务员所坐的柜台就是一个 操作系统,柜台外只要有人说话, 操作系统就会做出如下反应: 将CUP所坐的椅子移动到另一个更紧急的需要处理的进程位置,来处理相应的业务。而在移动之前, CPU还需要做一些事——保存当前进程的执行状态( 比如进程1的第一个用户,他的业务办理到了哪一步 ),以便于下一次该进程再次被 CPU调度时可以继续处理运算。而在移动到另一个位置之后, CPU业务员首先会打开一个记录簿,看看这个位置的业务处理到了哪一步,然后接着进行处理。
       描述的过程是缓慢的,幸好我们的 CPU执行的速度极快,因此我们看到的仿佛是所有的程序正在同时运行。例如我们可以一边听歌一边写文档,而两个进程不会互相干扰。

进程和线程

       进程(process) 表示在内存中正在运行的一个程序,而线程(thread)则表示当前进程中的最小处理(调度)单元

Egg 框架简述 (四):Worker 和 高效负载均衡_第2张图片
image

        CPU 真正需要运算的数据,是由 线程给与的,我们可以认为线程中包含了程序运行过程中的每一个行为、步骤,而线程本身,就相当于是上图所示的一个通道。在一个 进程中,至少会有一个 线程真正的在与 CPU打交道。当然, 进程中也允许拥有多个线程,也就是我们常说的多线程
        多线程模型解决的最大的问题,就是它允许每个线程共享进程中的资源。并且CPU在同一进程中的线程之间调度的过程中,减少了打开和保存进程上下文的开销。假设上述的业务模型中,每一个进程都表示一个需要办理业务的公司( 公司1 对应进程1 ,公司2对应进程2 )。此时在公司2的两个通道间切换 CPU业务员的调度时,需要保存的信息就少了很多——既然他们都来自于一个公司,那么在办理业务方面就有很多一致的地方。比如填写的表单、发票头等等。
        多线程模型在获取 CPU调度概率方面也有一定优势,更多人喊话当然得到回应的几率会增大(笑哭)。但如果当前运行环境只是基于单核CPU时,实际上在提升性能方面表现的优势非常之小。
       与此同时, 多线程模型还有着诸多应用隐患:假设图中进程2的两名男子都是接到了任务: 查询公司今天的入账,如果在100-150万之间,则取款50万。此时,男子A先查询并取钱,假设这一天公司入账是120万,符合条件。那么CPU应当告知男子A你可以取50万。但在此时男子B突然开口喊话,并得到了 操作系统的调度(此时男子A还没取款,账户依然是120万)。男子B需要完成同样的业务,CPU经过运算得知当前账户依然是120万,也允许男子B取款...男子B取款结束后,又被调度至男子A同样取款50万。像这样的 多个线程执行同一个任务的过程中,可能会引发的逻辑错误违背了可重入性(即函数可以由多于一个任务并发使用,而不必担心数据错误),当然可以以其他的机制尽可能规避 多线程下导致的线程安全问题,但这也着实提升了应用的复杂度。

单线程多进程模型

node.js 继承了 JavaScript 以单线程作为主线程的风格,与进程中开辟多线程而言具备以下优势:

  • 多线程占用内存高,单线程占用内存低;
  • 多线程间切换使得CPU开销大;
  • 多线程由内存同步开销;
  • 编写单线程程序更加简单;
  • 线程安全;

但是,相应的也拥有以下劣势:

  • CPU密集型任务占用CPU时间长;
  • 无法利用CPU的多核;
  • 单线程抛出异常使得程序停止;

Egg 框架简述 (四):Worker 和 高效负载均衡_第3张图片
image

       现代的计算机,基本都是基于多核的CPU。在计算机中运行 单进程单线程的应用程序时,最大的问题便是 无法利用多核导致CPU空闲,以及 当线程阻塞时,整个任务停滞无法继续
       因此,Egg 为了尽可能的压榨CPU资源,使用 单线程多进程模型,利用 nodejs 中的 cluster 模块,根据 CPU 核数创建相应的 子进程。这样的 子进程也被称之为是 工作进程——Worker。因此,处理方式就变成了:
Egg 框架简述 (四):Worker 和 高效负载均衡_第4张图片
image

以此确保 CPU的最大利用率。并且,程序运行的过程中,如果出现了某一个进程挂掉,也不会影响整体应用程序奔溃,因为其他进程也可以调度处理相应的问题。

Master 和 Worker

       假设我们的应用运行在一个提供了四核CPU的系统中,Egg 会帮我们建立如下结构:

Egg 框架简述 (四):Worker 和 高效负载均衡_第5张图片
image

Master 进程负责启动其他的 Worker ,并且在 Worker 出现问题时对其进行重启操作,我们可以认为他是一个 守护进程。而具体应用中的业务, Master 并不执行。
Worker 进程是具体的工作进程,我们之前的 APP 相对而言就是一个 WorkerMaster会根据具体的 CPU 核数进行判断,究竟 fork 几个 Worker 来榨干服务器CPU。

通过调用 $ npm start 便可以启动包含有数个子进程的你的Egg应用。

你可能感兴趣的:(Egg 框架简述 (四):Worker 和 高效负载均衡)