Files
BH-Bookstore-2023/docs/develop/总体设计文档.md
2023-11-29 13:38:34 +00:00

115 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

计划实现的bonus
- 缓存
- 并行
- GUI前端和完整部署方案
有时间打算实现的bonus按优先级次序排序
1. UTF-8中文支持
2. Validator
不打算实现的bonus
- 快照
- 文件系统修复
系统结构图:![系统结构图加载中……](https://cloud.zymsite.ink/f/O8IQ/%E7%B3%BB%E7%BB%9F%E7%BB%93%E6%9E%84%E5%9B%BE.png)
# 功能设计(模块划分)、数据库设计
整个程序分为后端和前端两部分,后端即主体部分
## 后端
后端只有一个可执行文件,书店实例通过配置文件夹来决定(默认为当前目录下`.bookstore`文件夹亦可通过命令行参数指定。当书店系统在运行时有且仅有一个后端进程分为interactive模式(使用后端自带的交互,单会话模式)和server模式(通过WebUI交互)。
### 数据库模块
维护相关数据支持并行API阻塞但可同时运行
#### 磁盘IO的并发
- 机械硬盘下不应该并发,反复寻址会负优化
- 从安全性角度,没有把握不要同时写
- 进程内部使用同一个文件描述符时,可以并发地调用读取函数,但是会自动相互阻塞
- 当使用不同的文件描述符时,并发调用读取函数时不会自动相互阻塞,如果并发读取的区域重叠,会有安全性问题
![pic](https://cloud.zymsite.ink/f/8vCd/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE%202023-11-29%20102719.png)
一次IO的固定开销差不多相当于1e5条CPU指令或1e4次基本操作以及相当于读入一块100KB~1MB的连续空间可以考虑把分块链表的块级索引集中到开头的一个大块里。
~~因此我不太打算碰文件系统~~
#### 数据库设计方案
##### 需求
能够逻辑上并发地响应需求并且实际上尽可能并行。每秒可响应1e4次请求。
##### 数据结构
###### 底层实现
通过B+树或一个有序的块状链表实现一个键值数据库,相当于`std::multimap`,一个键值数据库实例拥有恰好一个文件
###### 逻辑实现
数据库的一条记录有主键应当是唯一的、副键和数据构成从主键到数据建立multimap从副键到主键建立multimap一个数据库实例拥有恰好两个文件一个数据库可以有多个Sheet用于逻辑上存储不同的表。
##### 文件访问与缓存
memoryriver类维护一个缓存简单地缓存高频访问和连续访问键值数据库根据逻辑缓存最近访问和高频访问数据库层不设缓存。
##### 并发安全
实际的文件操作只由一个线程负责万级的IOPS足够应付需求内存中的资源
### 引擎模块
具体执行业务一次请求对应且只对应一次引擎模块API调用支持并行API阻塞但可同时运行。当API入口函数被调用后执行相关具体操作包括会话管理然后返回响应。
### 调度模块
负责对外提供文本模式的交互,以及维护请求级的并行。从流`request`读取文本格式的命令并把内部数据格式的响应翻译成文本格式向引擎模块发送请求调用引擎模块对外提供的API并把响应返回到流`response`。支持条件允许时的并行:
- 输入子模块从`request`读取请求,打上时间戳,分独占式请求和可并行请求按时间戳顺序分批处理。处理一个请求时,启动一个工作线程调用翻译执行子模块。同一个会话的请求不可并行,把`std::thread`move进相应的该会话的工作线程句柄队列下一个join前一个。成批执行可并行请求时启动完当前批次所有工作线程后join每个session的最后一个工作线程结束之后就可以执行下一批了。为了在服务模式下处理时有时无的请求两个流在读取时是阻塞的自己封装一个`std::condition_variable`通讯),并且读一个处理一个,碰到“不属于自己批次”的东西才算一批结束。
- 翻译执行子模块(入口函数本身阻塞)join完同一个session的上一个请求后向引擎模块API发送请求得到响应后把响应数据传给输出子模块的调用接口(阻塞但可并行,处理部分同时运行,上锁后直接输出到`response`,输出本身不同时进行)。
注意,在`request`流和`response`流中,输入输出仅保证单个请求/响应是完整的,多个请求/响应之间是完全“混杂”的,通过`session token``operation token`(操作标识由用户侧会话管理器维护,~~反正`session token`已经划分频道了用户开F12瞎改后果自负~~)区分,通过`outhentication key`鉴权。`request`流和`response`流始终由信模块控制。注意,通讯模块不负责会话管理。
### 内置交互模块
当处于server模式下不负责会话管理直接把`std::cin``std::cout`的内容转发给通信与调度模块处于interactive模式下时提供用户侧会话管理然后再调用通信与调度模块。
标准输入输出始终由内置交互模块控制。
## 前端
~~不清楚有没有时间写~~。WebUI采用`Node.JS`+`Socket.IO`~~不打算弄得很好看,不打算支持响应式设计~~支持图形化操作面板和“云命令行”。和interactive模式一样单个会话的操作支持逻辑上并发上一操作未结束可以发出下一操作但后端实际上是串行的。对于通讯中断、偶发的服务器未响应只保证不会彻底崩掉不保证出问题的业务能恢复。会话管理方面只负责信息准确投送。WebUI套一层Electron生成全平台客户端。
~~不考虑卡死的情况,数据库卡死了也拿它没办法,引擎部分不死循环就不会卡死~~只支持重置session。
### 服务端
提供会话管理。虽然有log但后端的响应只会发送一次用于防止客户端掉线的缓存由服务端维护。
### 客户端
云命令行和图形化操作面板分两个页面通过超链接关联。历史记录、会话信息存储于IndexDB中。负责用户侧会话管理。
#### 云命令行
#### 图形化操作面板
实际上是一个页面,不同模式下动态绘制。
# 用户交互设计
## 内置命令
- `# OpenSession <TempChannelID>`:向引擎申请一个新会话
- `# CloseSession <SessionToken> <OuthenticationKey>`:显示地告知引擎停止某个会话
- `# ReRequest <SessionToken> <OperationToken> <OuthenticationKey>`:向服务端重新请求调取某次操作响应的缓存
- `# Request <SessionToken> <OuthenticationKey> # <UserCommand>`:向引擎发送一个请求
## 向用户提供的命令
- `SU <UserName> <Password>`:登录某个用户(登录栈+1
- `LOGOUT`:退出当前用户(登录栈-1
- `EXIT`:退出当前会话
- `$SHUTDOWN`:关闭整个系统(需要最高权限)
- `$REBOOT`:重启整个系统(需要最高权限)
- `REGISTER <UserName> <Password> <NickName>`:注册用户
- `ADDUSER <UserName> <Password> <NickName> [Power]`:添加用户(至少需要销售员权限),权限缺省为客户,且不可超过当前用户的权限
- `QUERY ( [-ISBN=<isbn>] | [-AuthorName=<author>] | [-BookName=<bookname>] | [-KeyWords=<keywordslists>] )`:查询图书
- `BUY <ISBN> [Number]`购买图书默认数量是1
- `IMPORT <ISBN> <Number> <Cost>`:进货(至少需要销售员权限)
- `ADDBOOK <ISBN> <BookName> <Author> <KeyWordList>`:添加图书信息(至少需要销售员权限)
- `MODIFY <ISBN> [ [-NewISBN=<newisbn>] | [-BookName=<newname>] | [-Author=<newauthor>] | [-KeyWordList=<newkeylists>] ]`:修改图书信息(至少需要销售员权限)
- `$QUERYIMPORT [-from=<begintime>] [-till=<endtime>]`:查看采购情况(需要最高权限)
- `$QUERYSELL [-from=<begintime>] [-till=<endtime>]`:查看销售情况(需要最高权限)
- `$QUERYBENEFIT [-from=<begintime>] [-till=<endtime>]`:查看盈利情况(需要最高权限)
- `$QUERYOPT [-from=<begintime>] [-till=<endtime>] [-operator=<operatorname>]`:查看操作情况(需要最高权限)
- `$QUERYLOG [-from=<begintime>] [-till=<endtime>]`:查看日志情况(需要最高权限)
# 底层数据接口约定(类、结构体设计)
## 请求
- 引擎接受:内部数据`struct RequestType { std::string SessionToKen, OperationToken, AuthenticationKey; std::any content; };`
- 调度模块接受:`! <SessionToken> <OperationToken> <AuthenticationKey> ! <RequestContent>`
- 内置交互模块interactive模式接受字符串`<RequestContent>`
- 云命令行接受:字符串,`<RequestContent>`
- WebUI图形界面
## 响应
注意对于申请Session时响应用`<TempChannelID>`替代`<SessionToken`,返回信息内容是`SessionToken``AuthenticationKey`
- 引擎返回:内部数据,`struct ResponseType { int code; std::string SessionToken, OperationToken; std::any content; };`
- 调度模块返回:字符串,`! <SessionToken> <OperationToken> <Code> ! <ResponseContent>`
- 内置交互模块interactive模式返回字符串`<ResponseContent>`
- 云命令行返回:字符串,`<ResponseContent>`
- WebUI图形界面