xx il y a 2 ans
commit
0ac6c0fc99
6 fichiers modifiés avec 461 ajouts et 0 suppressions
  1. 33 0
      .gitignore
  2. 76 0
      CMakeLists.txt
  3. 21 0
      LICENSE
  4. 5 0
      README.md
  5. 220 0
      src/redis.cpp
  6. 106 0
      src/redis.h

+ 33 - 0
.gitignore

@@ -0,0 +1,33 @@
+# Prerequisites
+*.d
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+*.smod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+/build

+ 76 - 0
CMakeLists.txt

@@ -0,0 +1,76 @@
+cmake_minimum_required(VERSION 3.5)
+
+set(MODULE_NAME "redis")
+# 设置项目名为当前目录名
+project(${MODULE_NAME})
+
+# 搜索源文件和头文件
+file(GLOB_RECURSE SOURCE_FILES "${PROJECT_SOURCE_DIR}/src/*.cpp")
+file(GLOB_RECURSE HEADER_FILES 
+    "${PROJECT_SOURCE_DIR}/src/*.h"
+)
+
+# 将源文件分配到 Source Files 文件夹
+foreach(source IN LISTS SOURCE_FILES)
+    get_filename_component(source_path "${source}" PATH)
+    file(RELATIVE_PATH source_path_rel "${PROJECT_SOURCE_DIR}" "${source_path}")
+    string(REPLACE "/" "\\" source_path_rel_win "${source_path_rel}")
+    source_group("Source Files\\${source_path_rel_win}" FILES "${source}")
+endforeach()
+
+# 将头文件分配到 Header Files 文件夹
+foreach(header IN LISTS HEADER_FILES)
+    get_filename_component(header_path "${header}" PATH)
+    file(RELATIVE_PATH header_path_rel "${PROJECT_SOURCE_DIR}" "${header_path}")
+    string(REPLACE "/" "\\" header_path_rel_win "${header_path_rel}")
+    source_group("Header Files\\${header_path_rel_win}" FILES "${header}")
+endforeach()
+
+set(CMAKE_CXX_STANDARD 20) 
+set(CMAKE_CXX_STANDARD_REQUIRED TRUE)
+# 安装复制
+set(CMAKE_INSTALL_ALWAYS_COPY TRUE)
+
+set(YLIB ${CMAKE_INSTALL_PREFIX}/../ylib)
+set(FASTWEB ${CMAKE_INSTALL_PREFIX}/../fastweb)
+
+# 包含路径
+include_directories(
+	${YLIB}/include
+	${FASTWEB}/include
+	${FASTWEB}/include/lua
+	D:/3rdparty/hiredis
+	
+)
+
+# 添加共享库
+add_library(${MODULE_NAME} SHARED ${HEADER_FILES} ${SOURCE_FILES})
+
+if(MSVC)
+	target_link_libraries(${MODULE_NAME} PRIVATE
+			odbc32.lib
+			User32.lib
+			Advapi32.lib
+			IPHLPAPI.lib
+			WS2_32.lib
+			Shell32.lib
+			D:/3rdparty/hiredis/build/Release/hiredis.lib
+			${YLIB}/lib/libcrypto_static_win64.lib
+			$<$<CONFIG:Debug>:${FASTWEB}/bin/debug/3rdparty/lib/lua.lib>
+			$<$<CONFIG:Debug>:${YLIB}/lib/ylib_d.lib>
+
+			$<$<CONFIG:Release>:${FASTWEB}/bin/release/3rdparty/lib/lua.lib>
+			$<$<CONFIG:Release>:${YLIB}/lib/ylib.lib>
+	)
+else()
+	target_link_libraries(${MODULE_NAME} 
+			ylib
+			crypto
+			lua5.3
+			pthread
+	)
+
+endif()
+
+
+install(TARGETS ${MODULE_NAME} DESTINATION $<IF:$<CONFIG:Debug>,${FASTWEB}/bin/debug/module/${MODULE_NAME},${FASTWEB}/bin/release/module/${MODULE_NAME}>)

+ 21 - 0
LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 nianhua
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 5 - 0
README.md

@@ -0,0 +1,5 @@
+# module-mysql
+
+FastWeb - MYSQL模块
+
+文档:<a href="http://fw.newobj.org/doc/module/database/">点击访问</a>

+ 220 - 0
src/redis.cpp

