当前位置: 首页 > 后端技术 > Node.js

简单Node.js学习笔记(九)

时间:2023-04-03 19:20:40 Node.js

玩转进程JavaScript运行在单进程的单线程上。它带来的好处是:程序状态单一,不用多线程就不存在锁和线程同步问题,操作系统在调度时也会因为次要上下文而切换。可以很好的提高CPU使用率。服务器如何充分利用多核CPU?如何保证流程的稳健性和稳定性?一、服务模型的变化从“古”到现在,Web服务器的架构经历了数次变化。服务器处理的客户端请求的并发量是每个里程碑的见证。1.1石器时代:同步最早的服务器,它的执行模型是同步的,它的服务方式是一次只服务一个请求,所有的请求都要按顺序等待服务。1.2青铜时代:复制进程为了解决同步架构的并发问题,一个简单的改进就是通过进程复制同时服务更多的请求和用户。为了解决启动慢的问题,在服务模型中引入了prefork,即提前复制一定数量的进程。1.3白银时代:多线程为了解决进程复制的浪费问题,在服务模型中引入多线程,让一个线程服务一个请求。线程的开销比进程小很多,线程之间可以共享数据,内存浪费的问题可以解决,线程池的使用可以减少创建和销毁线程的开销。但是多线程面临的并发问题只能说比多进程稍微好一点,因为每个线程都有自己独立的栈,而这个栈需要占用一定的内存空间。此外。由于一个CPU内核一次只能做一件事,操作系统只能将CPU划分成时间片,让线程更均匀地使用CPU资源,但是操作系统内核在切换线程的同时也会切换线程。线程的上下文,当线程数过多时,时间会消耗在上下文切换上。1.4黄金时代:事件驱动为了解决高并发问题,出现了事件驱动的服务模型。Node和Nginx都是基于事件驱动方式实现的,单线程避免了不必要的内存开销和上下文切换开销。由于所有的处理都是在单线程上进行的,所以影响事件驱动服务模型性能的点就是CPU的计算能力。它的上限决定了该类服务模型的性能上限,但不受多进程或多进程模式下资源上限的影响。影响,可扩展性远高于前两者。2.多进程架构Master-Worker模式,也称为主从模式,其中进程分为两种类型:master进程和worker进程。这是一种典型的分布式架构下并行处理业务的模型,具有更好的扩展性和稳定性。主进程不负责具体的业务处理,而是负责调度或管理工作流程,趋于稳定。工作流程负责具体的业务处理,因为业务是多种多样的,甚至一个业务是由多人开发的,所以流程的稳定性值得开发人员关注。2.1创建子进程child_process模块??赋予了Node随意创建子进程(child_process)的能力。它提供了4种创建子进程的方法:spawn():启动子进程执行命令;exec():启动一个子进程执行命令,与spawn()的区别在于它的接口不同,它有一个回调函数知道子进程的状态;execFile():启动子进程执行可执行文件;fork():类似于spawn(),不同的是它创建的子进程只需要指定要执行的JavaScript文件模块即可;2.2进程间通信在Master-worker模式下,为了实现master进程管理和调度工作进程的功能,需要master进程与工作进程之间进行通信。对于child_process模块??来说,创建一个子进程然后与父子进程通信是非常容易的。在前端浏览器中,JavaScript与主进程和UI渲染共享一个线程。执行JavaScript时,UI渲染停滞。在渲染UI时,JavaScript停滞不前,两者相互阻塞。长时间执行JavaScript会导致UI冻结并变得无响应。为了解决这个问题,HTML5提出了WebWorkerAPI。WebWorker允许创建一个工作线程并在后台运行,这样一些严肃的计算不会影响主线程上的UI渲染。主线程之前通过onmessage()和postMessage()与工作线程通信,子进程对象使用send()方法实现主进程向子进程发送数据,message事件实现监听子进程发送的数据subprocess,这在一定程度上与上面的API类似。通过fork()或者其他API,在创建子进程后,为了实现父子进程之间的通信,会在父进程和子进程之间创建一个IPC通道。通过IPC通道,可以通过message()和send()在父子进程之间传递消息。IPC的全称是Inter-ProcessCommunication,即进程间通信。进程间通信的目的是让不同的进程能够访问资源并相互协调工作。实现进程间的技术有很多,如命名管道、匿名管道、socket、信号量、共享内存、消息队列、DomainSocket等。Node中IPC通道的实现就是管道技术。IPC通道是使用命名管道或域套接字创建的,其行为类似于网络套接字,属于双向通信。2.3handle通过proxy传递,可以避免端口不能重复监听的问题,甚至可以对proxy进程做适当的负载均衡,让各个子进程更均衡的执行任务。因为进程每次接收连接都会用完一个文件描述符,所以在代理方案中,客户端连接代理进程,代理进程连接到工作进程的进程需要使用两个文件描述符。操作系统的文件描述符是有限的,代理方案中浪费双倍文件描述符的做法影响了系统的可扩展性。为了解决此类问题,Node在0.5.9版本引入了进程间发送句柄的功能。除了通过IPC发送数据,send()方法还可以发送句柄,有两个可选参数是句柄。child.send(message,[sendHandle])handle:是一个可以用来标识资源的引用,它包含一个指向对象的文件描述符。该句柄可用于标识服务器套接字对象和客户端套接字对象。一个UDPsocket,一个pipe等。3.集群稳定之路集群搭建好,多核CPU资源得到充分利用后,似乎可以满足客户端的大量请求。但需要考虑的细节:性能问题多个工作进程的生存状态管理工作进程的平滑重启配置或静态数据的动态重载其他细节3.1进程事件节点进程事件出现,工作进程会立即停止接受新连接。当所有连接都断开后,退出进程。master进程监听到worker进程退出后,会立即启动新的进程服务,保证整个集群始终有进程为用户服务。SuicidesignalSuicide(自杀)信号:当工作进程知道自己要退出时,向主进程发送自杀信号,然后停止接收新的连接,当所有连接都断开后退出。主进程收到自杀信号后,立即创建一个新的工作进程服务。有限重启为了杜绝这种无意义的重启,不应该在一定的规划限制下反复重启。3.3负载均衡在多个进程间监听同一个端口,这样可以将用户请求分发到多个进程处理,调用CPU资源。确保跨多个工作单元进行大量处理的策略称为负载平衡。Node默认提供的机制是采用操作系统的抢占策略。所谓抢占策略,就是在一堆进程中,空闲的进程竞争进来的请求,谁抢到服务。一般来说,这种抢占策略是公平的,每个进程都可以根据自己的繁忙程度进行抢占。但是对于Node来说,需要区分的是它的busyness由CPU和I/O两部分组成,影响抢占的是CPU的busyness。对于不同的业务,可能会出现I/O繁忙而CPU相对空闲的情况,这可能会导致某个进程抢到更多的请求,造成负载不均衡。Node提供的循环调度(Round-Robin):master进程接收连接,依次分发给worker进程。Round-Robin可以避免因CPU和I/O繁忙程度不同而导致的负载不平衡。Round-Robin策略也可以通过代理服务器来实现,但它会导致服务器上消耗的文件描述符数量是通常方式的两倍。3.4状态共享在不允许共享数据之前实现多个进程的共享。第三方数据存储将数据存储在数据库、磁盘文件和缓存服务(如Redis)中,并在所有工作进程启动时读入内存。缺点:如果数据发生变化,需要一种机制来通知每个子进程,使其内部状态也随之更新。状态同步机制;每个子流程都交给第三方进行定期轮询。当数据更新时,主动通知子进程。当数据更新时,主动通知子进程。通知进程:用于发送通知和查询状态变化的进程。4.Cluster模块Cluster模块:用于解决多核CPU利用率问题,同时也提供了比较完善的API来处理进程的健壮性。4.1Clusterer工作原理Cluster模块是child_process和net模块的组合应用。4.2集群事件集群事件:forkonlinelisteningdisconnectexitsetup