我所理解的 Node.js
发布于 13 天前 作者 haijianyang 905 次浏览 来自 分享

我所理解的生活

从《三重门》的“叛逆”,再到《我所理解的生活》的成熟,韩寒对人生和生活的理解随着阅历的增加而更深。我想技术大概也是如此,随着不断使用、探索、思考而获得越来越多的认识。每一个阶段你都可以有不同的角度,不同的思考,不同的理解。

我对 Node 的理解过程与之类似,所以当我决定写这篇文章的时候,它的名字自然而然就是《我所理解的 Node.js》。

From Life For Life

这期间接触到了周以真教授的论文 《Computational Thinking》,大有醍醐灌顶之感,获得了一些新的技术理解和思考。

比如技术和生活其实也是有关联的:

  • 你把经常需要的东西放进背包,这就是预置和缓存。
  • 当你丢了东西的时候,你沿走过的路回寻,这就是回推。
  • 在超市付款时你应该去排哪个队,这就是多服务器系统的性能模型。

学校做的物理实验往往需要在精心准备的实验室环境下进行,我朋友研究的是“非常规物理实验”,鼓励在平常的生活场景中完成物理实验和教学(用最简单看得见的方式呈现物理知识)。受此启发,计算机能不能也可以在生活化场景中学习,更加简单化?

很多同学在上了大学后才接触计算机,计算机往往要求具备一定的数学思维、抽象思维等,对于一些计算机概念我们往往不太容易理解。对于生活的一些场景比较熟悉,我们相对容易理解。技术有时候也来源于生活而服务于生活,适当的融入一些生活的场景去描述一些晦涩难懂的技术概念,可以帮助更好的理解技术。

What Is Node.js

经常会遇到很多 Node 相关的概念,比如单线程、异步 I/O、事件循环等。 关于 Node 的讨论可谓百家争鸣:

  • 有人认为 Node 是一种语言,而有的人认为是 JS 运行环境
  • 有人认为 Node 是单线程的,而有的人认为 Node 是多线程的
  • 阮一峰和朴灵对事件循环的讨论

Node 10 周年了,而我用 Node 也有 5 年了。每当遇到事件循环、异步 I/O 等这些问题的时候,我发现其实我也讲不清楚这些概念,似懂非懂的感觉。所以我开始尝试弄清楚这些概念,找了很多相关的书和资料,还看了 Node 的源码。期间朋友写了《Node.js 调试指南》找我看初稿,我跟他说了我的想法:有没有更好的方式把事件循环这些难以理解的概念更通俗易懂。

尝试了多种方式后,遗憾的是一直都没有找到我想要的答案。

Why Node.js

直到偶然再次看到了 RY 早期关于 Node 的演讲,于是突发奇想,把 RY 的演讲、采访、文章等资料全部看了个遍,顺藤摸瓜发现了新大陆。

既然不知道什么是 Node,不妨换个角度:为什么要有 Node。

Node 之父 RY 在德国接触到了Rack,一个简化抽象 Web 接口的框架,很多 Ruby 的服务器都基于它实现。他联想到了 Nginx 异步 I/O 优异的性能,想着是否可以把两者结合。

V8 的出现使得他意识到 JS 在浏览器不就是单线程和异步 I/O 运行的吗。他觉得在服务器中使用 JS 和异步 I/O 是一件很酷的事情,于是开始尝试把这个想法实现。

当时 Ruby、Java 等语言已经有了成熟的服务器生态,为什么 RY 还要去设计更简单实用的高性能服务器。或者说:Node 要去解决什么问题。

C10X

C10X 包括 C10K、C10M 等。C10K 问题最早由 Dan Kegel 在《The C10K problem》提出,指的是单机处理 1 万个并发连接。我们已经解决了 C10K 问题,当前面临的是C10M ,指单机同时处理一千万的请求。

早期互联网并不普及,用户不多,服务器并不存在高并发问题。到了 Web1.0,用户主要浏览静态网页,以门户类网站为主(比如 Netscape、Yahoo),互联网逐渐普及,虽然用户增多但也没有达到 C10K。到了 Web2.0,浏览网页发展成了实时通信和在线实时互动,以社交网站为代表(比如 Twitter、Facebook),用户爆发式增长,这个时候出现了 C10K 问题,再往后便是 C10M 问题。

