目录
Node特性:高并发" >Node特性:高并发
单线程
异步I/O
事务驱动
分布式Node架构" >分布式Node架构
流量接入层
Node集群层
数据库层
首页 web前端 js教程 浅谈node.js中高并发与分布式集群的内容

浅谈node.js中高并发与分布式集群的内容

Aug 01, 2018 pm 03:54 PM
javascript node.js 分布式部署

这篇文章给大家介绍的内容是关于浅谈node.js中高并发与分布式集群的内容,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

Node特性:高并发

在解释node为什么能够做到高并发之前,不妨先了解一下node的其他几个特性:

单线程

我们先来明确一个概念,即:node是单线程的,这一点与JavaScript在浏览器中的特性相同,并且在node中JavaScript主线程与其他线程(例如I/O线程)是无法共享状态的。

单线程的好处就是:

  • 无需像多线程那样去关注线程之间的状态同步问题

  • 没有线程切换所带来的开销

  • 没有死锁存在

当然单线程也有许多坏处:

  • 无法充分利用多核CPU

  • 大量计算占用CPU会导致应用阻塞(即不适用CPU密集型)

  • 错误会引起整个应用的退出

不过在今天看来,这些坏处都已经不再是问题或者得到了适当的解决:

(1) 创建进程 or 细分实例

关于第一个问题,最直白解决方案就是使用child_process核心模块或者cluster:child_process 和 net 组合应用。我们可以通过在一台多核服务器上创建多个进程(通常使用fork操作)来充分利用每个核心,不过要处理好进程间通信问题。

另一个方案是,我们可以将物理机器划分为多台单核的虚拟机,并通过pm2等工具,管理多台虚拟机形成一个集群架构,高效运行所需服务,至于每台机器间的通信(状态同步)我这里先按下不表,在下文的Node分布式架构中再做详细说明。

(2) 时间片轮转

关于第二点,我跟小伙伴讨论过后认为可以通过时间片轮转方式,在单线程上模拟多线程,适当减少应用阻塞的感觉(虽然这种方法不会真的像多线程那样节约时间)

(3) 负载均衡、坏点监控/隔离

至于第三点,我跟小伙伴们也讨论过,认为主要的痛点就在于node不同于JAVA,它所实现的逻辑是以异步为主的。

这就导致了node无法像JAVA一样方便地使用 try/catch 来来捕获并绕过错误,因为无法确定异步任务会何时传回异常。而在单线程环境下,绕不过错误就意味着导致应用退出,重启恢复的间隙会导致服务中断,这是我们不愿意看到的。

当然,在服务器资源丰富的当下,我们可以通过 pm2 或 nginx 这些工具,动态的判断服务状态。在服务出错时隔离坏点服务器,将请求转发到正常服务器上,并重启坏点服务器以继续提供服务。这也是Node分布式架构的一部分。

异步I/O

你可能会问,既然node是单线程的,事件全部在一个线程上处理,那不是应该效率很低、与高并发相悖吗?

恰恰相反,node的性能很高。原因之一就是node具有异步I/O特性,每当有I/O请求发生时,node会提供给该请求一个I/O线程。然后node就不管这个I/O的操作过程了,而是继续执行主线程上的事件,只需要在该请求返回回调时在处理即可。也就是node省去了许多等待请求的时间。

这也是node支持高并发的重要原因之一

实际上不光是I/O操作,node的绝大多数操作都是以这种异步的方式进行的。它就像是一个组织者,无需事必躬亲,只需要告诉成员们如何正确的进行操作并接受反馈、处理关键步骤,就能使得整个团队高效运行。

事务驱动

你可能又要问了,node怎么知道请求返回了回调,又应该何时去处理这些回调呢?

答案就是node的另一特性:事务驱动,即主线程通过event loop事件循环触发的方式来运行程序

这是node支持高并发的另一重要原因

图解node环境下的Event loop:

   ┌───────────────────────┐
