Files
BH-Bookstore-2023/docs/develop/总体设计文档.md
2023-12-12 07:01:21 +00:00

8.3 KiB
Raw Blame History

计划实现的bonus

  • 缓存
  • 并行
  • GUI前端和完整部署方案

有时间打算实现的bonus按优先级次序排序

  1. UTF-8中文支持
  2. Validator

不打算实现的bonus

  • 快照
  • 文件系统修复

系统结构图:系统结构图加载中……

功能设计(模块划分)、数据库设计

整个程序分为后端和前端两部分,后端即主体部分

后端

后端只有一个可执行文件,书店实例通过配置文件夹来决定(默认为当前目录下.bookstore文件夹亦可通过命令行参数指定。当书店系统在运行时有且仅有一个后端进程分为interactive模式(使用后端自带的交互,单会话模式)和server模式(通过WebUI交互)。

数据库模块

维护相关数据支持并行API阻塞但可同时运行

磁盘IO的并发

  • 机械硬盘下不应该并发,反复寻址会负优化
  • 从安全性角度,没有把握不要同时写
  • 进程内部使用同一个文件描述符时,可以并发地调用读取函数,但是会自动相互阻塞
  • 当使用不同的文件描述符时,并发调用读取函数时不会自动相互阻塞,如果并发读取的区域重叠,会有安全性问题

pic 如果是图中的数据一次IO的固定开销差不多相当于1e5条CPU指令或1e4次基本操作。但是在OJ上的实测结果为 IOPS 约等于 4e5写入速率 1GB/s 读取速率 5GB/s

因此我不太打算碰文件系统

数据库设计方案

需求

能够逻辑上并发地响应需求并且实际上尽可能并行。每秒可响应1e4次请求。

数据结构
底层实现

通过一个哈希表实现一个键值数据库,相当于std::multimap,一个键值数据库实例拥有恰好一个文件

逻辑实现

数据库的一条记录有主键应当是唯一的、副键和数据构成从主键到数据建立multimap从副键到主键建立multimap一个数据库实例拥有恰好两个文件一个数据库可以有多个Sheet用于逻辑上存储不同的表。

文件访问与缓存

memoryriver类维护一个缓存简单地缓存高频访问和连续访问键值数据库缓存散列表。

并发安全

实际的文件操作只由一个线程负责万级的IOPS足够应付需求内存中的资源

引擎模块

具体执行业务一次请求对应且只对应一次引擎模块API调用支持并行API阻塞但可同时运行。当API入口函数被调用后执行相关具体操作不包括会话管理、鉴权然后返回响应。

调度模块

负责对外提供文本模式的交互,以及维护请求级的并行。从流request读取文本格式的命令并把内部数据格式的响应翻译成文本格式向引擎模块发送请求调用引擎模块对外提供的API并把响应返回到流response。支持条件允许时的并行:

  • 输入子模块从request读取请求,打上时间戳,分独占式请求和可并行请求按时间戳顺序分批处理。处理一个请求时,启动一个工作线程调用翻译执行子模块。同一个会话的请求不可并行,把std::threadmove进相应的该会话的工作线程句柄队列下一个join前一个。成批执行可并行请求时启动完当前批次所有工作线程后join每个session的最后一个工作线程结束之后就可以执行下一批了。为了在服务模式下处理时有时无的请求两个流在读取时是阻塞的自己封装一个std::condition_variable通讯),并且读一个处理一个,碰到“不属于自己批次”的东西才算一批结束。
  • 翻译执行子模块(入口函数本身阻塞)join完同一个session的上一个请求后向引擎模块API发送请求得到响应后把响应数据传给输出子模块的调用接口(阻塞但可并行,处理部分同时运行,上锁后直接输出到response,输出本身不同时进行)。

注意,在request流和response流中,输入输出仅保证单个请求/响应是完整的,多个请求/响应之间是完全“混杂”的,通过session tokenoperation token(操作标识由用户侧会话管理器维护,反正session token已经划分频道了用户开F12瞎改后果自负)区分,通过outhentication key鉴权。request流和response流始终由信模块控制。调度模块负责会话管理、鉴权,保证引擎只接受形式合法的简单内部数据。

内置交互模块

当处于server模式下不负责会话管理直接把std::cinstd::cout的内容转发给通信与调度模块处于interactive模式下时提供用户侧会话管理然后再调用通信与调度模块。 标准输入输出始终由内置交互模块控制。

前端

不清楚有没有时间写。WebUI采用Node.JS+Socket.IO不打算弄得很好看,不打算支持响应式设计支持图形化操作面板和“云命令行”。和interactive模式一样单个会话的操作支持逻辑上并发上一操作未结束可以发出下一操作但后端实际上是串行的。对于通讯中断、偶发的服务器未响应只保证不会彻底崩掉不保证出问题的业务能恢复。会话管理方面只负责信息准确投送。WebUI套一层Electron生成全平台客户端。

不考虑卡死的情况,数据库卡死了也拿它没办法,引擎部分不死循环就不会卡死只支持重置session。

服务端

提供会话管理。虽然有log但后端的响应只会发送一次用于防止客户端掉线的缓存由服务端维护。

客户端

云命令行和图形化操作面板分两个页面通过超链接关联。历史记录、会话信息存储于IndexDB中。负责用户侧会话管理。

云命令行

图形化操作面板

实际上是一个页面,不同模式下动态绘制。

用户交互设计

内部命令

  • #OpenSession [TempChannelID]:向调度模块申请一个新会话
  • #CloseSession [SessionToken] [OuthenticationKey]:显示地告知调度模块停止某个会话
  • #_Request [SessionToken] [OperationToken] [OuthenticationKey]:向服务端重新请求调取某次操作响应的缓存
  • #Request [SessionToken] [OperationToken] [OuthenticationKey] [UserCommand]:向后端发送一个请求
  • #ShutDownSystem:关闭整个系统

向用户提供的命令

  • quitexit:正常退出系统
  • 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]
  • 云命令行接受:字符串,[UserCommand]
  • WebUI图形界面

响应

注意对于申请Session时响应用[TempChannelID]替代[SessionToken],仅用于保证信息投递正确,返回信息内容是[SessionToken][AuthenticationKey]

  • 引擎返回:std::string
  • 调度模块返回:字符串,[SessionToken] [OperationToken] [LineCounter]\n[ResponseContent],其中,[ResponseContent]恰有[LineCounter]行,每行行末有且仅有一个\n。输出为空通过把[LineCounter]设置为0来实现。
  • 内置交互模块interactive模式返回字符串[ResponseContent]
  • 云命令行返回:字符串,[ResponseContent]
  • WebUI图形界面