@@ -0,0 +1,220 @@
+#include "redis.h"
+#include "dll_interface.h"
+extern "C" {
+#ifdef _WIN32
+    DLL_EXPORT
+#endif
+        int fastweb_module_regist(void* sol2, void* lua)
+    {
+        sol::state* state = static_cast<sol::state*>(sol2);
+        module::redis_regist(state);
+        return 0;
+    }
+}
+module::redis_pool::redis_pool()
+{
+
+}
+
+module::redis_pool::~redis_pool()
+{
+}
+
+void module::redis_pool::start(const std::string& address, ushort port, const std::string& password, int max_size)
+{
+    close();
+    
+    std::unique_lock<std::mutex> uni(m_mutex);
+
+    m_closed = false;
+    m_address = address;
+    m_port = port;
+    m_password = password;
+    m_max_size = max_size;
+}
+
+void module::redis_pool::close()
+{
+    std::unique_lock<std::mutex> uni(m_mutex);
+    redisContext* ctx = nullptr;
+    while (m_queue.pop(ctx))
+        redisFree(ctx);
+    m_pop_size = 0;
+    m_closed = true;
+}
+
+std::shared_ptr<module::redis> module::redis_pool::get()
+{
+    if (m_closed)
+    {
+        throw ylib::exception("connection pool is closed");
+    }
+    redisContext* ctx = nullptr;
+    if (m_queue.pop(ctx))
+    {
+        m_pop_size++;
+        return std::make_shared<module::redis>(ctx, this);
+    }
+    if (m_pop_size >= m_max_size)
+        throw ylib::exception("connection pool releases more connections than the maximum number");
+
+    ctx = reget(nullptr);
+    
+    m_pop_size++;
+    return std::make_shared<module::redis>(ctx, this);
+}
+
+int module::redis_pool::pop_size()
+{
+    return m_pop_size;
+}
+
+void module::redis_pool::regist_global(const char* name, sol::state* lua)
+{
+    lua->registry()[name] = this;
+    (*lua)[name] = this;
+}
+
+redisContext* module::redis_pool::reget(redisContext* ctx)
+{
+    if (ctx != nullptr)
+    {
+        redisFree(ctx);
+        ctx = nullptr;
+    }
+
+    redisContext* context = redisConnect(m_address.c_str(), m_port);
+    if (context == NULL || context->err) {
+        std::string exception;
+        if (context) {
+            exception = "connection error: " + std::string(context->errstr);
+            redisFree(context);
+        }
+        else
+            exception = "Connection error: can't allocate redis context";
+        throw ylib::exception(exception);
+    }
+
+    if (m_password != "")
+    {
+        try
+        {
+            reply(nullptr, context, (redisReply*)redisCommand(context, "AUTH %s", m_password.c_str()));
+        }
+        catch (const ylib::exception& e)
+        {
+            redisFree(context);
+            throw e;
+        }
+    }
+    return context;
+}
+
+sol::object module::redis_pool::reply(sol::this_state* ts,redisContext* ctx, redisReply* reply)
+{
+    if (reply == NULL) {
+        if (ctx->err)
+            throw ylib::exception("connection error: " + std::string(ctx->errstr));
+        else
+            throw ylib::exception("Unknown error: reply is NULL but context has no error\n");
+    }
+
+    sol::object result;
+    try
+    {
+        switch (reply->type) {
+        case REDIS_REPLY_ERROR:
+            throw ylib::exception("redis error: " + std::string(reply->str));
+            break;
+        case REDIS_REPLY_STATUS:
+        case REDIS_REPLY_STRING:
+            if (ts != nullptr)
+                result = sol::make_object(*ts, reply->str);
+            break;
+        case REDIS_REPLY_INTEGER:
+            if (ts != nullptr)
+                result = sol::make_object(*ts, reply->integer);
+            break;
+        case REDIS_REPLY_NIL:
+            if (ts != nullptr)
+                result = sol::make_object(*ts, sol::nil);
+            break;
+        case REDIS_REPLY_ARRAY:
+            if (ts != nullptr)
+            {
+                sol::state_view lua(*ts);
+                sol::table table = lua.create_table();
+                for (size_t i = 0; i < reply->elements; i++) {
+                    table[i + 1] = this->reply(ts, ctx, reply->element[i]);
+                }
+                result = table;
+            }
+            break;
+        default:
+            throw ylib::exception("Unknown reply type: " + std::to_string(reply->type));
+        }
+    }
+    catch (const std::exception& e)
+    {
+        freeReplyObject(reply);
+        throw e;
+    }
+    freeReplyObject(reply);
+    return result;
+}
+
+void module::redis_pool::recover(redisContext* ctx)
+{
+    if (ctx == nullptr)
+        return;
+    if (m_closed)
+    {
+        redisFree(ctx);
+        return;
+    }
+    m_queue.push(ctx);
+    m_pop_size--;
+}
+
+module::redis::redis(redisContext* context, redis_pool* pool):m_context(context),m_pool(pool)
+{
+
+}
+
+module::redis::~redis()
+{
+    m_pool->recover(m_context);
+}
+
+sol::object module::redis::command(const std::string& cmd, sol::this_state ts)
+{
+    //return m_pool->reply(&ts,m_context,(redisReply*)redisCommand(m_context, cmd.c_str()));
+    auto reply = (redisReply*)redisCommand(m_context, cmd.c_str());
+    if (reply == NULL) {
+        if (m_context->err)
+        {
+            m_context = m_pool->reget(m_context);
+
+            reply = (redisReply*)redisCommand(m_context, cmd.c_str());
+        }
+        else
+            throw ylib::exception("Unknown error: reply is NULL but context has no error\n");
+    }
+    return m_pool->reply(&ts,m_context,reply);
+
+
+}
+
+void module::redis_regist(sol::state* lua)
+{
+    lua->new_usertype<module::redis_pool>("redis_pool",
+        "close", &module::redis_pool::close,
+        "start", &module::redis_pool::start,
+        "get", &module::redis_pool::get,
+        "pop_size", &module::redis_pool::pop_size,
+        "self", &module::redis_pool::self
+    );
+    lua->new_usertype<module::redis>("redis_conn",
+        "command", &module::redis::command
+    );
+}

