从开始到上线,你可能只需要花 10 分钟。千里之行,始于足下,一起来尝试下吧。
项目源码:chhpt/egg-app-demo
选型
第一步,我们要根据自己的需求选择合适的框架。目前流行的 Node 框架有:Express、Koa、Egg、Nest 等。因为是个人项目,我希望这个框架功能比较齐全,不用花费较多时间就能开始使用,有一定的定制能力,同时可以方便部署。综合以上的需求,我选择了 Egg。 Egg 是一个企业级框架,意味着它有比较丰富的能力,考虑了许多业务通用能力,如路由、日志、安全、部署等,同时底层基于 Koa,拥有比较丰富的社区生态,支持 TypeScript,也拥有不错的定制能力,比较好的满足了我的需求。
初始化项目
Egg 提供了脚手架快速初始化项目的能力,只需要几条命令即可快速开始一个项目,使用起来非常方便
mkdir example && cd example
npm init egg
npm i
Egg 创建的项目已经默认包含了 ESLint 规则,但是我个人更倾向于使用 eslint-config-alloy,并结合 Prettier 进行代码格式化。如果是多人协作的项目,建议使用 EditorConfig规范编辑器行为。
日志
日志对服务的重要性是毋庸置疑的,它对错误排查,监控服务运行状态都有非常的意义,我们可以根据自己的需求输出所需的日志信息。基于 Koa 的洋葱模型,日志通常作为一个中间件存在,通常我们需要监控整个服务运行的生命周期,所以日志中间件一般为第一个使用,下面是 Egg 日志中间件的一个实例:
const logger = () => {
return async (ctx: Context, next) => {
const start = Date.now()
await next()
const end = Date.now()
const url = `${ctx.request.method} => ${ctx.request.url}`
ctx.logger.info(`${url} Cost Time: ${end - start}ms.`)
}
}
异常处理
我们的应用在运行时可会出现一些异常情况,如错误的参数输入,代码逻辑错误等。这时如果直接抛出一个 500 服务不可用的错误,会很不友好,也无法根据异常信息定位错误。所以我们最好对一些常见的异常错误进行处理、封装,添加一些可读性更高的提示信息。 错误的处理可以在对应的逻辑处理函数中进行,也可以在中间件里捕获服务中的所有异常,统一处理,或者二者结合,达到比较理想的状态
export default () => {
return async (ctx: Context, next) => {
try {
await next()
} catch (e) {
ctx.logger.error(e)
ctx.resError(e.message, e.code)
}
}
}
数据库
通常后台服务都需要读写数据库,Egg 提供了应用自定义启动的能力,我们可以在应用启动时初始化数据库连接池,保证逻辑代码可以直接操作数据库。 mongodb.ts
import { MongoClient, Db } from 'mongodb'
const url = 'mongodb://127.0.0.1:3717'
const dbName = 'test'
const client = new MongoClient(url, { useUnifiedTopology: true })
export async function mongodbConnect(): Promise<Db> {
await client.connect()
const db = client.db(dbName)
return db
}
app.ts
import { IBoot, Application } from 'egg'
import { mongodbConnect } from './mongodb'
class AppBootHook implements IBoot {
app: Application
constructor(app: Application) {
this.app = app
}
async willReady() {
const { app } = this
const db = await mongodbConnect()
app.db = db
app.boxCollRef = db.collection('test')
this.app.logger.info('Mongodb is ready.')
}
}
module.exports = AppBootHook
应用部署
应用开发完成后就可以部署上线了,Node 应用无需编译,安装依赖即可运行,如果你使用了 TypeScript 编写代码,则需要先把 TypeScript 编译为 JavaScript 代码。
新浪云 SAE
新浪云 SAE(或 LeanCloud)提供了容器运行环境,可以直接部署 Node 应用,无需关注服务器运维问题,最适合服务快速上线。
由于容器特殊的环境,我们需要修改 Egg 的启动方式为单进程,并根据环境变量启动应用监听对应的端口,创建 index.ts
文件,写入如下内容,并修改 start
脚本为 node index.js
(直接运行编译后的文件)
import { startCluster } from 'egg'
const port = Number(process.env.PORT) || Number(process.env.LEANCLOUD_APP_PORT) || 5050
startCluster(
{
port,
workers: 1
},
() => {
console.log('App start successful.')
}
)
在环境创建完成后,可以在运行环境管理/代码管理中找部署代码的仓库地址:https://git.sinacloud.com/appname,然后我们就可以将 Egg 项目的代码推送到这个仓库中了。
推送之后,SAE 会自动安装依赖并运行,需要注意的是 SAE 只会安装 dependencies 中的模块,而不会安装 devDependencies 中的模块。
服务器直接运行
我们也可以在服务器直接运行 Node 应用,Egg 提供了内置模块来启动 Egg 应用,并保证应用进程有足够的稳定性,不会因为异常而停止服务。直接使用 npm start 命令即可启动应用
{
"scripts": {
"start": "egg-scripts start --daemon",
"stop": "egg-scripts stop"
}
}
当然,我们也可以使用 PM2 等进程守护工具运行 Node 应用。
容器
容器是现在部署应用的主流方式之一,我们可以使用下面的 dockerfile 构建 docker 镜像,传输到服务器上使用 docker 运行。
FROM node:12.15.0-alpine
RUN apk --update add tzdata
&& cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
&& echo "Asia/Shanghai" > /etc/timezone
&& apk del tzdata
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# add npm package
COPY package.json /usr/src/app/package.json
COPY yarn.lock /usr/src/app/yarn.lock
# RUN npm i --registry=https://registry.npm.taobao.org
RUN yarn --registry=https://registry.npm.taobao.org
# copy code
COPY . /usr/src/app
EXPOSE 5050
CMD npm start
构建镜像
docker build -t egg-app .
运行
docker run -d --name egg-project -p 9002:9002 egg-app
下一步
项目基本框架搭建完成后,就可以继续编写的你的项目逻辑了~