• 技术文章 >web前端 >js教程

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

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

    Node特性:高并发

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

    单线程

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

    单线程的好处就是:

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

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

    (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('close', ...)
       └───────────────────────┘

    poll阶段:

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

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

    (2)如果poll队列为空:

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

    优先级:

    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

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

    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核实处理。
    上一篇:在jsp页面中使用jstl标签将long型的时间戳转换 下一篇:jQuery自调用匿名函数是如何调用的?
    VIP课程(WEB全栈开发)

    相关文章推荐

    • 【活动】充值PHP中文网VIP即送云服务器• 分享一个Nodejs web框架:Fastify• 【归纳总结】JS数组的常见操作方法,助你提高开发效率!• 详解Javascript对象的5种循环遍历方法• 实例介绍javaScript操作字符串的一些常用方法• BOM核心之window对象(总结分享)
    1/1

    PHP中文网