+ 106 - 0
src/redis.h

@@ -0,0 +1,106 @@
+#pragma once
+#include "base/define.h"
+#include "util/queue.hpp"
+#include "sol/sol.hpp"
+#include "hiredis.h"
+#include "basemodule.h"
+namespace module
+{
+	void redis_regist(sol::state* lua);
+	class redis;
+	/// <summary>
+	/// 连接池
+	/// </summary>
+	class redis_pool:public module::base
+	{
+	public:
+		redis_pool();
+		~redis_pool();
+		/// <summary>
+		/// 创建
+		/// </summary>
+		/// <param name="address"></param>
+		/// <param name="port"></param>
+		/// <param name="password"></param>
+		/// <param name="max_size"></param>
+		void start(const std::string& address,ushort port,const std::string& password,int max_size);
+		/// <summary>
+		/// 关闭
+		/// </summary>
+		void close();
+		/// <summary>
+		/// 获取连接
+		/// </summary>
+		/// <returns></returns>
+		std::shared_ptr<module::redis> get();
+		/// <summary>
+		/// 取弹出连接数
+		/// </summary>
+		/// <returns></returns>
+		int pop_size();
+
+		virtual void regist_global(const char* name, sol::state* lua) override;
+		virtual void delete_global() { delete this; }
+
+
+		friend class module::redis;
+	private:
+		/// <summary>
+		/// 重新获取
+		/// </summary>
+		/// <param name="ctx">归还CTX</param>
+		/// <returns></returns>
+		redisContext* reget(redisContext* ctx);
+		/// <summary>
+		/// 处理回复
+		/// </summary>
+		/// <param name="ct"></param>
+		/// <param name="reply"></param>
+		/// <returns></returns>
+		sol::object reply(sol::this_state* ts,redisContext* ct,redisReply* reply);
+		/// <summary>
+		/// 归还
+		/// </summary>
+		/// <param name="ctx"></param>
+		void recover(redisContext* ctx);
+	private:
+		// 池队列
+		ylib::queue<redisContext*> m_queue;
+		// 地址
+		std::string m_address;
+		// 端口
+		ushort m_port = 0;
+		// 密码
+		std::string m_password;
+		// 最大数量
+		int m_max_size = 0;
+		// 已弹出
+		int m_pop_size = 0;
+		// 锁
+		std::mutex m_mutex;
+		// 已关闭
+		bool m_closed = true;
+	};
+	/// <summary>
+	/// REDIS连接
+	/// </summary>
+	class redis{
+	public:
+		redis(redisContext* context,redis_pool* pool);
+		~redis();
+		/// <summary>
+		/// 执行命令
+		/// </summary>
+		/// <param name="format"></param>
+		/// <param name=""></param>
+		/// <returns></returns>
+		sol::object command(const std::string& cmd, sol::this_state ts);
+	private:
+		// 连接
+		redisContext* m_context = nullptr;
+		// 池
+		redis_pool* m_pool = nullptr;
+	};
+}
+
+