手把手教你 用C++实现一个 可持久化 的http_server
前言
本文介紹一個有趣的 通過C++實現的 持久化的http_server demo,這樣我們通過http通信之后的數據可以持久化存儲,即使server掛了,數據也不會丟失。我們的http_sever 也就能夠真正得作為一個后端server了。
本身持久化這個能力是數據庫提供的,像通用的http交互數據都會通過SQL server或者MySQL這樣的存儲系統來存儲關系型數據,而這里我只是調度了一個單機存儲引擎作為持久化存儲。
主要用到的一些技術:
- mongoose C語言 網絡通信庫,在此庫基礎上實現了一個C++的httpserver
- Rocksdb 單機存儲引擎,作為持久化通信數據的存儲。
- 通過模版工廠來優雅得創建http url 和 對應其操作的實現。
代碼地址:PersistentHttpserver
實現過程
1. HTTPSERVER C++封裝
這里mongoose的通信庫基本都已經實現了http應用層及以下的通信接口的封裝,包括接受http協議的數據并解析或者封裝成http請求并發送,我這里需要做的僅僅是做一些接口調用使用C++實現就可以了。
基本接口如下:
class HttpServer {public:HttpServer() {}~HttpServer() {Close();if (kv_engine_) {delete kv_engine_;kv_engine_ = nullptr;}}void Init(const std::string &port); // Init some variablebool Start(); // Start a http server with a portbool Close();// Send a message as a http requeststatic void SendHttpRsp(mg_connection *connection, std::string rsp);static mg_serve_http_opts s_server_option;static KVEngine *kv_engine_; // For persistent the dataprivate:// Listen the event on the portstatic void OnHttpEvent(mg_connection *connection, int event_type,void *event_data);// Handle the http request with the definite url.static void HandleHttpEvent(mg_connection *connection,http_message *http_req);std::string m_port_;mg_mgr m_mgr_;
};
2. Rocksdb單機引擎使用
大家需要高級功能可以擴展,這里僅僅是使用了一些基本的接口調度起了rocksdb
#pragma once#include <iostream>
#include <string>#include "rocksdb/db.h"
#include "rocksdb/options.h"class KVEngine {public:KVEngine(std::string path) : path_(path) {}~KVEngine() {if (db_) {delete db_;db_ = nullptr;}}void Init() {opt_.create_if_missing = true;if (Open() != "ok") {std::cout << "Open db failed " << std::endl;}}std::string Open() {auto s = rocksdb::DB::Open(opt_, path_, &db_);if (!s.ok()) {return s.ToString();}return "ok";}std::string Get(const std::string& key) {if (nullptr == db_) {return "db_ is nullptr, please init it.";}std::string tmp_val;auto s = db_->Get(rocksdb::ReadOptions(), key, &tmp_val);if (!s.ok()) {return "not ok";}return tmp_val;}std::string Put(const std::string& key, const std::string& val) {if (nullptr == db_) {return "db_ is nullptr, please init it.";}auto s = db_->Put(rocksdb::WriteOptions(), key, val);if (!s.ok()) {std::cout << "Put failed " << s.ToString() << std::endl;return s.ToString();}return "ok";}private:std::string path_;rocksdb::DB* db_;rocksdb::Options opt_;
}
3. 模版工廠來創建 url 及其 handler
通過如下模版工廠,我們后續增加更多的URL的時候,不需要更改httpserver.cpp源碼 ,僅僅需要增加一個擴展類 及其 實現,并將這個映射添加到全局映射表中就可以了。
template <class OperationType_t>
class OperationRegister {
public:virtual OperationType_t* CreateOperation(const std::string& op_name, mg_connection* conn, http_message* hm) = 0;protected:OperationRegister() = default;virtual ~OperationRegister() = default;
};// Factory class template
template <class OperationType_t>
class OperationFactory {
public:// Single pattern of the factorystatic OperationFactory<OperationType_t>& Instance() {static OperationFactory<OperationType_t> instance;return instance;}void RegisterOperation(const std::string& op_name,mg_connection* conn, http_message* hm,OperationRegister<OperationType_t>* reg) {operationRegister[op_name] = reg;}OperationType_t* GetOperation(const std::string& op_name,mg_connection* conn, http_message* hm) {if (operationRegister.find(op_name) != operationRegister.end()) {return operationRegister[op_name]->CreateOperation(op_name, conn, hm);}return nullptr;}private:// We don't allow to constructor, copy constructor and align constructorOperationFactory() = default;~OperationFactory() = default;OperationFactory(const OperationFactory&) = delete;const OperationFactory& operator= (const OperationFactory&) = delete;std::map<std::string, OperationRegister<OperationType_t>* > operationRegister;
};// An template class to create the detail Operation
template <class OperationType_t, class OperationImpl_t>
class OperationImplRegister : public OperationRegister<OperationType_t> {
public:explicit OperationImplRegister(const std::string& op_name, mg_connection* conn, http_message* hm) {OperationFactory<OperationType_t>::Instance().RegisterOperation(op_name, conn, hm, this);}OperationType_t* CreateOperation(const std::string& op_name, mg_connection* conn, http_message* hm) {return new OperationImpl_t(op_name, conn, hm);}
};
后續僅僅需要將對應的URL 字符串 及其實現類添加到如下映射表中就可以了。
// Register all the http request's input string and their Class pointer.
void InitializeAllOp(mg_connection* conn, http_message* hm) {static bool initialize = false;if (!initialize) {static OperationImplRegister<Url, GetValue>getValue("/test/getvalue", conn, hm);static OperationImplRegister<Url, SetValueUrl>setValue("/test/setvalue", conn, hm);static OperationImplRegister<Url, RouteUrl>routeUrl("/", conn, hm);initialize = true;}
}
我們在實際HandleHttpEvent邏輯中就不需要做任何更改,十分友好得提升了代碼得可擴展性。
void HttpServer::HandleHttpEvent(mg_connection *connection, http_message *http_req) {std::string req_str = std::string(http_req->message.p, http_req->message.len);std::string url = std::string(http_req->uri.p, http_req->uri.len);InitializeAllOp(connection, http_req);// Register the operation for the urlauto *judge = new JudgeOperation(connection, http_req);auto res = judge->Judge(url);if (res != "ok") {SendHttpRsp(connection, res);}
}
關于模版工廠的細節可以參考:C++ 通過模版工廠實現 簡單反射機制
編譯及使用
1. 編譯
編譯之前需要確保測試環境已經成功安裝了rocksdb。
git clone https://github.com/BaronStack/PersistentHttpserver.git
cd PersistentHttpserver
make httpserver
rocksdb的on mac安裝:brew install rocksdb
rocksdb的on linux安裝:rocksdb-Install
2. 使用
-
第一個console :
./httpserver -
第二個console:
╰─$ curl -d "value=firstvalue" 127.0.0.1:7999/test/setvalue { "result": ok }設置了一個數值之后可以看到httpserver運行的目錄處 生成了一個db目錄:
db |-- 000010.sst |-- 000013.sst |-- 000016.sst |-- 000019.sst |-- 000025.sst |-- 000030.log |-- CURRENT |-- IDENTITY |-- LOCK |-- LOG |-- MANIFEST-000029 |-- OPTIONS-000029 `-- OPTIONS-000032停止第一個./httpserver 進程,重新運行,在第二個終端再此輸入獲取數據的請求命令
╰─$ curl -d "value=firstvalue" 127.0.0.1:7999/test/getvalue { "result": firstvalue }可以看到能夠獲取到重啟server之前的數據。
有了針對HTTP-SERVER的持久化能力和友好的可擴展性代碼,那我就可以持續玩一些有持久化能力的用戶需求了。
當然實際中的httpserver請求上層的封裝到底層的存儲服務都會復雜千萬倍,正真能夠支持分布式一致性服務的底層存儲 不論是newSQL還是NoSQL都會是非常復雜的實現過程。
總結
以上是生活随笔為你收集整理的手把手教你 用C++实现一个 可持久化 的http_server的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++ 通过模版工厂实现 简单反射机制
- 下一篇: Rocksdb iterator 的 F