图片

C10X 的核心思想是让服务器处理更多的请求,追求单机极致性能。广义的角度来说,我们希望用更低的成本,更高的效率生产的产品为更多的用户提供有价值的服务。这样 C10X 也可以理解为用低成本、简单高效的方式提供高性能应用服务。

Solutions

解决这个问题的主要有三个方向:

  • 硬件系统
  • 操作系统
  • 应用软件

图片

Software Layers & Performance Productivity & Generality

从下往上,依次是硬件层、操作系统层、应用软件层。从下往上性能和通用性逐渐降低,生产效率逐渐升高。

Hardware

硬件是运行应用的物理基础,包括 CPU、内存、硬盘等,提供通用的计算、存储等能力。

硬件研发成本和风险高,但可以工厂化大规模批量生产。

CPU

单核 CPU —> 多核 CPU

根据摩尔定律,CPU 的性能在不断提高。当前硅基半导体工艺的已接近物理极限,单核的性能提升越来越慢,通过多核的方式可以以更低成本提升 CPU 整体性能。除此之外,芯片领域在探索新的材料和技术(比如碳基芯片)。

存储

软盘 —> 机械硬盘 —> SSD

随着存储器技术的更新换代,成本越来越低,存储容量越来越大,速度越来越快。

网卡

十兆网卡 —> 百兆 —> 千兆 —> 万兆

单网卡性能提升和多网卡绑定技术可以承载越来越大的吞吐量。

System

操作系统介于硬件和软件层之间,是管理硬件与软件资源的系统软件。操作系统的核心价值在于:实现软件治理、提供基础的编程接口、降低软件开发难度。

硬件在不断演进,操作系统也在演进。比如 CPU 演变成多核后,操作系统也具备更好利用多核的能力。

History of Evolution

  1. 手工操作阶段:独占计算机资源
  2. 单道批处理系统:高效利用 CPU 资源
  3. 多道批处理系统:宏观上并行,微观上串行
  4. 分时操作系统:交互性强
  5. 实时操作系统:及时性和可靠性不如分时
  6. 网络操作系统:集中式控制
  7. 分布式操作系统:分布式控制
  8. 个人操作系统:目前最广泛

Process & Thread

操作系统通过进程和线程管理资源的分配和调度,提高硬件资源的利用率。进程是资源分配的基本单位,通过进程可以实现多软件同时运行。线程是调度的基本单位,通过线程可以实现同一进程内多任务同时运行。

CPU 性能提升遇到了瓶颈,通过多核的方式提高 CPU 的整体性能。操作系统则通过多线程模型利用了 CPU 多核的优势。

I/O Model

网络处理能力是服务端性能的一个重要点。操作系统早期提供的阻塞 I/O 模型导致单线程只能同时处理一个网络请求,遇到阻塞需要进行线程上下文切换,带来了线程上下文切换时间消耗和线程资源消耗。非阻塞 I/O 模型和多路复用出现后,单线程可以同时处理多个网络请求,大大提高了网络请求的并发处理能力,又避免和线程上下文切换带来的时间消耗和线程资源消耗。I/O 多路复用提高了单线程并发处理网络请求的能力。

  • Blocking I/O - 阻塞 I/O
  • Nonblocking I/O - 非阻塞 I/O
  • I/O multiplexing - I/O 多路复用(Select、Poll、Epoll)
  • Signal Driven I/O - 信号驱动 I/O
  • Asynchronous I/O - 异步 I/O(未成熟技术)

Software

硬件和操作系统都在不断演进,软件也在演进,充分利用硬件和操作系统演进带来的进步。

软件层是生产效率最高的一层,大量的开发工作消耗在这层。作为软件开发工程师,我们专注于软件层。硬件资源是昂贵且有限的,在硬件层和操作系统层一定的情况下,我们只能尽可能提高软件层的生产效率、性能和通用性。