┌─>│        timers         │<————— 执行 setTimeout()、setInterval() 的回调
│  └──────────┬────────────┘
|             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │<————— 执行几乎所有的回调,除了 close callbacks 以及 timers 调度的回调和 setImmediate() 调度的回调
│  └──────────┬────────────┘
|             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
│  ┌──────────┴────────────┐
│  │     idle, prepare     │<————— 内部调用,可忽略
│  └──────────┬────────────┘     
|             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
|             |                   ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │ - (retrieve new I/O events; node will block here when appropriate)
│  │         poll          │<─────┤  connections, │ 
│  └──────────┬────────────┘      │   data, etc.  │ 
│             |                   |               | 
|             |                   └───────────────┘
|             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
|  ┌──────────┴────────────┐      
│  │        check          │<————— setImmediate() 的回调将会在这个阶段执行
│  └──────────┬────────────┘
|             |<-- 执行所有 Next Tick Queue 以及 MicroTask Queue 的回调
│  ┌──────────┴────────────┐
└──┤    close callbacks    │<————— socket.on(&#39;close&#39;, ...)
   └───────────────────────┘
登录后复制

poll阶段:

当进入到poll阶段,并且没有timers被调用的时候,会发生下面的情况:

(1)如果poll队列不为空:

  • Event Loop 将同步的执行poll queue里的callback(新的I/O事件),直到queue为空或者执行的callback到达上线。

(2)如果poll队列为空:

  • 如果脚本调用了setImmediate(), Event Loop将会结束poll阶段并且进入到check阶段执行setImmediate()的回调。

  • 如果脚本没有被setImmediate()调用,Event Loop将会等待回调(新的I/O事件)被添加到队列中,然后立即执行它们。

当进入到poll阶段,并且调用了timers的话,会发生下面的情况:

  • 一旦poll queue是空的话,Event Loop会检查是否timers, 如果有1个或多个timers时间已经到达,Event Loop将会回到timer阶段并执行那些timer的callback(即进入到下一次tick)。

优先级:

Next Tick Queue > MicroTask Queue

setTimeout、setInterval > setImmediate

由于timer需要从红黑树中取出定时器来判断时间是否到了,时间复杂度为O(lg(n)),故如果想立即异步执行一个事件,最好不要用 setTimeout(func, 0)。而是使用 process.nextTick() 来完成。

分布式Node架构

我了解到的Node集群架构主要分为以下几个模块:

Nginx(负载均衡、调度) -> Node集群 -> Redis(同步状态)

按我的理解整理了一副图:

1411000415-5b611c4439001_articlex.png

当然,这应该是比较理想状态下的架构方式。因为虽然 Redis 的读/写相当快,但这是因为其将数据存储在内存池里,在内存上进行相关操作。

这对于服务器的内存负荷是相当高的,所以通常我们还是会在架构中加入 Mysql,如下图:

1288196825-5b611c4441d66_articlex.png

先解释一下这幅图:
当用户数据到来时,将数据先写入 Mysql,Node 需要数据时再去 Redis 读取,若没有找到再去 Mysql 里面查询想要的数据,并写入 Redis,下次使用时就可以直接去 Redis 里面查询了。

加入 Mysql 相较于只在 Redis 里读/写的好处有:

(1)避免了短期内无用的数据写入 Redis,占用内存,减轻 Redis 负担

(2)在后期需要对数据进行特定查询、分析的时候(比如分析运营活动用户涨幅),SQL关系查询能提供很大的帮助

当然在应对短时间大流量写入的时候,我们也可以直接将数据写入 Redis,以达到快速存储数据、增加服务器应对流量能力的目的,等流量下去了再单独将数据写入 Mysql。

简单介绍完了大体的架构组成,接下来我们来细看每个部分的细节:

流量接入层

流量接入层所做的就是对所有接受的流量进行处理,提供了以下服务:

  • 流量缓冲

  • 分流和转发

762690855-5b612b43a7bb7_articlex.png

  • 超时检测

    • 与用户建立连接超时

    • 读取用户body超时

    • 连接后端超时

    • 读后端响应头超时

    • 写响应超时

    • 与用户长连接超时

  • 集群健康检查/隔离坏点服务器

    • 隔离坏点服务器并尝试修复/重启,直到该服务器恢复正常

  • 失败重试机制

    • 在请求转发到某集群某机器上,返回失败后,将该请求转发到该集群的别的机器,或者跨集群的机器上进行重试

  • 连接池/会话保持机制

    • 对于延迟敏感用户使用连接池机制,减少建立连接的时间

  • 安全防护

  • 数据分析

当转发到各个产品线后就到了负载层工作的时候了:将请求根据情况转发到各地机房

3038924812-5b612b439f6a0_articlex.png

当然,这个平台并不止转发这一个功能,你可以把它理解为一个大型的私有云系统,提供以下服务:

  • 文件上传/服务线上部署

  • 线上配置修改

  • 设置定时任务

  • 线上系统监控/日志打印服务

  • 线上实例管理

  • 镜像中心

  • 等等...

Node集群层

这一层主要的工作是:

(1)编写可靠的 Node 代码,为需求提供后端服务

(2)编写高性能查询语句,与 Redis、Mysql 交互,提高查询效率

(3)通过 Redis 同步集群里各个 Node 服务的状态

(4)通过硬件管理平台,管理/监控物理机器的状态、管理IP地址等(其实这部分工作放在这一层感觉不妥,但是我也不知道应该放在哪一层。。。)

(当然这部分我只是粗浅地列列条目,还是需要时间来积累、深入理解)

数据库层

这一层主要的工作是:

(1)创建 Mysql 并设计相关页、表;建立必要的索引、外键,提升查询便利性

(2)部署 redis 并向 Node 层提供相应接口

相关推荐:

vue如何使用axios请求后端数据

对Vue中表单输入绑定和组件基础的分析

以上是浅谈node.js中高并发与分布式集群的内容的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
2 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
2 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Linux中如何构建4块虚拟盘来搭建分布式MinIO集群? Linux中如何构建4块虚拟盘来搭建分布式MinIO集群? Feb 10, 2024 pm 04:48 PM

由于最近刚开始负责对象存储相关系统的建设与稳定性运维,作为一个“对象存储”的一个新手,需要加强这块的学习。由于公司目前采用MinIO来搭建公司的对象存储体系,后续我会逐步将自己关于MinIO的学习经验分享出来,欢迎大家持续关注。本文主要是介绍如何在测试环境中搭建MinIO,这也是构建MinIO学习环境最基本的步骤。1、准备实验环境使用OracleVMVirtualBox虚拟机,安装一个最小版本的Linux,然后添加4块虚拟盘,用于充当MinIO的虚拟盘。实验环境如下所示:接下来和大家简单介绍一下

如何使用WebSocket和JavaScript实现在线语音识别系统 如何使用WebSocket和JavaScript实现在线语音识别系统 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript实现在线语音识别系统引言:随着科技的不断发展,语音识别技术已经成为了人工智能领域的重要组成部分。而基于WebSocket和JavaScript实现的在线语音识别系统,具备了低延迟、实时性和跨平台的特点,成为了一种被广泛应用的解决方案。本文将介绍如何使用WebSocket和JavaScript来实现在线语音识别系

WebSocket与JavaScript:实现实时监控系统的关键技术 WebSocket与JavaScript:实现实时监控系统的关键技术 Dec 17, 2023 pm 05:30 PM

WebSocket与JavaScript:实现实时监控系统的关键技术引言:随着互联网技术的快速发展,实时监控系统在各个领域中得到了广泛的应用。而实现实时监控的关键技术之一就是WebSocket与JavaScript的结合使用。本文将介绍WebSocket与JavaScript在实时监控系统中的应用,并给出代码示例,详细解释其实现原理。一、WebSocket技

如何使用WebSocket和JavaScript实现在线预约系统 如何使用WebSocket和JavaScript实现在线预约系统 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript实现在线预约系统在当今数字化的时代,越来越多的业务和服务都需要提供在线预约功能。而实现一个高效、实时的在线预约系统是至关重要的。本文将介绍如何使用WebSocket和JavaScript来实现一个在线预约系统,并提供具体的代码示例。一、什么是WebSocketWebSocket是一种在单个TCP连接上进行全双工

如何利用JavaScript和WebSocket实现实时在线点餐系统 如何利用JavaScript和WebSocket实现实时在线点餐系统 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket实现实时在线点餐系统介绍:随着互联网的普及和技术的进步,越来越多的餐厅开始提供在线点餐服务。为了实现实时在线点餐系统,我们可以利用JavaScript和WebSocket技术。WebSocket是一种基于TCP协议的全双工通信协议,可以实现客户端与服务器的实时双向通信。在实时在线点餐系统中,当用户选择菜品并下单

JavaScript和WebSocket:打造高效的实时天气预报系统 JavaScript和WebSocket:打造高效的实时天气预报系统 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的实时天气预报系统引言:如今,天气预报的准确性对于日常生活以及决策制定具有重要意义。随着技术的发展,我们可以通过实时获取天气数据来提供更准确可靠的天气预报。在本文中,我们将学习如何使用JavaScript和WebSocket技术,来构建一个高效的实时天气预报系统。本文将通过具体的代码示例来展示实现的过程。We

简易JavaScript教程:获取HTTP状态码的方法 简易JavaScript教程:获取HTTP状态码的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教程:如何获取HTTP状态码,需要具体代码示例前言:在Web开发中,经常会涉及到与服务器进行数据交互的场景。在与服务器进行通信时,我们经常需要获取返回的HTTP状态码来判断操作是否成功,根据不同的状态码来进行相应的处理。本篇文章将教你如何使用JavaScript获取HTTP状态码,并提供一些实用的代码示例。使用XMLHttpRequest

javascript中如何使用insertBefore javascript中如何使用insertBefore Nov 24, 2023 am 11:56 AM

用法:在JavaScript中,insertBefore()方法用于在DOM树中插入一个新的节点。这个方法需要两个参数:要插入的新节点和参考节点(即新节点将要被插入的位置的节点)。

See all articles