如何部署 Node.js Web 应用程序?
Node.js 是一种 JavaScript 运行时环境,允许您在浏览器外运行 JavaScript 代码。
Node.js 基于 Chrome V8 JavaScript 引擎构建,采用事件驱动、无阻塞 I/O 模型,因此在构建服务器端应用程序时非常高效。
本文将探讨 Node.js 在服务器端应用程序开发方面的优势和局限性,以及 Node.js 应用程序的部署选项。
此外,您还将免费在Back4app Containers上构建、dockerize 和部署一个 Node.js 应用程序。
Contents
用于网络应用程序开发的 Node.js 的优势
Node.js 自 2009 年发布以来,一直是构建服务器端网络应用程序的首选。原因如下
效率和可扩展性
如前所述,Node.js 在 Chrome 浏览器的 V8 引擎上运行,这使它能够运行 JavaScript 代码。该引擎使用即时编译(JIT)将本地 JavaScript 代码转换为机器代码。
在執行時,V8 的 Turbofan 和 Crankshaft 元件會分析機器碼並重新編譯以提供最佳效能。
此外,由于 Node.js 的事件驱动模型,它可以根据用户交互等事件执行代码,而不会阻塞应用程序的主线程,因此非常适合流量较大的应用程序。
Node.js 的模块化架构和对集群的内置支持使使用它开发的应用程序易于扩展。Node.js 的模块化架构允许您将应用程序分解为可独立扩展的组件。
集群模块允许你在多个内核或服务器上生成多个应用程序实例。这样就能实现水平扩展,即通过向集群添加更多服务器来扩展应用程序。
学习曲线较浅
Node.js 基于 JavaScript,这是一种广泛用于网络开发的编程语言。JavaScript 在 Node.js 中的使用使已经熟悉 JavaScript 的开发人员更容易使用 Node.js 进行服务器端开发。
这降低了网络开发的复杂性,简化了开发流程。
大型生态系统
Node.js 拥有一个庞大而活跃的开发者社区,他们创建了一个庞大的模块和软件包生态系统。
Node.js 软件包管理器 npm 承载了 100 多万个软件包,您可以用它们为应用程序添加功能。
这些软件包小到像 lodash 这样的实用程序库,大到可用于构建复杂网络应用程序的大型框架 Nest.js。
各种模块和软件包的提供可以大大减少开发网络应用程序所需的时间和精力。
您可以利用这些软件包为您的应用程序添加身份验证、数据库集成和服务器端渲染等功能。
用于网络应用程序开发的 Node.js 的局限性
说到网络应用程序开发,Node.js 有很多优点,例如高效的性能、可扩展性以及庞大的模块和软件包生态系统。然而,与其他技术一样,Node.js 也有一些局限性。其中包括以下限制。
内存消耗大
Node.js 使用非阻塞 I/O 模型,这意味着它可以在不创建新线程的情况下并发处理多个请求。不过,每个请求的处理仍需要分配内存。
这意味着 Node.js 应用程序可能会消耗大量内存,尤其是在处理大量并发请求时。对于在内存有限的系统上运行的应用程序来说,这可能是个问题。
异步编程模型
虽然 Node.js 的异步编程模型具有很大的优势,但它也可能会给开发人员带来复杂性。
异步编程需要一种不同的程序流程思维方式。对于习惯于同步编程的开发人员来说,这种转变可能具有挑战性。
此外,异步编程还可能导致回调地狱(callback hell),即由于嵌套的回调,代码变得难以阅读和维护。
单线程事件循环
Node.js 设计用于处理 I/O 密集型任务,如网络通信、文件 I/O 和数据库操作。
不过,对于复杂计算、数据处理或机器学习等 CPU 密集型任务来说,它可能不是最佳选择。
这是因为 Node.js 使用单线程事件循环模型,这意味着它一次只能执行一个任务。
如果任务需要很长时间才能完成,就会阻塞事件循环,导致应用程序无响应。
部署 Node.js Web 应用程序
部署 Node.js 应用程序有多种方法。让我们来探讨其中一些。
云托管服务
雲端託管服務讓您可以將 Node.js 應用程式部署在由 Amazon Web Services (AWS)、Google Cloud Platform (GCP) 或 Microsoft Azure 等公司管理的伺服器上。
它们具有可扩展性、全球可用性、易于部署和现收现付等优势。此外,它们还与数据库和负载平衡等其他云服务集成,帮助您构建更好的应用程序。
您只需将 Node.js 应用程序部署到云提供商的服务器上,即可使用云托管服务。然后,您就可以通过网络浏览器或其他客户端应用程序访问您的应用程序。
Node.js 云托管服务的一些示例包括
- AWS Elastic Beanstalk
- GCP App Engine
- Microsoft Azure App Service
这些平台可以轻松部署、扩展和管理 Node.js 应用程序,而无需担心底层基础设施。
此外,它们还提供自动扩展、负载平衡和内置监控等功能,帮助您保持应用程序的平稳运行。
虚拟专用服务器(VPS)
虚拟专用服务器 (VPS) 是在物理服务器上运行的虚拟机,可让您像在专用服务器上运行一样安装和运行 Node.js 应用程序。
与共享主机相比,虚拟专用服务器为您提供了更大的控制权和定制选项,同时也为您提供了比专用服务器更具成本效益的选择。
要为您的 Node.js 应用程序使用 VPS 托管,请选择提供预配置 Node.js 映像的托管提供商,或自行安装 Node.js 和其他依赖项。
Node.js VPS 托管提供商的一些示例包括
- DigitalOcean
- Linode
- Vultr
集装箱化
容器化是一种在容器化环境中部署和运行应用程序的技术,它将应用程序与底层基础设施隔离开来。
容器为传统虚拟机提供了轻量级和灵活的替代方案,使其成为部署 Node.js 应用程序的理想选择。
它们具有可移植性,允许您在本地机器上构建和测试应用程序,然后将其部署到任何支持容器化的平台上。
容器还可以根据工作负载轻松扩大或缩小规模,从而提高可扩展性。它们在不同平台上保持一致,使管理和维护应用程序变得更加容易。
提供容器化即服务(CaaS)的平台实例包括
- Back4app Containers
- AWS ECS
- Azure ACI
- Google GKE
使用 Back4app 容器在 Back4app 上部署 Node.js 应用程序
Back4app 是一个云平台,可让您使用直观的用户界面或功能齐全的 CLI 工具创建、管理和部署网络应用程序。Back4app 提供一系列服务,其中之一就是容器化。
Back4app 容器通过自动执行重复性任务和管理服务器端基础架构,消除了开发与生产之间的差距,确保您无需担心 DevOps。
在本文中,您将使用 Back4app 容器构建和部署一个简单的 Node.js 应用程序。您要构建的 Node.js 应用程序是一个简单的书店 API,支持 CRUD(创建、读取、更新、删除)功能。
设置开发环境
创建一个新的项目目录,并通过运行下面的命令在项目目录中初始化 npm:
mkdir bookstoreapp && cd bookstoreapp && npm init -y
接下来,运行下面的命令安装项目所需的依赖项:
npm install express dotenv mysql knex
您在上面安装的依赖项是
- Express.js:Express 是一个 Node.js 框架,可简化 Node.js 应用程序开发流程。
- dotenv: dotenv 是一个 npm 软件包,可用于管理环境变量。
- MySQL:MySQL 依赖关系是 MySQL 的 Node.js 驱动程序,您将把它用作此应用程序的数据库。
- KnexKnex 是 JavaScript 的查询生成器。要与数据库交互而无需编写原始 SQL 查询,您将需要这个依赖项。
接下来,在项目根目录下创建routes.js
和index.js
文件。
然后,将下面的代码块添加到您的index.js
文件中:
//index.js
require("dotenv").config();
const express = require("express");
const app = express();
const port = 3000;
const router = require("./routes.js");
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use("/", router);
app.listen(port, () => {
console.log(`App listening at ${port}`);
});
上面的代码块创建了一个 Express 服务器,并在端口 3000 上侦听传入的 HTTP 请求。它使用中间件函数解析传入数据,并将路由器与根路径关联起来,以处理传入请求。
最后,它会启动服务器,并向控制台记录一条信息,说明服务器正在运行,并在指定端口上监听。
接下来,在package.json
文件中添加启动
脚本。像这样
"start": "node index.js",
连接数据库
Knex 需要一个包含连接数据库配置选项的 knexfile。
运行以下命令创建 knexfile:
knex init
要配置 Knex 使用 MySQL,请用下面的代码块替换knexfile.js
文件的内容:
// Update with your config settings.
require("dotenv").config()
/**
* @type { Object.<string, import("knex").Knex.Config> }
*/
module.exports = {
development: {
client: "mysql",
connection: {
host: process.env.DEV_HOST,
user: process.env.DEV_USER,
password: process.env.DEV_PASSWORD,
database: process.env.DEV_NAME,
},
migrations: {
directory: "./db/migrations",
}
},
production: {
client: "mysql",
connection: {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
},
migrations: {
directory: "./db/migrations",
}
},
};
接下来,在项目根目录下创建一个db
文件夹,并创建一个db.js
文件。
将下面的代码块添加到您的db.js
文件中:
//db.js
const knex = require("knex");
const knexFile = require("../knexfile.js");
const environment = process.env.NODE_ENV || "development";
module.exports = knex(knexFile[environment]);
上述代码块将环境变量
设置为NODE_ENV
环境变量,如果未设置NODE_ENV
,则将环境变量
设置为开发
环境变量
。这样,您就可以为不同的环境(如开发环境或生产环境)指定不同的配置。
创建迁移文件
运行以下命令为数据库创建迁移文件:
knex migrate:make bookstore
上述命令会在knexfile.js
指定的文件路径(”./db/migrations”)中创建迁移文件。
接下来,打开迁移文件,用下面的代码块替换其中的代码:
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.up = function (knex) {
return knex.schema.createTable("books", (table) => {
table.increments("id").primary();
table.string("title");
table.string("author");
table.string("genre");
table.timestamps(true, true);
});
};
/**
* @param { import("knex").Knex } knex
* @returns { Promise<void> }
*/
exports.down = function (knex) {
return knex.schema.dropTableIfExists("books");
};
然后,运行下面的命令来执行迁移文件:
knex migrate:latest
实施路由选择
最后,将下面的代码块添加到routes.js
文件中:
const express = require("express");
const router = express.Router();
const db = require("./db/db.js");
// GET /books
router.get("/books", async (req, res) => {
try {
const books = await db("books");
res.json(books);
} catch (err) {
console.error(err);
res.status(500).send("Internal Server Error");
}
});
//POST /books/new
router.post("/books/new", async (req, res) => {
try {
const { title, author, genre } = req.body;
const book = await db("books").insert({
title,
author,
genre,
});
res.status(201).json(book);
} catch (err) {
console.error(err);
res.status(500).send("Internal Server Error");
}
});
//PUT /books/:id
router.put("/books/:id", async (req, res) => {
const { id } = req.params;
try {
const { title, author, genre } = req.body;
const book = await db("books").where({ id }).update(
{
title,
author,
genre,
},
["id", "title", "author", "genre"]
);
if (book.length !== 0) {
res.status(201).send(book);
} else {
res.status(404).json({ error: "Book not found" });
}
} catch (err) {
console.error(err);
res.status(500).send("Internal Server Error");
}
});
//DELETE /books/:id
router.delete("/books/:id", async (req, res) => {
const { id } = req.params;
try {
const book = await db("books").where({ id }).del();
if (book !== 0) {
res.status(200).json({ message: "Book deleted" });
} else {
res.status(404).json({ error: "Book not found" });
}
} catch (err) {
console.error(err);
res.status(500).send("Internal Server Error");
}
});
module.exports = router;
上面的代码块定义了几个处理 HTTP 请求的路由,包括获取所有图书、创建新图书、更新现有图书和删除图书。
创建 Dockerfile
Dockerfile 是一个文件,其中包含一系列以特定格式编写的指令,说明如何构建 Docker 镜像。Docker 镜像是一个容器的快照,其中包含运行应用程序所需的一切,如应用程序代码、运行时、库和系统工具。
要在 Back4app Containers 上运行 Node.js 应用程序,必须创建一个 Dockerfile,其中包含构建 Docker 映像的说明。
运行下面的命令创建 Dockerfile:
touch Dockerfile
接下来,您需要为 Node.js 应用程序选择一个基础镜像。Docker 中的基础镜像是构建新 Docker 镜像的起点。它是构建 Docker 镜像的基础。
将下面的代码块添加到 Dockerfile 中,以指定基本映像:
# Specify base image
FROM node:18-alpine
这一行指定了构建 Docker 镜像的基础镜像。在本例中,Node.js 18 版本运行在 Alpine Linux 发行版上。
接下来,你需要指定工作目录。Dockerfile 中的所有后续命令都将相对于该目录执行。
在 Dockerfile 中添加以下代码块,指定工作目录:
# Specify working directory
WORKDIR /app
然后,您需要将package.json
和package-lock.json
文件从当前目录(即包含 Dockerfile 的目录)复制到工作目录(/app
)。
将下面的代码块添加到 Dockerfile 中,以复制文件:
# Copy package.json and package-lock.json
COPY package*.json ./
接下来,您需要在工作目录中运行npm install
命令,以安装package.json
和package-lock.json
中列出的 Node.js 依赖项。
将下面的代码添加到 Dockerfile 中,以安装依赖项:
# Install dependencies
RUN npm install
上述命令将安装package.json
****andpackage-lock.json
文件中列出的所有依赖项,并将它们存储在指定工作目录下的node_modules
文件夹中。
接下来,您需要将应用程序的源代码复制到工作目录中。
将下面的代码块添加到 Dockerfile 中,复制源代码:
# Copy source code
COPY . .
接下来,你需要向主机暴露一个端口。暴露端口可以让 Docker 容器在运行时接收暴露端口上传入的网络连接。
在 Dockerfile 中添加以下代码块,将端口 3000 暴露给主机:
# Expose port 3000
EXPOSE 3000
最后,您需要指定 Docker 容器启动时应运行的命令。通常,Node.js 应用程序使用npm start
命令启动。
将下面的代码块添加到 Dockerfile 中,以指定命令:
# Run the app
CMD ["npm", "start"]
完成后的 Dockerfile 应与下面的代码块相似:
# Specify base image
FROM node:18-alpine
# Specify working directory
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy source code
COPY . .
# Expose port 3000
EXPOSE 3000
# Run the app
CMD ["npm", "start"]
创建 Dockerfile 后,将代码推送到 GitHub。
创建新的 Back4app 应用程序
在 Back4app 上部署 Node.js 应用程序的第一步是在 Back4app 上创建一个账户(如果您还没有账户)。您可以按照以下步骤创建一个账户。
- 导航至Back4app 网站。
- 然后,点击登陆页面右上角的注册按钮。
- 最后,填写注册表并提交。
成功创建 Back4app 账户后,登录 Back4app 账户并点击右上角的 “新建应用程序“按钮。
单击此按钮将进入一个页面,在此您可以选择 “如何创建新应用程序?由于您要使用容器化进行部署,因此请选择“容器即服务”,如下图所示。
接下来,将您的 GitHub 账户连接到 Back4app 账户。您可以选择让 Back4app 访问您账户中的所有版本库或特定版本库。
选择要部署的应用程序,在本例中就是本教程中构建的应用程序,然后单击 “选择“。
单击 “选择 “按钮后,您将进入一个页面,在这里您需要填写有关应用程序的一些信息,如名称、分支、根目录和环境变量。
请务必填写应用程序运行所需的所有环境变量。填写所需详细信息后,单击 “创建应用程序“,如下图所示。
单击 “创建应用程序“按钮启动部署流程。部署过程完成后,将在屏幕左角指定一个 URL,通过该 URL 可以访问已部署的应用程序,如下图所示。
如果部署过程耗时较长,您可以查看日志,了解部署是否发生错误,或查看Back4app 故障排除指南。
结论
Node.js 是一种流行的框架,它具有许多优点,如快速性能、可扩展性和灵活性。不过,它也有一些局限性,比如它的单线程特性,这可能会使它在处理繁重的工作负载时面临挑战。
尽管存在这些限制,但 Node.js 应用程序仍有多种部署选项,其中包括 Back4app,它为托管和管理 Node.js 应用程序提供了一个可靠且用户友好的平台。
通过创建 Node.js 应用程序并按照本文概述的步骤操作,您就可以在 Back4app 上轻松部署 Node.js 应用程序,并享受其诸多优势。
还对 Node.js 部署和托管感兴趣?请查看这两个教程:
常见问题
如何部署 Node.js Web 應用程式?
– 設定您的開發環境
– 連接到您的資料庫
– 建立遷移文件
– 實施路由
– 建立 Dockerfile
– 建立一個新的 Back4app 應用程式