软件开发目前没有实现工厂化一样自动化批量生产,还属于“劳动力密集型”。多人协作需要软件工程和项目管理控制效率和质量。

语言

不同语言写的代码在同一台计算机(硬件和操作系统都一样)跑为什么会有不同的性能。

CPU 只能执行机器码,高级语言的代码需要编译器编译成机器码。不同语言的编程模型不一样,编译成的机器码不一样导致了执行效率不一样。编程模型和编译这两部是语言性能和效率的关键,比如 Go 引入了Goroutine。

对于底层和极致性能追求的场景我们往往选择C、C++ 等语言,但是在应用层似乎用的比较多的是 Java、PHP 等语言。Java 等类高级语言的性能可能比 C 差,但是开发效率高,用部分性能换取更高的效率。

分布式

单机性能再厉害也有极限,单机性能提升到一定程度再往后提升会越来越困难。单核 CPU 性能提升遇到瓶颈,我们使用多核 CPU 提升整体性能。同样,单机性能遇到瓶颈,我们使用集群和分布式系统提升整体性能。

传统的 SQL 数据库单机性能遇到瓶颈后,我们通过分库分表、NewSQL 提高数据库的整体能力。

Node

在 Node 之前,硬件、网络、服务端等面临的问题等都发生了很大的变化,但是没有出现比较好的解决方案(实际上 Go 已经出现了,可能 RY 没有注意到)。

其他解决方案存在的问题:

  • C、C++ 编程复杂,开发效率低
  • Java 多线程阻塞模型,过于笨重(线程资源消耗、上下文切换损耗等)

RY 想要实现的 Node 对应了软件层:

  • 生产效率:把服务端编程变得简单高效。Node 提供了简洁的开发接口,屏蔽了底层复杂的实现逻辑。
  • 性能:异步网络 I/O 更好的性能

How To Use

了解什么是 Node 以及为什么需要 Node 之后,进一步了解如何使用 Node。了解 Node 的优势和不足,以便在合适的场景下使用 Node。

优势

高性能网络 I/O

我们常说 Node 适合 I/O 密集型,这个 I/O 指的是网络 I/O。互联网大多数都是 I/O 密集型场景,比如 Web、IOT 等。

统一语言

  • 前后端统一语言:无语言切换成本,轻松实现全栈开发。
  • 前后端代码复用:比如表单的参数校验可以提取出来作为一个公共代码库,一套代码前后端复用,可以确保前后端的数据校验逻辑一致而且只需要维护一份代码。
  • 多端代码复用:前端(React)、移动端(RN)、PC 端(Electron)。

不足

缺少工业化的标准

Node 社区缺少工业级的标准,比如 J2EE 这样的企业级标准。

缺少杀手锏级应用

Node 社区活跃,Github 上有很多开源项目、NPM 有很多包,但是 Node 重量级的框架和杀手锏级别的应用几乎没有,更没有 Java Spring 这样强大的生态体系。

解释型语言的性能

JS 解释型语言性能先天不足,虽然 V8 做了大量的优化,但是性能跟编译型语言比还是有一定的差距。

不适合 CPU 密集型

Node 主线程使用了 Event Loop,直接运行 CPU 密集型任务会导致其他任务受到影响。

可以使用 Libuv 线程池等方式,但是开发维护成本比较高,不推荐。在微服务思想下,Node 做不了的可以用 Java、Go 等其他语言补,但这也带来了多语言成本。

应用

Microservice

微服务是一种架构风格,一个大型复杂软件应用由多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的,使用轻量级的通讯协议进行通讯。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。

每种语言都有自己的优势和应用场景。 现代系统往往遇到复杂多样的场景, 在微服务架构下不同服务可以根据对应的情况选择语言,使用多语言组合优势。实际上我们一直在使用多语言,比如 MySQL 是 C、Hadoop 是 Java。网络 I/O 密集型场景,Node 的优势,CPU 密集型可以用 Java、GO 等。

Monolith to Microservice

架构是演进而来的,而不是设计而来的。架构往往随着业务的变化迭代而演进,以满足业务的需求。我们的系统在初期往往被设计成单体应用,而后逐渐演进成微服务。

