目录
3.编译mongo-c-driver (windows环境)
1.开发环境准备
需要搭建mongodb数据库,这里不赘述mongodb的搭建过程
需要下载mongo-c-driver库,可以从官网下载MongoDB C Driver — MongoDB C Driver 1.24.3
需要下载cmake 3.0以上版本
如果你是windows环境只需要VS2015以上版本,如果你是linux环境,准备个GCC编译器即可。
2.编译mongo-c-driver (linux环境)
一般情况下,我们的mongo-c-driver需要依赖openssl来支持mongo的用户名密码登录或者其他方式的密钥登录,因此我们需要编译下openssl库,实际上openssl的编译也很方便,这里我仍然使用官网的openssl https://www.openssl.org/source/ 你可以下载最新版本,
下载到openssl-3.0.10.tar.gz ,解压,然后./Configure && make -j16 && make install 即可。
接下来进入到mongo-c-driver路径下,可以看到下面目录结构:
编辑
cd build路径下执行cmake ..
编辑 make install
编辑
然后我们可以看下libmongoc.so依赖的openssl相关库的路径刚好是我们刚才make install的ssl库路径。
编辑
3.编译mongo-c-driver (windows环境)
windows环境下也是先安装openssl,我们方便起见,直接安装openssl的开发库和头文件即可,从Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions 或
OpenSSL 3.1, 3.0 and 1.1 Binaries for Microsoft Windows 任意一个网站可下载
编辑
安装好之后,能看到下面的头文件和动静态库路径:
编辑
然后进入到mongo-c-driver路径下,新建一个win32的工程文件夹
编辑
进入到win32目录下,打开cmd终端,输入 cmake ..
编辑
然后你就可以在win32目录下看到工程文件
编辑
用vs打开后,你主要编译这几个项目即可:
编辑
编辑
注意修改bson动态库的库名和输出路径
编辑
以及运行库类型:
编辑
同样的静态库的路径和动态库的库路径一样,都输出到lib文件夹下
编辑
同理,libmongoc库也这样生成。
最终生成对应的动态库和静态库:
编辑
当然你想省事了, 直接编译运行INSTALL工程
编辑
然后VS会执行INSTALL安装到默认的C盘库路径下如图:
编辑
4.CMake工程创建
新建一个工程mongo_wapper
编辑
配置CMakeLists.txt,设置当前工程为mongo-proxy,生成动态库(linux)或静态库(windows)的版本为0.0.1,库文件生成在当前工程目录下的lib文件夹里。
PROJECT(mongo-proxy) CMAKE_MINIMUM_REQUIRED(VERSION 2.6) SET(MONGOPROXY_LIB mongo-proxy) SET(CMAKE_AR ar) SET(LIB_VERSION 0.0.1) IF(WIN32) MESSAGE(STATUS "windows do not need install") ELSE(WIN32) SET(CMAKE_INSTALL_PREFIX /usr/local/${MONGOPROXY_LIB}-${LIB_VERSION}) ENDIF(WIN32) SET(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib) SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) ADD_SUBDIRECTORY(src lib)
然后我们在src路径下来写我们的mongo代理接口的相关实现,那么我们仍然需要在src下面来配置下我们的库依赖和头文件依赖:
IF(WIN32) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/../../mongo-c-driver/include/libbson-1.0) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/../../mongo-c-driver/include/libmongoc-1.0) ADD_DEFINITIONS(-D_REENTRANT) LINK_LIBRARIES(mongoc-1.0 bson-1.0) LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/../../mongo-c-driver/lib/) ELSE(WIN32) INCLUDE_DIRECTORIES(/usr/local/include/libmongoc-1.0/) INCLUDE_DIRECTORIES(/usr/local/include/libbson-1.0/) ADD_DEFINITIONS(-g -o3 -W -Wall -std=gnu++11 -Wno-invalid-offsetof -fPIC -Wno-deprecated -Wno-deprecated-declarations -D_REENTRANT -lpthread) ENDIF(WIN32)
完整的CMakeLists.txt文件如下:
IF(WIN32) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/../../utils/trunk/src) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/../../mongo-c-driver/include/libbson-1.0) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/../../mongo-c-driver/include/libmongoc-1.0) INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/../../concurrentqueue/) ADD_DEFINITIONS(-D_REENTRANT) LINK_LIBRARIES(utils mongoc-1.0 bson-1.0) LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/../../utils/trunk/lib) LINK_DIRECTORIES(${PROJECT_SOURCE_DIR}/../../mongo-c-driver/lib/) ELSE(WIN32) INCLUDE_DIRECTORIES(/usr/local/utils-0.0.1/include) INCLUDE_DIRECTORIES(/usr/local/include/libmongoc-1.0/) INCLUDE_DIRECTORIES(/usr/local/include/libbson-1.0/) ADD_DEFINITIONS(-g -o3 -W -Wall -std=gnu++11 -Wno-invalid-offsetof -fPIC -Wno-deprecated -Wno-deprecated-declarations -D_REENTRANT -lpthread) #LINK_LIBRARIES(utils mongoc bson rt) LINK_DIRECTORIES(/usr/local/utils-0.0.1/lib) ENDIF(WIN32) FILE(GLOB_RECURSE DIR_HEADERS "*.h") AUX_SOURCE_DIRECTORY(. DIR_SRCS) IF(WIN32) ADD_LIBRARY(mongo-proxy STATIC ${DIR_SRCS} ${DIR_HEADERS}) ELSE(WIN32) ADD_LIBRARY(mongo-proxy SHARED ${DIR_SRCS}) ENDIF(WIN32) SET_TARGET_PROPERTIES(mongo-proxy PROPERTIES VERSION 0.0.2) IF(WIN32) ELSE(WIN32) INSTALL(TARGETS mongo-proxy LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) INSTALL(DIRECTORY ./ DESTINATION ${CMAKE_INSTALL_PREFIX}/include PATTERN "*.cpp" EXCLUDE PATTERN ".svn" EXCLUDE PATTERN "gen" EXCLUDE PATTERN "*.txt" EXCLUDE) ENDIF(WIN32)
5.抽象接口设计
我的初衷是在Linux平台下只提供动态库和头文件,windows平台下提供静态库和头文件给开发者,这个库mongo-proxy对外提供了一些对mongodb的连接,增删改查,创建索引,聚合等操作的封装,开发者只需要关心接口如何调用,而不需要关心接口是如何实现的,也不需要关心mongo-c-driver的相关依赖,这里我抽象出mongo_proxy类,
mongo_proxy类
类图就不画了,直接上代码。
class mongo_proxy { public: mongo_proxy(){} virtual ~mongo_proxy() {} virtual bool mongodb_init(const char* url, int pool_size) =0; virtual bool mongodb_stop() = 0; virtual bool insert_coll(const char* databasename, const char* collname, const bson_doc& doc) = 0; virtual bool delete_coll(const char* databasename, const char* collname, const bson_doc& doc) = 0; virtual bool delete_many(const char* databasename, const char* collname, const bson_doc& filter) = 0; //其他接口...自己可以根据需要来实现 }
mongo_task类
一般情况下,我们的游戏服务器是数据库读写线程分离,读放在主线程里,写操作可以放到子线程来异步执行,这种情况下,我们可以把每一个mongodb的写操作或者读操作可以封装成mongo_task抽象类,它包含了所有的增删改查操作类型,并且提供一个处理相关操作的接口,而这个操作发生了什么事情, 交给其继承子类来实现即可。
typedef void mongo_proxy_t; class mongo_proxy; enum mongo_operator { mongo_op_insert = 0, mongo_op_del = 1, mongo_op_modify = 2, mongo_op_search = 3, }; class mongo_task { public: mongo_operator m_op; mongo_task(mongo_operator op) : m_op(op) {} virtual ~mongo_task() { } virtual void handle(mongo_proxy* proxy) = 0; };
因此当我有一个保存玩家数据的mongo操作,我可以这样来继承和实现:
class mongo_save_player : public mongo_task { public: BsonDoc m_query; BsonDoc m_update; mongo_save_player(mongo_operator op) : mongo_task(op) { } virtual ~mongo_save_player() { LOG(INFO)("~mongo_save_player"); } virtual void handle(mongo_proxy* proxy){ auto bret = proxy->upsert_coll("KOA", "player", m_query, m_update); if (!bret) { LOG(ERROR)("upsert failed:"); return; } } };
bson_doc类对bson对象的再次封装
原有的bson类的接口比较原始,不太方便为我们服务,因此我封装了一些常用的bson对象转json,查询追加字段,查询结果解析的方法。
class bson_doc { public: bson_doc(); bson_doc(const bson_doc& doc); bson_doc(void* doc); ~bson_doc(); bson_doc(const string& json); bson_doc(const char* i, ...); void* doc() const; void initid(); void setid(const string& id); static string getid(void* pdoc); string toJson(); void fromJson(const string& json); static string toJson(void* pdoc); static string cursorToJson(void* pcursor); static string& trim(string& str, const string& trimstr = "$"); //更新set void append_document(string feild, bson_doc& doc); void append_bin(string feild, const uint8_t* str, int len); void append_string(string feild, const string& str); void append_uint32(string feild, uint32_t t); void append_uint64(string feild, uint64_t t); void append_bool(string feild, bool t); //查找条件过滤 void append_filter_expess(string feild, string value); void append_filter_string(string feild, const string& str); void append_filter_uint32(string feild, uint32_t t); void append_filter_uint64(string feild, uint64_t t); void append_filter_bool(string feild, bool t); void append_filter_array(string feild, const bson_doc& reg); // 去掉所有 trimstr std::string get_oid() const; bool get_bool(const std::string& key) const; bool get_string(std::string& result, const std::string& path) const; std::string get_string(const std::string& path) const; int32_t get_int32(const std::string& path) const; int64_t get_int64(const std::string& path) const; double get_double(const std::string& path) const; int64_t get_datetime(const std::string& key) const; bson_doc get_document(const std::string& path) const; std::string get_binary(const std::string& key) const; std::vector<std::string> get_keys() const; std::vector<std::string> get_array(const std::string& path) const; std::vector<bson_doc> get_docs(const std::string& path) const; std::string getDateTimeStr(const std::string& key) const; std::string getDateStr(const std::string& key) const; std::string getDateStr(const std::string& format, const std::string& key) const; bool isDocument(const std::string& path) const; bool isString(const std::string& path) const; bool isDouble(const std::string& path) const; bool isInt64(const std::string& path) const; unsigned count() const; bool empty() const; bool has(const std::string& key) const; int get_type(const std::string& path) const; static bool isValid(const std::string& json); static bool removeDuplicates(std::vector<bson_doc>& vec, const std::string& key); bool clear(); bson_doc fromFile(const std::string& filename, const std::string& path); std::string toString() const; bson_doc& append(const std::string& key, const bson_doc& val); bson_doc& append(const std::string& key, const char* val); bson_doc& append(const std::string& key, const std::string& val); bson_doc& append(const std::string& key, const std::vector<bson_doc>& val); bson_doc& append(const std::string& key, const std::vector<std::string>& vector); bson_doc& append(const std::string& key, const std::vector<uint8_t>& binary); bson_doc& append(const std::string& key, int val); bson_doc& append(const std::string& key, unsigned val); bson_doc& append(const std::string& key, double val); bson_doc& append(const std::string& key, time_t time); bson_doc& append(const std::string& key, bool val); bson_doc& appendOid(); bson_doc& appendOid(const std::string& oid); bson_doc& appendRegex(const std::string& key, const std::string& regex, const std::string& options = ""); bson_doc& appendNowUtc(const std::string& key); bson_doc& operator=(const bson_doc& val); bson_doc(const std::string& key, bool val); bson_doc(const std::string& key, const bson_doc& value); bson_doc(const std::string& key, const char* value); bson_doc(const std::string& key, const std::string& value); bson_doc(const std::string& key, const std::vector<bson_doc>& val); bson_doc(const std::string& key, int val); bson_doc(const std::string& key, unsigned int val); private: void* m_doc; };
mongo_proxy可实例化对象的创建和释放接口
那么,还需要提供一个接口给调用库的人来创建mongo_proxy实例化对象:
mongo_proxy* mp_create_mongo_proxy(); void mp_delete_mongo_proxy(mongo_proxy* mp);
这几个都需要在对应的头文件里定义,而真正的实现则由抽象类的子类来实现,隐藏内部实现,也减少了其他开发者开发过程中对原始的mongo-c-driver的头文件依赖。
6.接口实现
mongo_proxy_imp
那么对于mongo_proxy的子类,我要去实现mongo_proxy的所有抽象接口,
编辑
class mongo_proxy_imp :public mongo_proxy { public: mongo_proxy_imp(); ~mongo_proxy_imp(); bool mongodb_init(const char* url, int pool_size = 5); bool mongodb_stop(); bool insert_coll(const char* databasename, const char* collname, const bson_doc& doc); bool delete_coll(const char* databasename, const char* collname, const bson_doc& doc); bool delete_many(const char* databasename, const char* collname, const bson_doc& filter) ; private: int m_ulthreads = 10; mongoc_client_pool_t* m_pool = nullptr; mongoc_uri_t* m_url = nullptr; }
由于篇幅过长,这里我只提供几个实现看下:
mongo_proxy_imp::mongo_proxy_imp() { mongoc_init(); } mongo_proxy_imp::~mongo_proxy_imp() { mongodb_stop(); } bool mongo_proxy_imp::mongodb_init(const char* url, int pool_size) { m_url = mongoc_uri_new(url); if (m_url == nullptr) { LOG(ERROR)("mongoc uri new failed url:%s", url); return false; } m_pool = mongoc_client_pool_new(m_url); if (m_pool == nullptr) { LOG(ERROR)("mongoc create pool failed url:%s", url); return false; } mongoc_client_pool_max_size(m_pool, pool_size); return true; } bool mongo_proxy_imp::mongodb_stop() { if (m_url == nullptr || m_pool == nullptr) { return false; } mongoc_client_pool_destroy(m_pool); m_pool = nullptr; mongoc_uri_destroy(m_url); m_url = nullptr; mongoc_cleanup(); return true; } bool mongo_proxy_imp::insert_coll(const char* databasename, const char* collname, const bson_doc& doc) { mongoc_client_pool_t* pool = static_cast<mongoc_client_pool_t*>(m_pool); mongoc_client_t* client; bson_error_t error; client = mongoc_client_pool_pop(pool); if (client == nullptr) { return false; } mongoc_collection_t* collection = mongoc_client_get_collection(client, databasename, collname); if (!mongoc_collection_insert_one(collection, static_cast<const bson_t*>(doc.doc()), NULL,NULL, &error)) { mongoc_collection_destroy(collection); mongoc_client_pool_push(pool, client); return false; } mongoc_collection_destroy(collection); mongoc_client_pool_push(pool, client); return true; }
mongo_proxy可实例化对象的创建和释放
而且刚才我提到还需要提供一个对外接口来创建mongo_proxy对象,因此我们可以在mongo_proxy子类的实现cpp里来实现如下方法:
mongo_proxy* mp_create_mongo_proxy() { return new mongo_proxy_imp(); } void mp_delete_mongo_proxy(mongo_proxy* mp) { delete mp; mp = NULL; }
bson_doc
#include <string.h> #include "mongo_proxy.h" #include <log.h> #include <bson.h> #include <mongoc.h> #include <ctime> #include <fstream> #include <functional> #include <iostream> #include <iterator> #include <vector> #define BSONDOC_APPEND(_bson, ...) BCON_APPEND(_bson,__VA_ARGS__) bson_doc::bson_doc() { m_doc = (void*)bson_new(); } bson_doc::~bson_doc() { if (m_doc != NULL) bson_destroy((bson_t*)m_doc); m_doc = NULL; } bson_doc::bson_doc(void* doc):m_doc(bson_copy((const bson_t*)doc)) //m_doc(bson_copy(&val)) { } bson_doc::bson_doc(const string& json) { m_doc = (bson_t*)bson_new(); bson_error_t error; bson_init_from_json((bson_t*)m_doc, json.c_str(), json.length(), &error); } void bson_doc::fromJson(const string& json) { bson_error_t error; bson_init_from_json((bson_t*)m_doc, json.c_str(), json.length(), &error); } void bson_doc::initid() { if (m_doc == NULL) return; bson_oid_t oid; bson_oid_init(&oid, NULL); BSON_APPEND_OID((bson_t*)m_doc, "_id", &oid); } void bson_doc::setid(const string& id) { if (m_doc == NULL) return; bson_oid_t oid; bson_oid_init_from_string(&oid, id.c_str()); BSON_APPEND_OID((bson_t*)m_doc, "_id", &oid); } string bson_doc::getid(void* pdoc) { string strid; if (pdoc == NULL) return strid; const bson_oid_t* ooid = NULL; BCON_EXTRACT((bson_t*)pdoc, "_id", BCONE_OID(ooid)); if (ooid != NULL) { char id[64] = { 0 }; bson_oid_to_string(ooid, id); strid += id; } return strid; } string bson_doc::toJson() { string strret; if (m_doc == NULL) return strret; char* str = bson_as_json((bson_t*)m_doc, NULL); if (str != NULL) { strret += str; //trim(strret); bson_free(str); } return strret; }
完整代码可以通过下面链接下载
在windows/linux下如何调用该动态库/静态库下的接口,操作常见的增删改操作,我将在下节继续分享给大家,谢谢关注