首次提交
This commit is contained in:
33
.gitignore
vendored
Normal file
33
.gitignore
vendored
Normal file
@@ -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
CMakeLists.txt
Normal file
76
CMakeLists.txt
Normal file
@@ -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
LICENSE
Normal file
21
LICENSE
Normal file
@@ -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
README.md
Normal file
5
README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# module-mysql
|
||||
|
||||
FastWeb - MYSQL模块
|
||||
|
||||
文档:<a href="http://fw.newobj.org/doc/module/database/">点击访问</a>
|
||||
220
src/redis.cpp
Normal file
220
src/redis.cpp
Normal file
@@ -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
src/redis.h
Normal file
106
src/redis.h
Normal file
@@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user