一个产品的生命周期大概分成三个阶段:1.产品初期;2.产品增长期;3.产品稳定期。

  1. 产品初期

产品初期的需求可能不太明确、需要快速应对业务变化又要平衡软件效率和质量,这个时候需要技术可以灵活且快速的响应需求的变化。前端(React)、后端(Node)统一 JS,可以实现代码复用,Node 高效的开发效率,实现快速开发上线并持续迭代。

图片

  1. 产品增长期

随着用户的快速甚至是爆炸式增长,系统可能出现一些性能问题或是需要解决之前因为快速上线而设计不合理的问题,这个时候需要对系统进行优化或重构以保持服务的稳定和业务的持续迭代。把性能重要的业务拆分微服务,使用 Java、Go 等去处理 Node 不擅长的场景(比如 CPU 密集型业务)。

为了满足用户多设备场景的需求,还需要支持移动版和 PC 版。 浏览器(React)、移动端(RN)、PC端(Electron)多端统一 JS,代码进一步复用,降低了开发成本。

图片

  1. 产品稳定期

产品的主要业务趋于稳定后,用户量和数据量一般都达到了一个比较大的量级,稳定持续长。开发可以有时间做优化和重构的事情。把稳定的业务继续拆分微服务,Node 变成 API Gateway。

图片

Serverless

Serverless(SLS) 无服务器架构是基于互联网的系统,其中应用开发不使用常规的服务进程。它们仅依赖于第三方服务(例如 Lambda)、客户端逻辑和服务托管远程过程调用的组合。SLS 适合事件请求(比如 IOT)、流量突发等场景。

Node 具有轻量级低资源成本、易于部署等特点。 主流的云服务平台的 SLS 服务对 Node 的支持比较友好,工具相对完善。

我们在通知服务(事件请求)和市场活动(流量激增)使用 SLS 收到了比较好的效果,提高了开发效率并降低了成本(和使用 Java 对比)。

我们每个 Q 会进行几场不同规模的市场活动进行拉新或增强社区氛围,比如小游戏、投票、答题等形势。活动少则几万人,多则数百万人参与。早期的活动由前端和 Java 后端协作开发,之后 Java 后端人力短缺,便由前端同学使用 Node 独立完成前后端开发。由于前端同学的后端知识和经验不足,后端部分的效率和质量稍欠缺。除此之外,传统服务器扩容无法很好应对活动的流量突发激增。以上因素导致用户体验不佳,活动效果受到一定影响。考虑到活动灵活多变、业务逻辑不复杂、流量激增等特点以及当前开发人员配置,我们决定引入 SLS。我们封装了一个 SLS 架子,前端同学只需要关注业务部分,实现一些基本的后端系统功能,不需考虑部署扩容等繁琐的非业务问题。

图片

Node 并不是理想或完美的 SLS 选择:我们似乎用不到 I/O 高并发,那 Node 的主要优势之一就用不上。AWS 的 Lambda 性能大概是这样:Go > Python3 > Node.js。更多的技术细节,大家可以参考网上的评测。

Event Loop

Event Loop(EL)是一个比较重要的概念,关系到是否理解 Node,是否可以写好 Node 代码。同时 EL 难以理解并产生了很多困惑或误解,网上流传着很多对 EL 理解的版本,这些文章大多数从技术的角度去分析。直接从技术实现去探讨 EL,可能容易陷入具体的技术细节,而忽略 EL 本身是什么。先从概念上解释 EL,再到具体如何实现 EL,这样可能会更通俗易懂。

从名字来看,Event Loop 由两个单词组成(复合名词),Event 和 Loop:

  • Event:事件,比如定时器到时、收到新的请求、文件可读
  • Event Source:事件源,产生事件的对象,比如定时器
  • Event Queue:事件队列,记录产生的事件
  • Loop:循环,反复地连续做某事(处理事件)

EL 字如其意,经过咬文嚼字,可以猜出 EL 的大概意思。EL 不断等待事件源产生事件,然后处理产生的事件。比如服务端收到新的请求(事件)事件就可以等待接收客户端的数据(处理)。

