109 lines
6.8 KiB
Markdown
109 lines
6.8 KiB
Markdown
计划实现的bonus:
|
||
- 缓存
|
||
- 并行(数据库保证并发安全,但由于锁和条件变量的过高开销,不打算使用;后端不支持操作并发,逻辑并发由服务端维护)
|
||
- GUI前端和完整部署方案
|
||
|
||
有时间打算实现的bonus(按优先级次序排序):
|
||
1. UTF-8中文支持
|
||
2. Validator
|
||
|
||
不打算实现的bonus:
|
||
- 快照
|
||
- 文件系统修复
|
||
|
||
系统结构图:
|
||
|
||
# 功能设计(模块划分)、数据库设计
|
||
整个程序分为后端和前端两部分,后端即主体部分
|
||
## 后端
|
||
后端只有一个可执行文件,书店实例通过配置文件夹来决定(默认为当前目录下`.bookstore`文件夹,亦可通过命令行参数指定)。当书店系统在运行时,有且仅有一个后端进程,分为interactive模式(使用后端自带的交互,单会话模式)和server模式(通过WebUI交互)。
|
||
### 数据库模块
|
||
维护相关数据,支持并行(API阻塞但可同时运行)。
|
||
#### 磁盘IO的并发
|
||
- 机械硬盘下不应该并发,反复寻址会负优化
|
||
- 从安全性角度,没有把握不要同时写
|
||
- 进程内部使用同一个文件描述符时,可以并发地调用读取函数,但是会自动相互阻塞
|
||
- 当使用不同的文件描述符时,并发调用读取函数时不会自动相互阻塞,如果并发读取的区域重叠,会有安全性问题
|
||
|
||

