Node 初识
什么是 Node
- 是JS运行时环境
- 构建在V8引擎之上
- 通过libuv实现异步I/O
- 目标是让并发编程更简单,主要应用在以网络编程为主的I/O密集型应用中,不适合CPU密集型应用。JAVA在基础平台建设及大数据等领域有着非常深厚的基础,直接使用即可。
- V8 解释并执行JS
- libuv 由事件循环和线程池组成,负责所有I/O任务的分发与执行。
Node 的特点
- 构建传统网站
- 构建移动端 API:在后端的 API 接口开发上封装一层专门供前端使用的 API Proxy 是非常有必要的。后端API接口开发用于数据库访问,将返回的数据进行包裹,以 HTTP 形式返回;API Proxy 针对前端使用的API进行优化,使前端开发更人性化。
- 构建 RPC 服务:如 JAVA 的 Dubbo,Node 实现的 DNode、微服务工具集Seneca、跨语言的gRPC客户端。将数据库访问返回的数据以 TCP 形式传输给调用方。组装多个RPC服务,完成某项服务。将RPC服务包装成HTTP API。
- 前后端分离:前端页面静态化;前端页面服务化(PAAS);服务端渲染(SSR); 渐进式Web应用(PWA)
- 适用于Severless:无须关心运维(自带运维功能,一般集成Kubernetes),无须关心流量(自带服务网格功能,比如Istio,根据流量可以快速实例化容器),无须关心高并发(API层有缓存)。
Node 的应用场景
- 跨平台开发:PC web、移动端、HTML5、React Native,Weex 以及 硬件 Ruff.io
- 后端开发:面向网站、API、RPC 服务等
- API: 绝大部分是I/O密集型应用,是node最擅长的
- RPC:主要是对OLTP(联机事务处理过程)数据库进行操作,node可以支持,但如果是复杂计算,使用Go或JAVA会更好。
- 前端开发:三大框架辅助开发及工程化演进过程(使用Gulp、Webpack构建Web开发工具)
- 工具开发:主要是开发npm上的各种工具模块,包括各种前端预编译工具、构建工具Gulp、脚手架、命令行工具等
- 数据挖掘
- AI
Node 安装与入门
3m安装法
- nvm: 解决多版本共存、切换、测试等问题
- npm: 解决Node模块安装问题,其本身也是一个Node模块,每次安装都会内置某个版本的npm
nrm: 解决npm镜像访问慢的问题,提供测速、切换下载器(registry)功能
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
cat ~/.bashrc_nvm | grep nvm # 我自己的Mac电脑是把nvm命令的执行路径放到该文件下的。有可能你的在~/.bashrc
source ~/.bashrc_nvm # 使系统环境变量生效
vi ~/.zshrc # 手动将环境变量放到~/.zshrc里,将~/.bashrc_nvm的内容copy一份到这个文件中
source ~/.zshrc # 使系统环境变量生效
nvm ls-remote # 查看可安装的node版本
nvm install 10.15.3
which node
nvm alias default node # 手动指定一个default别名
node -v
nvm ls
nvm use stable # 切换版本
nvm reinstall-packages stable # 一键安装全局模块
⚠️若经常切换版本,最痛苦的是全局模块需要重新安装。通过
nvm reinstall-packages
来解决npm v2: 典型的树形依赖,层次深,依赖重叠,安装时间长,但相对更清晰
- npm v3: 扁平化依赖,将依赖移到了顶层,同一模块多版本依赖时采用npm v2 模式,由于Node项目模块依赖非常多,因此在查找依赖时特别麻烦,虽没有npm v2用起来体验好,但下载速度确实快了不少。
- npm v5: 自动记录依赖树,下载使用强校验,重写缓存系统等功能得到了升级和改造。代表性特征是新增了package.json文件,在操作依赖时默认生成,用于记录和锁定依赖树的信息,与Yarn类似。 | 命令 | 简写 | 说明 | | —- | —- | —- | | 无 | 无 | 将模块安装到本地 node_modules目录下,但不保存到package.json里 | | —save-prod | -P | 将模块安装到本地 node_modules目录下,同时保存到package.json里的dependencies,这在安装模块时是必须安装的 | | —save-dev | -D | 将模块安装到本地 node_modules目录下,同时保存到package.json里的devDependencies仅供开发时使用,一般测试模块等辅助开发工具都会这样安装 | | —global | -g | 安装的模块为全局模块,如果是命令行模块,会直接链接到环境变量里 |
如果用nvm
安装的安装包放在用户目录的nvm
版本对应的bin
目录~/.nvm/versions/node/v13.3.0
下,否则在/usr/local
下。
npm install --global nrm
nrm test # 测速
nrm ls
nrm use cnpm
nrm add yourcompany http://registry.npm.yourcompony.com/
hello node
touch hello_node.js
const http = require('http')
http.createServer((req, res) => {
let status = 200
res.writeHead(status, { 'Content-Type': 'text-plain' })
res.end('Hello Node')
}).listen(3000, '127.0.0.1')
console.log('Server running at http://127.0.0.1:3000/')
编程与调试
编程三等境界:
- 打日志:最简单、便捷、略显低级
- 断点调试:可以很直观地跟踪代码执行逻辑、调用栈、变量等
- 测试驱动开发:在写代码之前先写测试,在保证代码质量、重构等方面有帮助
VSCODE
安装code命令
打开Command Palette(command shift P)
,并键入 shell command:Install 'code' command in PATH
调试
https://code.visualstudio.com/docs/editor/debugging
编号 | 名称 | 描述 |
---|---|---|
1 | Launch Program | 直接执行 |
2 | Launch via NPM | 通过npm来启动 |
3 | Attach to Port | 附加端口,根据端口来查找进程 |
4 | Attach to Process | 附加到进程,即跨进程调试 |
5 | Nodemon Setup | 代码变动自动重启的 node monitor, |
6 | Mocha Tests | 简单、强大的测试框架 |
7 | Yeoman generator | 使用最多的脚手架生成器 |
8 | Gulp task | 最流行的流式构建系统, |
request 有两个选项,对应着两种调试模块:
- launch:启动程序,相当于直接在编辑器里启动node.js,即本地调试
- attach: 附加到进程,在编辑器外通过node —debug 命令启动,然后附加到 debug 进程中,即远程调试
{
"type": "node",
"request": "attach",
"name": "启动程序",
"address": "localhost",
"port": 5858
},
{
"type": "chrome",
"request": "launch",
"name": "Chrome",
"url": "http://127.0.0.1:8092/",
"webRoot": "${workspaceRoot}"
},
{
"type": "node",
"request": "launch",
"name": "Mocha JS",
"program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
"args": [
"-u",
"tdd",
"--timeout",
"999999",
"--compilers",
"js:babel-core/register",
"--colors",
"${workspaceRoot}/test/js/**/*.spec.js"
],
"internalConsoleOptions": "openOnSessionStart"
},
{
"type": "node",
"request": "launch",
"name": "Mocha TS",
"program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
"args": [
"-u",
"tdd",
"--timeout",
"999999",
"-r",
"ts-node/register",
"--colors",
"${workspaceRoot}/test/ts/**/**/*.spec.ts"
],
"protocol": "auto",
"internalConsoleOptions": "openOnSessionStart"
}
{
"type": "node",
"request": "attach",
"name": "Attach to Remote",
"address": "TCP/IP address of process to be debugged", // IP 地址或域名
"port": 5858, // 端口
"localRoot": "${workspaceRoot}", // 本地的代码根目录
"remoteRoot": "Absolute path to the remote directory containing the program" // 远程代码的相对目录
},
更了不起的node
架构变迁
| | 单体式架构 | 页面即服务 | | —- | —- | —- | | 开发难度 | 越大越复杂 | 单个页面不会特别复杂,更容易把控 | | 可用性 | 容易雪崩,影响极大 | 单个页面崩溃对整体来说影响极小 | | 运维 | 容易 | 麻烦(必须配合DevOps) |
单体式架构(Monolithic Architecture)
- 表现层:处理HTTP请求,直接返回HTML渲染,或者返回API结果。对于一个复杂的应用系统,表现层通常是代码中比较重要的部分。
- 业务逻辑层:完成具体的业务逻辑,是应用的核心组成部分
- service,通常组合多个DAO对象进行某项业务处理
- controller里组装了多个service对象,可实现具体的功能
数据访问层:访问基础数据,例如数据库、缓存和消息队列等
node层
- 提供请求转发、反向代理等功能
- 提供web服务,所有后端语言能做到的,node都能做到
- 圈定范围,便于维护,只要修改功能包含在当前node项目范围内即可维护
- 充当HTTP客户端,访问后端接口,但一般不直接访问数据库
- nginx层
- 负载均衡
- 反向代理
- 缓存
- 限流限速
- nginx需要繁杂的配置文件,且不能动态控制与实时生效,而openResty 只需要少量的lua代码即可
Node模块
分类 | 举例 |
---|---|
压缩 | UglifyJS |
CSS预处理 | Less |
依赖管理 | npm |
模块系统 | AMD | CMD | CMJ | ESM |
模块加载 | system.js |
模块打包器 | webpack + npm scripts |
构建工具 | gulp |
模版引擎 | jade | nunjucks |
JS超集 | TS(动态类型一时爽,代码重构火葬场) |
跨平台打包 | Electron(客户端) | cordova(移动端) | Ruff.io(物联网) |
生成器 | Yeoman | vue-cli | create-react-app |
其他 | imagemin | DataURL |
后端 | express | koa | egg |
测试 | AVA(支持新特性,基于Babel安装) | Mocha |
数据库 | Mongoose(MongoDB) |
框架选型原则:
- 考虑业务场景、特点,不必为了用而用,避免本末倒置
- 考虑自身团队能力、喜好,有时候技术选型决定团队氛围,需要平衡激进与稳定
- 保证在出现问题的时候有人能够做到源码级定制
- 考虑安全、生态及历史遗留问题的处理
BFF
- API Proxy
- 服务组装
- API 网关
- 请求转发
- 跨域JSONP支持
更好的node
编程范式
面向过程
面向对象
TS 无缝兼容ES6,且提供静态类型、接口、反射、范型等特性,在代码质量、抽象程度等方面都表现的比较好。
函数式编程
- 一等公民是函数
- 函数柯里化(curry): 传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数
- 通过“函数组合”来解决函数嵌套问题
- 函数式编程的思想如果是在团队中应用,还是要谨慎选择,持观望态度,因为其一学习成本高,其二维护成本高。
Metetor: 著名的同构 Web 框架
ThinkJS: 典型的结合 npm scripts 和 Babel 一起使用的框架
单线程会死
Node是单线程的,只要代码出错肯定是要崩溃的。
- uncaughtException
- try/catch 捕获异常
- 自动重启:forever模块 | pm2
- 通过cluster模块充分利用服务器多核优势,启动多个实例
即本质上单线程会死是个伪命题:
- 单个应用实例可以适当捕获异常,减少崩溃几率
- 单个应用实例崩溃之后,采用forever或pm2自动重启,可以继续服务
- 利用多核集群同时在一台服务器上启动多个实例,崩溃的几率极低
- 应用线上部署就只部署一台服务器吗?这种几率其实也很小,多台服务器也要做集群。
- 如果所有集群中的服务器都崩溃了该怎么办?这其实是运维的问题,和Node无关
异步解决方案
解决方案 | 描述 | 推荐指数 |
---|---|---|
callback | Node API 天生就是这样的,异步必须要约定错误优先风格的问题 | 3 |
thunk | 编译器的“传名调用”实现,通常做法是将参数放到一个临时的函数之中,再将这个临时函数传入函数体。 | 1 |
promise | 最开始是社区定义的Promise/A+规范,大家习惯称之为Promise,随后成了ES6语言标准 | 4 |
generator | Es6中的生成器,用于计算,但TJ想将其用作流程控制 | 1 |
co | Generator用起来非常麻烦,故而TJ编写了co这个Generator生成器,用法简单 | 2 |
async | NodeV7以上的版本都可以使用 | 5 |
Node 是如何执行的
以 node v8 为基础剖析核心原理:
- V8 和 libuv 的作用
- JS 是如何与C/C++通信的
- 掌握模块机制
./configure --debug # 开启debug会进行两个CMake任务,且在源码根目录分别连接node.js和node_g,编译完成后,生成的文件将位于out/Release和out/Debug目录下
make -j4
sudo make install
模块与核心
工具资源
vscode debugger:https://code.visualstudio.com/docs/editor/debugging
node 各版本对ES6的支持情况:https://node.green/