同一时间可能产生很多同类型事件,需要保存在 EQ 中按顺序处理。如果事件类型复杂多样,还需要把不同类型的事件分别保存在不同的 EQ 中,并根据事件类型的优先级采取不同的处理。比如在 Node EL 中 Next tick 队列的事件比其他 micro tasks 队列的事件优先级高。

Abstract

Event Loop 是一个抽象的概念,不是具体的。 对于计算机来说,EL 是一个程序结构/模型,Nginx EL、Redis EL 和 Node.js EL 是 EL 的实现,是具体的。 每种语言可以有自己的 EL 实现,甚至同一种语言中 EL 会有不同的实现,但是大多数的 EL 都是大同小异的。在计算机中 EL 往往使用一个线程实现。

JS EL Visualization

Philip Roberts 曾在 JSConf 2014 分享了《What the heck is the event loop anyway》,介绍了他对 EL 的理解,以及 EL 的可视化。虽然这个可视化比较简单,但是可以帮助更好的理解 EL。

Dinner

做饭是一件很平常的事,同时也需要花费时间和心思。一般做饭有这几件事:煮米饭、煲汤、炒菜。

假如分别用这三种方式去做饭:一个人一件事情一件事情按顺序做;多个人每个人做一件事件;一个人同时做多件事情。

One by One

一个人按部就班,做好一件事再做下一件事。煮米饭 -> 煲汤 -> 炒菜。

虽然可以专注做好一件事,但是耗费大量时间。

Collaboration

多个人同时一起做,每个人做一件事。A 煮米饭,B 煲汤,C 炒菜。

相比之前一个人做饭效率提高了,但是浪费了人力资源。(但是一起做饭有乐趣)

Busy

一个人同时一起做多件事,一边煮米饭一边煲汤一边炒菜。

比一个人做饭效率提高了,比多个人减少了人力,但是一个人的能力是有限的。

“Busy”模式的 Timeline 可能是这样:

time1:淘米
time2:入锅煮米饭
time3:汤锅水加热
time4:洗汤料
time5:入锅煲汤
time6:切菜
time7:汤锅开了,转文火
time8:继续切菜
time9:炒菜
time10:汤时间够了,关火
time11:继续炒菜
...

做饭的每件事都可以细分为多个更小的步骤(还可以进一步细分)

  • 煮饭:淘米 -> 入锅煮 -> 盛饭
  • 炒菜:洗菜 -> 切菜 -> 入锅炒 -> 加调料 -> 装盘
  • 煲汤:洗汤料 -> 入锅煲 -> 加调料 -> 文火 -> 盛汤

把大事情分解成多个小步骤,把每个步骤做好,最后组合起来煮饭就完成了。这个也是我们平时使用的方式,或者接近的方式。

假如一个人的速度快十倍、一百倍、一万倍 …… 这个效果就会更加的明显。

Dinner & Event Loop

每个步骤之间往往需要一定时间的等待,这个时候人是闲下来的,可以去做其它事情。比如把米放入电饭锅后,电饭锅会自动煮,煮熟后会有提示;把汤料入锅后,看着手表过一段时间再换温火。一顿饭就是一个人在这样一个个步骤之间切换完成的。

电饭锅的提示、手表到了某个时间(定时器)等都是事件,收到事件后就可以进行下一个步骤(做出相应的处理)。做饭就是在这样不断处理各种事件,这就是 Event Loop。

Server

HTTP 服务器是常见的服务器类型,往往需要同时处理大量的请求。 服务器对请求的处理一般分为三个阶段:1.Receive;2.Handle;3.Return。

假如分别使用这三种方式处理请求:

  • 一个线程处理完一个请求再处理下一个请求
  • 多个线程每个线程处理一个请求
  • 一个请求同时处理多个请求

Serialization

单线程串行,一个个请求顺序处理:处理请求1 -> 处理请求2 -> 处理请求 X。

简单,资源消耗少。但是资源利用率不高,效率低。如果阻塞会导致等待,并发能力低。

Parallellism

