diff --git a/backend/include/bs-utility.h b/backend/include/bs-utility.h index e15a385..6cb9311 100644 --- a/backend/include/bs-utility.h +++ b/backend/include/bs-utility.h @@ -96,7 +96,8 @@ class SessionClass { }; namespace BookStore_ZYM { extern std::mutex debug_Print_Mutex; -} +extern bool shut_down; +} // namespace BookStore_ZYM void debugPrint(); template void debugPrint(Args... args) { diff --git a/backend/include/engine.h b/backend/include/engine.h index a40afdc..20d0072 100644 --- a/backend/include/engine.h +++ b/backend/include/engine.h @@ -1,11 +1,14 @@ #ifndef PROTECTOR_ENGINE_H #define PROTECTOR_ENGINE_H +#include #include +#include class BookStoreEngineClass { std::string config_dir; public: BookStoreEngineClass() = delete; BookStoreEngineClass(std::string config_dir) : config_dir(config_dir) {} + std::vector Execute(const std::string &cmd, std::stack &login_stack); }; #endif // PROTECTOR_ENGINE_H \ No newline at end of file diff --git a/backend/include/schedule.h b/backend/include/schedule.h deleted file mode 100644 index f2f9e5e..0000000 --- a/backend/include/schedule.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef PROTECTOR_SCHEDULE_H -#define PROTECTOR_SCHEDULE_H -#include -#include -#include - -#include "bs-utility.h" -#include "engine.h" -class BookStoreBackEndClass { - std::string config_dir; - BlockingStringStream *input_ptr; - BlockingStringStream *output_ptr; - BookStoreEngineClass *engine_ptr; - std::unordered_map session_map; - std::unordered_map> worker_theads_queue; - - public: - BookStoreBackEndClass() = delete; - BookStoreBackEndClass(std::string config_dir, BlockingStringStream *input_ptr, - BlockingStringStream *output_ptr) - : config_dir(config_dir), input_ptr(input_ptr), output_ptr(output_ptr) { - engine_ptr = new BookStoreEngineClass(config_dir); - } - ~BookStoreBackEndClass() { delete engine_ptr; } - void Run(); - void PostRequest(std::string SessionToken, std::string OperationToken, - std::string AuthenticationKey, std::string cmd); -}; -#endif // PROTECTOR_SCHEDULE_H \ No newline at end of file diff --git a/backend/src/bs-utility.cpp b/backend/src/bs-utility.cpp index c56564f..14d8d95 100644 --- a/backend/src/bs-utility.cpp +++ b/backend/src/bs-utility.cpp @@ -45,7 +45,8 @@ void ReadWriteLock::endWrite() { } namespace BookStore_ZYM { std::mutex debug_Print_Mutex; -} +bool shut_down = false; +} // namespace BookStore_ZYM void debugPrint() { BookStore_ZYM::debug_Print_Mutex.lock(); std::cerr << std::endl; @@ -66,7 +67,7 @@ void BlockingStringStream::unreadlock() { void Respond(BlockingStringStream *output_ptr, std::string SessionToken, std::string OperationToken, std::string AuthenticationKey, - const std::vector & ret) { + const std::vector &ret) { static std::mutex output_mutex; output_mutex.lock(); (*output_ptr).readlock(); diff --git a/backend/src/builtin-cli.cpp b/backend/src/builtin-cli.cpp index d3161ac..bbbdc5c 100644 --- a/backend/src/builtin-cli.cpp +++ b/backend/src/builtin-cli.cpp @@ -2,93 +2,25 @@ #include #include +#include +#include #include "bs-utility.h" -#include "schedule.h" +#include "engine.h" void BookStoreMain(bool is_server, std::string config_dir) { + BookStoreEngineClass engine(config_dir); + std::ios::sync_with_stdio(false); if (!is_server) { - int cnt = 0; - BlockingStringStream input; - BlockingStringStream output; - BookStoreBackEndClass backend(config_dir, &input, &output); - std::thread backend_thread([&backend]() { backend.Run(); }); - input.readlock(); - input << "#OpenSession INNERCLI\n"; - input.unreadlock(); - std::string SessionToken, AuthenticationKey, tmp; - output.getline(tmp); - output >> SessionToken >> AuthenticationKey; - // debugPrint("SessionToken=", SessionToken, - // " AuthenticationKey=", AuthenticationKey); + std::stack login_stack; std::string cmd; - output.getline(tmp); - while (getline(std::cin, cmd)) { - if (cmd == "quit" || cmd == "exit") { - input.readlock(); - input << "#CloseSession " << SessionToken << ' ' << AuthenticationKey - << '\n'; - input << "#ShutDownSystem\n"; - input.unreadlock(); - backend_thread.join(); - return; + while (std::getline(std::cin, cmd)) { + auto result = std::move(engine.Execute(cmd, login_stack)); + for (auto &line : result) { + std::cout << line << std::endl; } - input.readlock(); - input << "#Request " << SessionToken << " I-T-D" << ++cnt << " " - << AuthenticationKey << ' ' << cmd << '\n'; - // assert(input.internalStream.peek() != EOF); - input.unreadlock(); - // assert(input.is_writing == false); - // debugPrint("Sent Request ", cnt, " cmd=", cmd); - std::string SessionToken; - std::string OperationToken; - int LineCounter; - output >> SessionToken >> OperationToken >> LineCounter; - // debugPrint("Get the Head of response id=", OperationToken, - // " LineCounter=", LineCounter); - // debugPrint("Get SessionToken=", SessionToken, - // " OperationToken=", OperationToken, - // " LineCounter=", LineCounter); - output.getline(tmp); - for (int i = 0; i < LineCounter; i++) { - output.getline(tmp); - std::cout << tmp << std::endl; - // std::cerr << tmp << std::endl; - // debugPrint(tmp); - } - // std::cout.flush(); + if (BookStore_ZYM::shut_down) return; } - input.readlock(); - input << "#CloseSession " << SessionToken << ' ' << AuthenticationKey - << '\n'; - input << "#ShutDownSystem\n"; - input.unreadlock(); - backend_thread.join(); - return; } else { - std::ios::sync_with_stdio(false); - std::cin.tie(nullptr); - std::cout.rdbuf(nullptr); - BlockingStringStream input; - BlockingStringStream output; - BookStoreBackEndClass backend(config_dir, &input, &output); - std::thread backend_thread([&backend]() { backend.Run(); }); - std::thread input_thread([&input]() { - std::string data; - while (std::getline(std::cin, data)) { - input.readlock(); - input << data << '\n'; - input.unreadlock(); - } - }); - std::thread output_thread([&output]() { - std::string data; - while (true) { - output.getline(data, '\n'); - std::cout << data << std::endl; - } - }); - input_thread.join(); - output_thread.join(); - backend_thread.join(); + throw FatalError("Not implemented yet", 1); } } \ No newline at end of file diff --git a/backend/src/engine.cpp b/backend/src/engine.cpp index 11856cc..e61b48b 100644 --- a/backend/src/engine.cpp +++ b/backend/src/engine.cpp @@ -1 +1,14 @@ -#include "engine.h" \ No newline at end of file +#include "engine.h" + +#include +#include + +#include "bs-utility.h" +std::vector BookStoreEngineClass::Execute( + const std::string &cmd, std::stack &login_stack) { + if (cmd == "quit" || cmd == "exit") { + BookStore_ZYM::shut_down = true; + return std::vector(); + } + return std::vector({cmd}); +} \ No newline at end of file diff --git a/backend/src/schedule.cpp b/backend/src/schedule.cpp deleted file mode 100644 index d2cfe49..0000000 --- a/backend/src/schedule.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "schedule.h" - -#include -#include -#include -#include - -#include "engine.h" -void BookStoreBackEndClass::Run() { - std::string request_data; - const unsigned int RndSeed = std::random_device{}(); - std::mt19937 rnd(RndSeed); - while (true) { - input_ptr->getline(request_data, '\n'); - // debugPrint("Get_request_data=", request_data); - if (request_data[1] == 'O') // #OpenSession [TempChannelID] - { - std::stringstream ss; - ss << request_data; - ss >> request_data; - std::string TempChannelID; - ss >> TempChannelID; - SessionClass new_session; - std::string new_SessionToken; - std::string new_AuthenticationKey; - for (int i = 0; i < 16; i++) new_SessionToken.push_back(rnd() % 26 + 'A'); - for (int i = 0; i < 16; i++) - new_AuthenticationKey.push_back(rnd() % 26 + 'A'); - new_session.SessionToken = new_SessionToken; - new_session.OuthorizationKey = new_AuthenticationKey; - session_map[new_SessionToken] = new_session; - (*output_ptr).readlock(); - (*output_ptr) << TempChannelID << " IinitialOpt 1\n" - << new_SessionToken << ' ' << new_AuthenticationKey << '\n'; - (*output_ptr).unreadlock(); - } else if (request_data[1] == 'C') { - ; - } else if (request_data[1] == '_') { - std::stringstream ss(request_data); - std::string SessionToken; - std::string OperationToken; - std::string OuthenticationKey; - std::string cmd; - ss >> cmd >> SessionToken >> OperationToken >> OuthenticationKey; - (*output_ptr).readlock(); - (*output_ptr) << SessionToken << ' ' << OperationToken << " 1\n" - << "[Internal Error] This API shouldn't be called\n"; - (*output_ptr).unreadlock(); - } else if (request_data[1] == 'S') { - return; - } else if (request_data[1] == 'R') { - std::stringstream ss(request_data); - std::string SessionToken; - std::string OperationToken; - std::string OuthenticationKey; - std::string cmd; - ss >> cmd >> SessionToken >> OperationToken >> OuthenticationKey; - ss.get(); - std::getline(ss, cmd); - PostRequest(SessionToken, OperationToken, OuthenticationKey, cmd); - } - } -} - -void BookStoreBackEndClass::PostRequest(std::string SessionToken, - std::string OperationToken, - std::string AuthenticationKey, - std::string cmd) { - if (session_map[SessionToken].OuthorizationKey != AuthenticationKey) { - Respond(output_ptr, SessionToken, OperationToken, AuthenticationKey, - std::vector({"[Error] AuthenticationKey is wrong"})); - return; - } - Respond(output_ptr, SessionToken, OperationToken, AuthenticationKey, - std::vector({cmd})); -} \ No newline at end of file diff --git a/docs/develop/总体设计文档.md b/docs/develop/总体设计文档.md index ceab551..86b49c0 100644 --- a/docs/develop/总体设计文档.md +++ b/docs/develop/总体设计文档.md @@ -1,6 +1,6 @@ 计划实现的bonus: - 缓存 -- 并行 +- 并行(数据库保证并发安全,但由于锁和条件变量的过高开销,不打算使用;后端不支持操作并发,逻辑并发由服务端维护) - GUI前端和完整部署方案 有时间打算实现的bonus(按优先级次序排序): @@ -47,17 +47,10 @@ 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`流始终由信模块控制。调度模块负责会话管理、鉴权,保证引擎只接受形式合法的简单内部数据。 +具体执行业务,一次请求对应且只对应一次引擎模块API调用,支持并行(API阻塞但可同时运行)。当API入口函数被调用后,执行相关具体操作(不包括会话管理,但包括鉴权、登录栈维护),然后返回响应。 ### 内置交互模块 -当处于server模式下,不负责会话管理,直接把`std::cin`和`std::cout`的内容转发给通信与调度模块;处于interactive模式下时,提供用户侧会话管理,然后再调用通信与调度模块。 +当处于server模式下,参与会话管理和分发,然后调用引擎;处于interactive模式下时,简单封装后直接调用引擎。 标准输入输出始终由内置交互模块控制。 ## 前端 @@ -73,13 +66,17 @@ memoryriver类维护一个缓存,简单地缓存高频访问和连续访问; 实际上是一个页面,不同模式下动态绘制。 # 用户交互设计 -## 内部命令 +## server模式下的前后端交互格式 +### 前端向后端 - `#OpenSession [TempChannelID]`:向调度模块申请一个新会话 - `#CloseSession [SessionToken] [OuthenticationKey]`:显示地告知调度模块停止某个会话 - `#_Request [SessionToken] [OperationToken] [OuthenticationKey]`:向服务端重新请求调取某次操作响应的缓存 - `#Request [SessionToken] [OperationToken] [OuthenticationKey] [UserCommand]`:向后端发送一个请求 - `#ShutDownSystem`:关闭整个系统 +### 后端向前端 +- 字符串,`[SessionToken] [OperationToken] [LineCounter]\n[ResponseContent]`,其中,`[ResponseContent]`恰有`[LineCounter]`行,每行行末有且仅有一个`\n`。输出为空通过把`[LineCounter]`设置为0来实现。 + ## 向用户提供的命令 - `quit`和`exit`:正常退出系统 - `su [UserID] ([Password])?`:登录 @@ -101,15 +98,13 @@ memoryriver类维护一个缓存,简单地缓存高频访问和连续访问; # 底层数据接口约定(类、结构体设计) ## 请求 - 引擎接受:内部数据,数据传进函数参数列表 -- 调度模块接受:内部命令 -- 内置交互模块(interactive模式)接受:字符串,`[UserCommand]` +- 内置交互模块接受:interactive模式下,接受一个字符串`[UserCommand]`;server模式下,接受前后端交互格式。 - 云命令行接受:字符串,`[UserCommand]` - WebUI:图形界面 ## 响应 注意,对于申请Session时,响应用`[TempChannelID]`替代`[SessionToken]`,仅用于保证信息投递正确,返回信息内容是`[SessionToken]`和`[AuthenticationKey]`。 - 引擎返回:`std::string` -- 调度模块返回:字符串,`[SessionToken] [OperationToken] [LineCounter]\n[ResponseContent]`,其中,`[ResponseContent]`恰有`[LineCounter]`行,每行行末有且仅有一个`\n`。输出为空通过把`[LineCounter]`设置为0来实现。 -- 内置交互模块(interactive模式)返回:字符串,`[ResponseContent]` -- 云命令行返回:字符串,`[ResponseContent]` +- 内置交互模块返回:interactive模式下返回(输出)一个字符串,`[ResponseContent]` +- 云命令行返回:字符串,`[ResponseContent]`;server模式下,返回前后端交互格式。 - WebUI:图形界面 \ No newline at end of file