从零搭建 Node.js 企业级 Web 服务器(零):静态服务

前言

过去 5 年,我先后在菜鸟网络和蚂蚁金服作开发工做,一方面支撑业务团队开发各种业务系统,另外一方面在本身的技术团队作基础技术建设。期间借着 Node.js 的锋芒作了很多 Web 系统,有的至此生气蓬勃、有的早已夭折淡出。在实践中,蚂蚁的 Chair 与淘系的 Midway 给了我很多启发,也借鉴了很多 bad case。思考过身边团队、本身团队、国外团队的各类案例以后发现,搭建一个 Node.js 企业级 Web 服务器并不是难事,只是必须作好几个关键事项html

在接下来的一段时间里,我会以如何 “从零搭建 Node.js 企业级 Web 服务器” 为主题,将本身的所见所闻、所思所想详尽地记录下来,每一个章节最后会附上实现本章内容的源码,但愿能够帮助正在学习和了解 Node.js 的朋友对 Web 服务器领域得到更清晰的理解和洞见。前端

阅读提示:node

  • 本文着重表述 Web 后端技术相关内容,Web 前端内容采用 JavaScript Modules 进行演示。
  • 本文须要读者具有基础的编程能力以及对计算机网络的基本了解,一些经常使用术语限于篇幅再也不展开。

准备环境

安装 Node.js

Node.js 发布版本分为 Current 和 LTS 两类,前者每 6 个月迭代一个大版本,快速提供新增功能和问题修复,后者以 30 个月为一个大周期,为生产环境提供稳定依赖。当前 Node.js 最新 LTS 版本为 12.18.2,本文以此版本做为运行环境,能够在官网下载并安装。git

安装完成后,在命令行输入 node --version 查看输出是否为 v12.8.2,若是一致,那么安装成功。github

另外,有兴趣的读者能够尝试经过 nvm / nvm-windows 管理多个 Node.js 版本,此处不是本文重点再也不展开。redis

安装 Yarn

Node.js 提供了本身的包管理器 npm,npm 默认从海外官方的 registry 拉取包信息速度比较慢,须要执行如下命令设置使用国内镜像地址:docker

$ npm config set registry http://r.cnpmjs.org/

经过 npm 能够全局或本地安装依赖包,当本地安装时 npm 会根据 package.json 安装最新的依赖包,同时自动生成并更新 package-lock.json 文件,这就引起了一个问题:若是一个依赖包在 package.json 标记版本范围内发布了有问题的新版本,那么咱们本身的项目也会跟着出问题。express

为了解决这个问题,就引入了第三方包管理器 yarn,经过如下命令安装:npm

$ npm i -g yarn

相比 npm,yarn 会严格按照自动生成的 yarn.lock 本地安装依赖包,只有增删依赖或者 package.json 标记版本发生不可兼容的变化时才会更新 yarn.lock,这就完全杜绝了上述 npm 的问题。考虑到企业级 Web 服务器对稳定性的要求,yarn 是必要的。编程

安装 Docker

通常来说,作一个 Web 服务器会有两种部署选择,要么是传统的包部署,要么是容器镜像部署,后者较前者在编排上更方便,结合 Kubernetes 能够作到很好的伸缩,是当前发展的趋势。Docker 做为主流容器技术必须熟练掌握,能够在官网下载并安装。

写一个静态资源服务器

初始化工程

准备好了环境就能够开始编码了,先新建工程根目录,而后进入并初始化 package.json 与目录结构:

$ mkdir 00-static   # 新建工程根目录
$ cd 00-static      # 进入工程根目录

$ yarn init -y      # 初始化 package.json
yarn init v1.22.4
success Saved package.json

$ mkdir src         # 新建 src 目录存放核心逻辑
$ mkdir public      # 新建 public 目录存放静态资源

$ tree -L 1         # 展现当前目录内容结构
.
├── package.json
├── public
└── src

Express 仍是 Koa?

Express 与 Koa 均是 Node.js 服务端基础框架。Express 发布于 2010 年,凭借出色的中间件机制在开源社区积累了大量的成熟模块,如今是 OpenJS 基金会 At-Large 级别项目。Koa 发布于 2013 年,相比 Express 具有了更加完善的中间件机制以及编程体验,但在开源社区模块积累的质与量上还有必定差距。在此比较几个经常使用模块:

模块名称 功能简介 Express / Koa Star Contributers Used by 最近提交时间
passport 认证登陆 Express 17.7k 33 385k 2020-06-10
koa-passport 认证登陆 Koa 737 21 4.7k 2019-07-13
connect-redis 会话存储 Express 2.3k 51 26.3k 2020-07-10
koa-redis 会话存储 Koa 310 13 2.7k 2020-01-16
helmet 网络安全 Express 7.2k 25 136.4k 2020-07-11
koa-helmet 网络安全 Koa 546 24 4.1k 2020-06-03

上表整理自 Github 截止 2020 年 7 月 20 日的数据。

相比 Koa 模块,Express 模块广泛在星数(Star)、贡献者数(Contributers)、使用数(Used by)上高出一个层次,同时 Express 模块的贡献者更热心于维护与更新,Koa 尽管在国内受到过一些追捧,但在更全面的考量下 Express 才是更稳健的选择。在工程根目录执行如下命令安装:

$ yarn add express  # 本地安装 Express
# ...
info Direct dependencies
└─ express@4.17.1
# ...

$ tree -L 1         # 展现当前目录内容结构
.
├── node_modules
├── package.json
├── public
├── src
└── yarn.lock

静态服务

如今能够开始写应用逻辑了,本章先作一个静态资源服务器,以 public 目录为静态资源目录:

// src/server.js
const express = require('express');
const { resolve } = require('path');
const { promisify } = require('util');

const server = express();
const port = parseInt(process.env.PORT || '9000');
const publicDir = resolve('public');

async function bootstrap() {
  server.use(express.static(publicDir));
  await promisify(server.listen.bind(server, port))();
  console.log(`> Started on port ${port}`);
}

bootstrap();
<!-- public/index.html -->
<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <h1>It works!</h1>
  </body>
</html>
$ tree -L 2 -I node_modules   # 展现除了 node_modules 以外的目录内容结构
.
├── package.json
├── public
│   └── index.html
├── src
│   └── server.js
└── yarn.lock

逻辑写好以后在 package.json 中设置启动脚本:

{
  "name": "00-static",
  "version": "1.0.0",
-  "main": "index.js",
+  "scripts": {
+    "start": "node src/server.js"
+  },
  "license": "MIT",
  "dependencies": {
    "express": "^4.17.1"
  }
}

而后就能够启动应用了:

$ yarn start
> Started on port 9000

访问 http://localhost:9000/ 便可看到 index.html 内容:

765bb72ad6058d32b641116e26f8d7338cf949f9.jpg

使用容器

接下来经过 Docker 对作好的静态资源服务器进行容器化,新建如下配置文件:

# Dockerfile
FROM node:12.18.2-slim

WORKDIR /usr/app/00-static
COPY . .
RUN yarn

EXPOSE 9000
CMD yarn start
# .dockerignore
node_modules
$ tree -L 1 -a  # 展现包括 . 开头的所有目录内容结构
.
├── .dockerignore
├── Dockerfile
├── node_modules
├── package.json
├── public
├── src
└── yarn.lock

而后构建镜像并启动容器:

$ docker build -t 00-static:1.0.0 . # 构建容器镜像,命名为 00-static,标签为 1.0.0
# ...
Successfully tagged 00-static:1.0.0

$ docker run -p 9090:9000 -d --name 00-static 00-static:1.0.0 # 以镜像 00-static:1.0.0 运行容器,命名为 00-static

$ docker logs 00-static   # 查看 00-static 容器的日志
> Started on port 9000

$ docker stats 00-static  # 查看 00-static 容器的状态
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
43c451232fa5        00-static           0.03%               37.41MiB / 1.945GiB   1.88%               8.52kB / 3.35kB     0B / 0B             24

访问 http://localhost:9090/ 便可与以前同样看到 index.html 内容:

e369752861fb54057d2683cca41c81eb01071368.jpg

本章源码

host1-tech/nodejs-server-examples - 00-static

更多阅读

从零搭建 Node.js 企业级 Web 服务器(零):静态服务
从零搭建 Node.js 企业级 Web 服务器(一):接口与分层
从零搭建 Node.js 企业级 Web 服务器(二):校验