阶段三:服务端
14 周,服务端技术选型
node.js 框架选型:Koa2
数据库:Mysql、MongoDB、Redis
登录校验:JWT
单元测试和接口测试:Jest
上线服务:PM2 + nginx
开发环境效率工具
框架对比
Koa2 更加简单,精简
数据库
- Mysql 和 Sequelize
- Mongodb 和 Mongoose
- Redis
Mysql 适合存储表格形式,格式规整的数据。MongoDB 用于存储文件,格式零散的数据。
数据结构设计
- JSON 数据存储作品内容,使用 MongoDB
- 用户信息,作品其它信息,表格形式存储,使用 MySQL
- 缓存能力,使用 Redis
登录校验
web 常用的登录鉴权方式:
- JWT
- Cookie 和 Session
- SSO 和 OAuth2
JWT
- JSON Web Token
校验过程:
- 客户端输入用户名和密码,传给服务端
- 服务端验证成功,返回一段将用户信息加密后的 token 字符串给客户端
- 客户端获得 token 之后,自己存储下来
- 接下来客户端所有接口请求,都在
header
中带上这段 token 作为 Authorization 字段 - 服务端接收到请求后,拿到 token 会解密,判断
优点:
- 无需在服务端存储用户信息:服务端不需要在存储用户信息,因为 JWT 中已经包含了所有必要的信息。
- 成本低、维护简单
- 不占用服务器内存
- 多进程、多服务器,不受影响
- 不受跨域限制
缺点:
- 无法主动退出:JWT 令牌一旦生成,其有效期内就可以一直使用,无法主动退出。
- 载荷大小有限制:由于 JWT 令牌需要进行传输和存储,其载荷大小有限制,不适用于存储大量数据的场景。
- 无法快速封禁某个登录的用户(极少有这种需求情况)
Cookie 和 Session
Cookie 校验过程:
- 客户端输入用户名和密码,传给服务端
- 服务端验证成功,会生成一个
Session ID
存储在服务端,并在响应头中添加Set-Cookie
同时返回给浏览器 - 接下来客户端所有接口请求,都自动带上
cookie
(浏览器的默认行为,Http 协议的规定) - 服务端通过解析 Cookie 中的
Session ID
,与服务器端存储的 Session 进行对比判断
Session:具体的用户信息存储在 session 中,可以理解为是一个 JSON 数据,放在服务端。cookie 只存储 userid。
优点:用户信息存储在服务端,可以快速封禁某个登录的用户,可以选择 Session
缺点:
- 占用服务端内存,有硬件成本。
- 多进程,多服务器时,不好同步,需要将 Session 放到第三方缓存服务中
- 跨域传递 cookie 时需要特殊配置
JWT 和 Session 的重要区别
- JWT 用户信息存储在客户端
- Session 用户信息存储在服务端
SSO 和 OAuth2
- SSO 企业集团统一登录,然后可以访问集团下面所有系统
- OAuth2 第三方鉴权登录,比如微信授权登录
短信验证码登录
短信验证码是一种登录的方式,无需注册,无需记住密码。但是要花钱,还要防止攻击,恶意刷短信接口
单元测试
抽离单元,主要用来测试逻辑
单元测试为何难以落实?
因为混淆了单元测试和集成测试,导致单元测试代码中有 Mock。需要服务器启动才能执行的代码,就不再是单元测试,而是集成测试。
单元测试是针对一个单元,即单一的功能。
单元测试是针对一段逻辑(比如判断和循环),平铺直叙的代码不用测试。
测试覆盖率,应该看抽离出来的单元模块,而非所有代码。有些代码不适合或不需要单元测试。
还有一个原因是研发流程不规范。
supertest 接口测试
保证安全感。
jest + supertest
单元测试和接口测试的概念需要区分开,不要彼此依赖。
PM2 + Nginx
线上服务关键:稳定 + 高效
PM2 服务只对内开启,nginx 服务作为内部和外部的代理
客户端接口统一先访问 Nginx 服务
日志
- 日志记录
- 日志拆分:PM2 日志拆分,nginx 日志拆分,框架日志拆分
开发环境效率工具
- eslint prettier
- pre-commit
- commit 规范
15 周,CI/CD 自动化
- 使用 GitHub actions
- 使用 Docker
- 使用 Docker-compose
- 自动发布到测试机
使用 GitHub actions
用途:
- master 分支,自动化测试
- dev 分支,自动部署到测试服务
- 提交 tag,自动上线,支持回滚
关于测试流程
pre-commit 时执行本地接口测试
npm run test:local
master push 时执行远程接口测试
npm run test:remote
使用 Docker
使用 Docker 构建 node.js 项目
通过Docker-compose
搭建开发环境
自动发布到测试机
dev 分支 push 时,自动部署到测试机
- github actions 监听 git 提交,并执行自定义命令
- docker 一键部署开发环境
配置测试服务器
创建 work 账号
用 root 登录,创建 work 账号
添加 work 的 sudo 权限
添加登录信任
安装必备软件:git、docker、docker-compose
开放端口
测试机开放需要的端口,让外网可以访问
- B 端 FE:
80
- B 端 server:
8081
- C 端:
8082
- 统计服务,收集日志:
8083
- 统计服务 OpenAPI:
8080
- admin FE:
8085
- admin server:
8084
线上环境不需要开发这么多端口,只需要使用 nginx 反向代理
梳理思路
使用 github actions 监听 dev 分支 push
登录测试机,获取最新 dev 分支代码
重新构建镜像
docker-compose build editor-server
重启所有容器
docker-compose up -d
第 16 周 编辑器服务端基础 API 开发
- 技术方案设计和基础功能开发
技术方案设计和基础功能开发
需求指导设计,设计指导开发,无设计不开发。
- 1.服务端技术方案设计的方法
- 2.B 端和编辑器基本功能 API
1.服务端技术方案设计的方法
- 1.1 接口设计
- 1.2 数据库设计
- 1.3 选择 Restful API 而非 GraphQL
1.1 接口设计
在需求评审的时候,需要思考要用到哪些接口,接口有哪些输入和输出,接口是否合理,接口是否能够实现。
需要具体的设计出每个接口的输入与输出。
可以使用工具做设计,比如 postman
B 端功能拆分
- 用户信息
- 模板管理
- 我的作品管理
- 编辑器
- 渠道
- 工具类
用户信息
- 获取手机短信验证码(思考输入什么,输出什么)
- 登录(包含注册)
- 获取用户信息
- 修改用户信息
模板管理:
- 首页推荐模板列表,分页,搜索
- 获取单个模板信息
- 我的模板列表,分页,搜索
作品管理:
- 创建空白作品
- 复制作品
- 删除作品
- 恢复作品
- 转赠作品
- 我的作品列表,分页,搜索
- 我的回收站列表,分页,搜索
编辑器:
- 查询单个作品信息
- 保存作品
- 预览作品
- 发布为作品
- 发布为模板
渠道:
创建渠道
删除渠道
修改渠道名称
获取单个作品的所有渠道
工具类:
上传图片
统一的输出格式
{
"errno": 0,
"data": {},
"message": ""
}
2
3
4
5
1.2 数据库设计
需要存储的数据:
- 用户
- 作品/模板
- 渠道
数据之间的关系:
一个用户可以创建很多个作品(1 对多关系),一个作品可以创建很多个渠道(1 对多关系),一个作品只对应一个内容(1 对 1 关系)。
数据表设计
用户:
列 | 类型 | 注释 |
---|---|---|
username | varchar | 用户名 |
password | varchar | 密码(保留字段,暂时用不到) |
phoneNumber | varchar | 手机号 |
nickName | varchar | 昵称 |
gender | int | 性别 |
picture | varchar | 用户头像 |
作品/模板:
同上,省略...
列 | 类型 | 注释 |
---|---|---|
uuid | varchar | h5 url 中使用,隐藏真正的 id,避免被爬 |
contentId | varchar | 未发布内容 id,内容存储在 MongoDB 中 |
publishContentId | varchar | 布内容 id,内容存储在 MongoDB 中,未发布的为空 |
isTemplate | boolean | 是否模板 |
status | int | 状态:0=删除,1=未发布,2=发布,3=强制线下 |
渠道:
同上,省略...
作品内容
- 未发布
- 已发布
MongoDB
{
// 页面的组件列表
components: [Object],
// 页面的属性
props: Object,
// 配置信息
setting: Object,
}
2
3
4
5
6
7
8
sequelize Model 以及关联关系
// 和 UserModel 建立关系
Work.belongsTo(UserModel, {
foreignKey: 'author',
targetKey: 'username', // 对应 UserModel.username 属性
});
2
3
4
5
mongoose Schema 和 Model
- 未发布的内容:workContent
- 已发布的内容:workPublishContent
B 端-server 整体架构图
完成上面步骤之后,再设计出整体架构图,结合接口设计,数据库设计,数据格式设计,将各个功能和模块抽象成一张图来表示。
结构是一层又一层,每一个模块使用不同颜色标记,越底层越抽象,底层的支撑上层的,最上层的是对外开放的路由。
biz-editor-server 端技术方案
评审技术方案设计前,最后整理出一份技术方案文档,让人能够对这个系统有一个初步的整体认识。
范围:
- B 端 + 编辑器 server
- 多项目关系图,当前的范围,与其它模块的关系
技术选型:
- 框架
- 数据库
- 登录校验
- 单元测试
- 发布上线
- CI/CD
整体架构设计:
- 整体架构图
接口设计:
- 范围、各个接口列表
- 接口输入和输入(只评审关键的、有疑问的接口)
数据库设计:
- 数据关系
- 数据表
基本功能开发
- 登录功能
- 用户信息接口
- 作品接口
- 模板接口
登录功能
短信功能,初次发短信,再次发短信。
考虑费用,禁止频繁发送,短信服务的报警,稳定性。
使用短信验证码直接登录,比用户名和密码登录更加简单方便,用户名和密码登录还需要配套功能,比如忘记密码、修改密码,这些功能做起来也很费力,而且也要发短信。