一个线程处理一个请求:A 线程处理请求 1,B 线程处理请求 2,N 线程处理请求 X。

复杂,并发性高,但是消耗大量资源,还有线程切换成本。这个是 Java Web 常用的模式。

Concurrency

一个线程同时处理多个请求:

Time1 处理请求 1
Time2 处理请求 2
Time3 处理请求1
Time4 处理请求 2
TimeN 处理请求 X

略复杂,并发性高,资源消耗低,效率高,但是一个线程的处理能力有限。通过 I/O Multiplexing 实现,比如 Select、Epoll 等。这个 Nginx 和 Node.js 用的模式。 “Concurrency” 模式的 timeline 可能是这样:

time1:建立请求1
time2:建立请求2
time3:等待请求1数据
time4:等待请求2数据
time5:读取请求1数据
time6:处理请求1
time7:读取请求2数据
time8:返回请求1
...

处理请求的三个阶段可以进一步细分为更多小的步骤

  • Receive:建立连接 -> 等待可读 -> 接收数据1 -> 接收数据 N (可能需要多次读取) -> 数据接收完成
  • Handle:可能进行很多的操作,读写数据库、文件、压缩等。每个又会继续分为更多的小步骤,比如读取数据库会有网络 I/O
  • Return:等待可写 -> 写入数据1 -> 写书数据 N (可能多次写入) -> 数据写入完成 -> 关闭连接

把请求分解成多个步骤,把每个步骤完成,最后组合起来,就完成了一个请求处理。

Server & Event Loop

每个步骤之间往往需要一定时间的等待,这个时间线程是闲下来的,可以去做其它事情。比如在等待读取请求数据的时候,可以去处理其它请求。CPU、内存、网卡等之间的速度差异是非常大的,等待的时间 CPU 可以做的事情是很明显的。

建立连接、数据接收完成等都是事件,收到事件后就会进行下一个步骤(做出相应的处理)。服务器就是在这样不断处理各种事件,这个就是 Event Loop。

Why Event Loop

每个人只有一个大脑、两只手、两条腿,我们每天只有 24 小时,所以如何高效的做事情尤为重要。生活或工作中我们往往可以同时应对多任务,我们可以轻松同时应对煮饭、炒菜、煲汤等事情。

对于 CPU 来说,CPU 的计算能力也是一定的,也就是每个时间段可以做的事情也是有限的,所以如何高效的利用 CPU 尤为重要。

无论是做饭还是服务器处理客户端请求,我们都在把每个任务拆解成很多小的步骤,每个步骤之间都有时间差,我们利用时间差可以同时做多个任务。所以 EL 本质上是个体高效处理多任务的一种方式。

CPU 时间片分给线程,目的是为了可以同时处理多任务。但是多线程带来了线程资源的消耗以及线程切换带来的时间片损耗。如果我们可以在一个线程同时处理多个任务,那么就可以避免这个问题,并且保持 CPU 的高效利用。I/O 多路复用让单线程可以同时处理多网络请求,同时 CPU 的速度远远大于网络速度,明显的时间差,所以使用 EL 模型处理多网络请求是一种比较高效的的方式。我们熟知的高性能网络应用 Nginx 和 Redis 都使用了 EL。

图片

Latency numbers every programmer should know

Node Event Loop

Node 为服务端开发设计,不止是网络功能,还需要支持定时器、文件管理、加解密等。所以 Node EL 面临的任务场景更加复杂,如果是定时器这样的非 CPU 密集任务,只要往事件队列加一个事件队列处理即可(时间到了就有事件触发)。如果是面对文件这样的阻塞操作,或是面对加解密这样的 CPU 密集型操作怎处理呢?

再次回到做饭,假如你正在做饭,没有酱油了,如果你去楼下便利店买需要耗时 10 分钟左右,这样可能你煮的菜就糊了。这个时候你可以会喊正在写作业的小盆友帮你打酱油,你接着做饭,等酱油买回来你再做用到酱油的菜。买酱油其实有点像计算机耗时任务,所以对于阻塞和 CPU 密集型操作,我们可以在 EL 主线程开一个线程完成这些任务,EL 继续处理其他事件。等其他线程把耗时任务完成,通过事件通知 EL,EL 根据优先级处理这些事件,这样 EL 就可以应对阻塞和 CPU 密集型的耗时任务了。

