Para as necessidades do projeto mais recente, o serviço de back-end do Node foi criado para registrar todo o processo. Não explicarei o uso de cada plug-in em detalhes aqui, mas apenas explicarei o processo. Para obter detalhes, você pode consultar o site oficial do ferramenta correspondente.
Ambiente do projeto:
- OS: m1、
- nó: 17,9
- Imagem Dcoekr: node:18-alpine
- Ambiente de implantação: centos
Primeiro, o projeto de inicialização
1. Novo projeto
express-demo
npm init
复制代码
2. Instale as dependências necessárias
yarn add typescript ts-node @types/node @types/express cross-env nodemon -D
yarn add express
复制代码
3. Configure tsconfig.json, configuração geral
{
"compilerOptions": {
"target": "es2017",
"module": "commonjs", //通过commonjs处理模块化
"rootDir": "./",
"outDir": "./dist",
"esModuleInterop": true,
"baseUrl": "src",
"strict": true,
"strictPropertyInitialization": false // 不用严格要求值的初始化
},
"exclude": ["node_modules"]
}
复制代码
4. Crie um novo diretório no projeto src
para armazenar os arquivos de origem. A estrutura de diretórios do projeto é a seguinte:
O diretório principal é:
controllers
Controlador, usado principalmente para lidar com a lógica da APImodels
modelo, modelo de tabela de banco de dadosservices
API para manipular o banco de dadosdatabases
Configuração e inicialização relacionadas ao banco de dados
Então, com base nisso, também temos alguns diretórios auxiliares
config
Usado para obter variáveis de ambiente passadas externamente ou parâmetros de banco de dados configuradosexceptions
Usado para definir a estrutura JSON retornada pela interfaceinterface
Usado para declarar tipos de variáveisroutes
Usado para expor a interface de API externautils
Diretório de arquivos como função de utilidadeapp.ts
Ele é usado para construir todo o aplicativo e centralizar todos os tipos de processamento que precisam ser processados com antecedênciaindex.ts
Usado como o arquivo de entrada para todo o projeto
Plugins usados pelo nó atual
- nodemon mantém atualizações quentes durante o desenvolvimento reiniciando automaticamente o nó quando detecta alterações de arquivo em um diretório
- cross-env é usado para definir variáveis de ambiente através da linha de comando, distinguir o ambiente de desenvolvimento do ambiente de produção
Arquivo de entrada index.ts
, usamos para introduzir rotas e iniciar serviços
import App from './app'
import monitorRouter from './routes/monitor.route'
const app = new App([new monitorRouter()])
app.listen()
复制代码
app.ts
, quando o aplicativo for instanciado, para se conectar ao banco de dados, inicializar roteamento, middleware, documentos, etc., defina o método aqui primeiro
class App {
app: express.Application
port: number = 3000
constructor(routers: Routes[]){
this.app = express();
this.connectToDatabase()
this.initializeMiddlewares()
this.initializeRoutes(routers)
this.initializeSwagger()
}
// 连接数据库
private connectToDatabase() {
DB.sequelize.sync({ force: false });
}
// 初始化中间件
private initializeMiddlewares() {
this.app.use(express.json());
this.app.use(express.urlencoded({ extended: true }));
}
// 初始化路由
private initializeRoutes(routes: Routes[]) {
routes.forEach(route => {
this.app.use('/', route.router);
});
}
// 初始化接口文档
private initializeSwagger() {
// 生成文档路由
this.app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));
}
// 启动服务
public listen(){
this.app.listen(this.port, () => {
console.log(`TypeScript with Express
http://localhost:${this.port}/`);
});
}
}
export default App
复制代码
2. Inicialize a rota
Classe de roteamento, para que a instanciação da rota seja introduzida em index.ts, todas as rotas tenham efeito, e então vemos que cada rota corresponde ao método do controlador, desde que esses métodos sejam implementados
import { Router } from 'express';
import MonitorsController from '../controllers/monitor.controller';
import { Routes } from '../interfaces/routes.interface';
class MonitorRoute implements Routes {
public path = '/v1/monitor';
public router = Router();
public monitorsController = new MonitorsController();
constructor() {
this.initializeRoutes();
}
private initializeRoutes() {
this.router.get(`${this.path}`, this.monitorsController.getMonitor);
this.router.post(`${this.path}`, this.monitorsController.createMonitor);
this.router.post(`${this.path}/:id`, this.monitorsController.getMonitorById);
}
}
export default MonitorRoute;
复制代码
3. Inicialize o banco de dados
yarn add sequelize mysql2
复制代码
Instale duas dependências, uma é o orm que opera o banco de dados e a outra é o driver do banco de dados.
在databases
中,我们初始化数据库
new Sequelize.Sequelize(database, user, password, {
dialect: 'mysql',
host: DB_HOST,
port: DB_PORT
} as any);
复制代码
四、配置区分生产环境和预发环境
在package.json
下新增scripts
命令,配置项目以不同的环境变量启动
"start": "cross-env NODE_ENV=development nodemon src/index.ts",
"start:prod": "cross-env NODE_ENV=pruduction nodemon src/index.ts",
复制代码
然后在项目根目录下新建.env.development.local
文件,配置变量
PORT = 3000
DB_HOST = localhost
DB_PORT = 3306
DB_USER = root
DB_PASSWORD = 12345678
DB_DATABASE = stark
复制代码
安装dotenv
可以读取各种环境变量
yarn add dotenv
复制代码
在config/index.ts
中,读取环境变量,在其他地方共享
import { config } from 'dotenv';
config({ path: `.env.${process.env.NODE_ENV || 'development'}.local` });
export { PORT } = process.env
复制代码
之后根据业务需要写controller和service就可以了
五、引入swagger
如图所示,可以自动生成API,这样就不用,每次单独写了 安装依赖
yarn add swagger-jsdoc swagger-ui-express
yarn add @types/swagger-jsdoc @types/swagger-ui-express -D
复制代码
配置swagger文档,读取对应的yaml文件,生成对应的路由,然后在项目初始化的时候执行该函数
private initializeSwagger() {
const options = {
swaggerDefinition: {
info: {
title: 'REST API',
version: '1.0.0',
description: 'Example docs',
},
},
apis: ['swagger*.yaml'],
};
const specs = swaggerJSDoc(options);
this.app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));
}
复制代码
yaml配置文件:配置文档路由和Modal(可以直接通过注释生成,需要自行查看文档)
tags:
- name: monitors
description: monitors
paths:
# [GET] monitors
/v1/monitor:
get:
tags:
- monitors
summary: Find All monitors
responses:
200:
description: "OK"
500:
description: "Server Error"
# definitions
definitions:
monitors:
type: object
required:
- msg
properties:
msg:
type: string
description: error message
column:
type: number
description: error column
复制代码
六、配置PM2启动服务
在部署项目之前,需要先编译成js,这里我们使用swc(通过 rust 实现的 babel:swc,一个将 ES6 转化为 ES5 的工具)当然也可以配置webpack啥的。
yarn add @swc/cli @swc/core -D
复制代码
在根目录下新建文件配置文件.swcrc
,这里附一份配置,具体的内容可以查看文档
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": false,
"dynamicImport": true,
"decorators": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true
},
"target": "es2017",
"externalHelpers": false,
"keepClassNames": true,
"loose": false,
"minify": {
"compress": false,
"mangle": false
},
"baseUrl": "src",
"paths": {
"@/*": ["*"]
}
},
"module": {
"type": "commonjs"
}
}
复制代码
在package.json
中配置scripts
命令
"build": "swc src -d dist --source-maps --copy-files",
复制代码
执行yarn build,就可以看到根目录下生成了dist目录,就是解析后的js文件,要部署的也是这个文件
接着我们使用pm2启动项目
yarn add global pm2
复制代码
因为pm2运行时肯定要区分生产环境和预发环境,所以我们需要给pm2增加配置文件.ecosystem.config.js
module.exports = {
apps: [
{
name: 'monitor', // pm2 start App name
script: 'dist/index.js',
autorestart: true, // auto restart if process crash
watch: false, // files change automatic restart
ignore_watch: ['node_modules', 'logs'], // ignore files change
max_memory_restart: '1G', // restart if process use more than 1G memory
merge_logs: true, // if true, stdout and stderr will be merged and sent to pm2 log
output: './logs/access.log', // pm2 log file
error: './logs/error.log', // pm2 error log file
env_test: {
PORT: 3000,
NODE_ENV: 'development',
DB_HOST: "localhost",
DB_PORT: 3306,
DB_USER: "root",
DB_PASSWORD: 12345678,
DB_DATABASE: "stark"
},
env_production: { // environment variable
PORT: 3000,
NODE_ENV: 'production',
DB_HOST: "localhost",
DB_PORT: 3306,
DB_USER: "root",
DB_PASSWORD: "12345",
DB_DATABASE: "monitor"
}
}
]
};
复制代码
执行命令pm2 start ecosystem.config.js --env test
到这里已经可以部署项目成功了,接着我们通过Docker部署一下
七、Docker构建镜像并部署
FROM node:18-alpine as common-build-stage
COPY . ./app
WORKDIR /app
RUN npm i -g pm2 --registry=https://registry.npm.taobao.org && yarn add production
EXPOSE 3000
FROM common-build-stage as production-build-stage
ENV NODE_ENV production
CMD ["pm2-runtime", "start", "ecosystem.config.js", "--env", "production"]
复制代码
这里使用pm2-runtime
,是因为如果pm2的话,Docker监听不到服务的运行,就会退出,所以这里pm2官方给出了pm2-runtime
来解决这个问题
docker build -t demo --platform linux/amd64 --target production-build-stage -f Dockerfile .
复制代码
我们要构建的镜像最终是要部署到centos上的,但是m1下打包的无法兼容,所以增加参数--platform linux/amd64
就可以了