日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

手把手教你 用C++实现一个 可持久化 的http_server

發布時間:2023/11/27 生活经验 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 手把手教你 用C++实现一个 可持久化 的http_server 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

本文介紹一個有趣的 通過C++實現的 持久化的http_server demo,這樣我們通過http通信之后的數據可以持久化存儲,即使server掛了,數據也不會丟失。我們的http_sever 也就能夠真正得作為一個后端server了。

本身持久化這個能力是數據庫提供的,像通用的http交互數據都會通過SQL server或者MySQL這樣的存儲系統來存儲關系型數據,而這里我只是調度了一個單機存儲引擎作為持久化存儲。

主要用到的一些技術:

  1. mongoose C語言 網絡通信庫,在此庫基礎上實現了一個C++的httpserver
  2. Rocksdb 單機存儲引擎,作為持久化通信數據的存儲。
  3. 通過模版工廠來優雅得創建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的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。