|
||
如果是图中的数据,一次IO的固定开销差不多相当于1e5条CPU指令或1e4次基本操作。但是在OJ上的实测结果为 IOPS 约等于 4e5,写入速率 1GB/s, 读取速率 5GB/s
|
||
|
||
~~因此我不太打算碰文件系统~~
|
||
|
||
#### 数据库设计方案
|
||
##### 需求
|
||
能够逻辑上并发地响应需求,并且实际上尽可能并行。每秒可响应1e4次请求。
|
||
##### 数据结构
|
||
###### 底层实现
|
||
通过一个哈希表实现一个键值数据库,相当于`std::multimap`,一个键值数据库实例拥有恰好一个文件
|
||
###### 逻辑实现
|
||
数据库的一条记录有主键(应当是唯一的)、副键和数据构成,从主键到数据建立multimap,从副键到主键建立multimap,一个数据库实例拥有恰好两个文件,一个数据库可以有多个Sheet,用于逻辑上存储不同的表。
|
||
|
||
##### 文件访问与缓存
|
||
memoryriver类维护一个缓存,简单地缓存高频访问和连续访问;键值数据库缓存散列表。
|
||
|
||
##### 并发安全
|
||
实际的文件操作只由一个线程负责,万级的IOPS足够应付需求;内存中的资源,
|
||
|
||
### 引擎模块
|
||
具体执行业务,一次请求对应且只对应一次引擎模块API调用,支持并行(API阻塞但可同时运行)。当API入口函数被调用后,执行相关具体操作(不包括会话管理,但包括鉴权、登录栈维护),然后返回响应。
|
||
|
||
### 内置交互模块
|
||
当处于server模式下,参与会话管理和分发,然后调用引擎;处于interactive模式下时,简单封装后直接调用引擎。
|
||
标准输入输出始终由内置交互模块控制。
|
||
|
||
## 前端
|
||
~~不清楚有没有时间写~~。WebUI,采用`Node.JS`+`Socket.IO`,~~不打算弄得很好看,不打算支持响应式设计~~,支持图形化操作面板和“云命令行”。和interactive模式一样,单个会话的操作支持逻辑上并发(上一操作未结束可以发出下一操作)但后端实际上是串行的。对于通讯中断、偶发的服务器未响应,只保证不会彻底崩掉,不保证出问题的业务能恢复。会话管理方面,只负责信息准确投送。WebUI套一层Electron生成全平台客户端。
|
||
|
||
~~不考虑卡死的情况,数据库卡死了也拿它没办法,引擎部分不死循环就不会卡死~~,只支持重置session。
|
||
### 服务端
|
||
提供会话管理。虽然有log,但后端的响应只会发送一次,用于防止客户端掉线的缓存由服务端维护。
|
||
### 客户端
|
||
云命令行和图形化操作面板分两个页面,通过超链接关联。历史记录、会话信息存储于IndexDB中。负责用户侧会话管理。
|
||
#### 云命令行
|
||
#### 图形化操作面板
|
||
实际上是一个页面,不同模式下动态绘制。
|
||
|
||
# 用户交互设计
|
||
## server模式下的前后端交互格式
|
||
### 前端向后端
|
||
- `#OpenSession [TempChannelID]`:向调度模块申请一个新会话
|
||
- `#CloseSession [SessionToken] [OperationToken] [OuthenticationKey]`:显示地告知调度模块停止某个会话
|
||
- `#Request [SessionToken] [OperationToken] [OuthenticationKey]\n[UserCommand]`:向后端发送一个请求
|
||
- `#Who [SessionToken] [OperationToken] [OuthenticationKey]`:查询自己是谁和权限
|
||
- `#ShutDownSystem [SessionToken] [OperationToken] [OuthenticationKey]`:关闭整个系统
|
||
|
||
### 后端向前端
|
||
- 字符串,`[SessionToken] [OperationToken] [LineCounter]\n[ResponseContent]`,其中,`[ResponseContent]`恰有`[LineCounter]`行,每行行末有且仅有一个`\n`。输出为空通过把`[LineCounter]`设置为0来实现,若对话验证失败,`[LineCounter]`设为-1
|
||
|
||
## 向用户提供的命令
|
||
- `quit`和`exit`:正常退出系统
|
||
- `su [UserID] ([Password])?`:登录
|
||
- `logout`:退出上一个登录
|
||
- `register [UserID] [Password] [Username]`:注册用户
|
||
- `passwd [UserID] ([CurrentPassword])? [NewPassword]`:修改密码
|
||
- `useradd [UserID] [Password] [Privilege] [Username]`:店员修改密码
|
||
- `delete [UserID]`:删除用户
|
||
- `show (-ISBN=[ISBN] | -name="[BookName]" | -author="[Author]" | -keyword="[Keyword]")?`:查找图书
|
||
- `buy [ISBN] [Quantity]`:购买图书
|
||
- `select [ISBN]`:选中图书
|
||
- `modify (-ISBN=[ISBN] | -name="[BookName]" | -author="[Author]" | -keyword="[Keyword]" | -price=[Price])+`:修改图书信息
|
||
- `import [Quantity] [TotalCost]`:进货
|
||
- `show finance ([Count])?`:查询交易总账
|
||
- `report finance`:查询财务报表
|
||
- `report employee`:查询员工工作报告
|
||
- `log`:查询系统操作日志
|
||
|
||
# 底层数据接口约定(类、结构体设计)
|
||
## 请求
|
||
- 引擎接受:内部数据,数据传进函数参数列表
|
||
- 内置交互模块接受:interactive模式下,接受一个字符串`[UserCommand]`;server模式下,接受前后端交互格式。
|
||
- 云命令行接受:字符串,`[UserCommand]`
|
||
- WebUI:图形界面
|
||
|
||
## 响应
|
||
注意,对于申请Session时,响应用`[TempChannelID]`替代`[SessionToken]`,仅用于保证信息投递正确,返回信息内容是`[SessionToken]`和`[AuthenticationKey]`。
|
||
- 引擎返回:`std::string`
|
||
- 内置交互模块返回:interactive模式下返回(输出)一个字符串,`[ResponseContent]`
|
||
- 云命令行返回:字符串,`[ResponseContent]`;server模式下,返回前后端交互格式。
|
||
- WebUI:图形界面 |