2. Node.js 微服务开发环境搭建
微服务开发环境的核心诉求是“可复现”:同一份代码,在任何一台机器上都能用相同的 Node.js 版本、相同的依赖解析结果、相同的容器运行时启动。本节围绕四件事展开——Node.js 版本管理、npm 与 pnpm 工作区、Docker 与 Docker Compose 安装、项目目录结构初始化。它们不是四个孤立步骤,而是一条从“本机运行时”到“可编排服务”的搭建链路。
一、Node.js 版本管理:先锁定运行时
微服务通常由多个独立仓库或一个多包仓库组成,不同服务可能依赖不同 Node.js 主版本。若直接用系统包管理器安装单一 Node.js,升级或降级会互相干扰。推荐使用版本管理器,如 nvm(macOS/Linux)、nvm-windows 或 fnm。以 nvm 为例,安装后在项目根目录放置 .nvmrc:
# .nvmrc
20.11.1
# 使用
nvm install
nvm use
node -v # v20.11.1
选择版本时优先考虑 LTS(长期支持)版本,例如 20.x 或 22.x。示例中选用 20.11.1 作为教学版本,实际项目应结合依赖库的 engines 字段与团队约定确定。除 .nvmrc 外,还应在 package.json 中声明引擎约束,让不满足条件的运行时尽早报错:
{
"engines": {
"node": ">=20.11.0 <21",
"pnpm": ">=9.0.0"
}
}
判断是否生效:执行 node -v 应输出与 .nvmrc 一致的主版本;若使用 pnpm,再执行 pnpm -v 确认包管理器版本。两者都对齐,运行时层才算稳定。
二、npm 与 pnpm 工作区:依赖如何被解析
npm 是 Node.js 自带包管理器,pnpm 则以内容寻址存储和硬链接著称,安装更快、磁盘占用更小,并且对多包工作区支持更严格。微服务项目常见形态是 monorepo:一个仓库内包含多个服务包和共享库。此时用 pnpm workspace 管理最合适。
在仓库根目录创建 pnpm-workspace.yaml:
packages:
- "services/*"
- "packages/*"
根 package.json 中声明私有并固定包管理器:
{
"name": "microservices-root",
"private": true,
"packageManager": "pnpm@9.1.0",
"scripts": {
"dev:user": "pnpm --filter @ms/user-service dev",
"build": "pnpm -r build"
}
}
各服务通过 workspace:* 引用共享包,例如用户服务依赖共享日志库:
{
"name": "@ms/user-service",
"dependencies": {
"@ms/shared-logger": "workspace:*",
"fastify": "^4.26.0"
}
}
执行 pnpm install 后,pnpm 会在根 node_modules/.pnpm 建立内容寻址存储,并在各包的 node_modules 中创建符号链接。判断工作区是否生效:运行 pnpm -r list --depth -1 应列出所有子包;运行 pnpm --filter @ms/user-service exec node -e "console.log(require.resolve('@ms/shared-logger'))" 应解析到工作区内的共享包路径,而不是远程下载的副本。
三、Docker 与 Docker Compose 安装
微服务最终要跑在容器里,本机需要 Docker Engine 与 Compose 插件。Linux 上可用官方脚本或发行版仓库安装;macOS/Windows 推荐 Docker Desktop,它已内置 Compose v2。安装后验证:
docker --version
docker compose version
docker run --rm hello-world
三条命令分别确认引擎、Compose 插件和镜像拉取运行链路正常。若 docker compose 报“不是命令”,说明只装了旧版 docker-compose,应升级到 Compose v2。安装完成后,把当前用户加入 docker 组可免 sudo(Linux):sudo usermod -aG docker $USER,重新登录后生效。
四、项目目录结构初始化
一个可直接起步的 monorepo 目录如下:
microservices/
├── pnpm-workspace.yaml
├── package.json
├── .nvmrc
├── docker-compose.yml
├── services/
│ ├── user-service/
│ │ ├── package.json
│ │ └── src/index.js
│ └── order-service/
│ ├── package.json
│ └── src/index.js
└── packages/
└── shared-logger/
├── package.json
└── src/index.js
初始化顺序建议:先建根目录与 .nvmrc,再写 pnpm-workspace.yaml 与根 package.json,然后创建 services/ 与 packages/ 下的子包,最后补 docker-compose.yml。Compose 文件先定义基础设施,例如 PostgreSQL 与 Redis,服务容器可后续再接入:
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: ms
POSTGRES_PASSWORD: ms_dev_pwd
POSTGRES_DB: ms_dev
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
执行 docker compose up -d 后,用 docker compose ps 查看容器状态,两个服务应为 running;再用 docker compose logs postgres 确认数据库已就绪。至此,运行时、包管理、容器编排、目录骨架四层就位,后续服务代码可以直接挂载进这套环境。
.nvmrc、packageManager、docker-compose.yml 一并提交到仓库,是保证环境可复现的最低成本做法。node_modules、.pnpm-store 或本地数据库数据目录提交到 Git;它们应由 pnpm install 与 docker compose up 重新生成。若 pnpm install 后子包仍解析到远程包,检查 pnpm-workspace.yaml 的 glob 是否覆盖了该子包路径。