Node.js ?

Node 像榴莲,喜欢的人欲罢不能,不喜欢的可能不太能接受。

Node 在后端领域国内使用的比较少,国外多一些,整体来说还是比较小众。Node 可能没有取得所谓的成功,并没有在各个领域大规模使用。Node 不是最好的编程模型,但是 Node 的探索是有意义的。

Node 使得开发高性能服务变得简单高效,编程变得轻松有趣。Node 封装了很多底层的东西,帮我们解决了很多复杂麻烦的问题。但是也导致了很多开发者很少接触到底层,对底层缺少了解。

无意插柳柳成荫,Node 在前端领域大放光彩,几乎成了前端的标配,极大地提高了前端工程的效率和质量。近几年 Serverless 开始火起来,Node 也进一步有了更多的应用范围。特别是对于前端同学来说,Serverless 无论是应用到前端还是前端延伸到后端,都是一个不错的选择。

RY

Node.js

想在服务端运行 JS 的想法很多,只有 Node 成功了。

RY 从小就接触了计算机,本硕博就读数学专业,喜欢创造的乐趣。 从一个想法到不断探索,解决一个个难题实现了 Node。体现了 RY 发现问题、分析问题、解决问题的能力,或者说具备很好的计算思维。

Hello Go

在 Node 发展形势大好的时候 RY 突然放弃 Node,转向 Go。

Go 提供了更好的编程模型,引入了 Goroutine,在充分利用 CPU 的同时,又减少了线程数量和线程上下文切换带来的问题。CPU 密集型和 I/O 密集型场景同时兼具,鱼和熊掌同时兼得。除了优异的性能表现,Go 还有不错的开发效率,简洁易掌握。

整体上,Go 比 Node.js 在设计上更胜一筹,但是这不影响 Node.js 的价值。

Deno

我和 Node.js

Node 只是玩具?

Node、Java、Go 应该怎么选:每种技术都有自己的优势和不足,目前来说没有特别全能可以一统天下的技术。没有最好的技术,只有最合适的。只要可以解决业务问题,那就是好的技术。所以还是需要根据自身技术情况、业务场景以及语言特点做综合选择。

Node 能不能做大型项目:国内外有不少公司使用 Node 做大型项目的成功案例(比如 Paypal、Netflix)。我参与开发的 Node 后端项目大大小小有十几个,有支付相关的、也有日均请求数亿的系统。相比后端,Node 在前端或者中间层应用比较广泛,很多大厂的项目都在用 Node 做服务端渲染、数据聚合等。

JS 很酷

JS 有很多酷炫的语法和技巧,用的时候一时爽,爽过后是痛苦。JS 是在很短的时间下设计出来的,难免有一些问题。大学刚学 JS 的时候抱着厚重的《JavaScript 权威指南》看的头都大,折腾来折腾去啥也记不住,一度忘了怎么写代码。去其糟粕取其精华,忘记那些复杂难以理解的语法,把常用的好的语法用好就足够应对开发了。尽可能不要使用那些令人疑惑的语法,比如 call 和 apply 会经常遇到搞不清楚 this 是什么的问题。 Node 的出现也极大推动了 ES 标准的发展,带来了很多新的好用的语法,拥抱新变化,比如 class 取代原型链、Promise 取代 callback。

JS 是动态类型语言,JS 类型千变万化,你遇到的变量可能不是你想要的。可以使用 TS ,或者参考《JS 数据和类型》提到的“动态类型静态化”,以不变应万变。

Node.js Everywhere

2016 年 Node.js 官方在中国第一次官方活动:Node.js Everywhere。

图片

参考

3 回复

👍,现在node.js的多线程方案也有,CPU密集多少也是可以做点的

ts全栈就完事了,研究产品吧

@zy445566 之前比较多用 C/C++ 拓展,我们微服务选了多语言。

回到顶部