hiredis异步接口封装并导出到Lua
hiredis异步接口封装并导出到Lua
(金庆的专栏 2017.1)
hiredis 不支持 Windows, Windows 下使用 wasppdotorg / hiredis-for-windows 。
Linux 下仍是 redis/hiredis。
hiredis-for-windows 是以 hiredis 0.13.3 为基础移植的。
hiredis-for-windows 需要稍加修正:
* 去除 inline 宏
* TCP_NODELAY 改在连接之前设置。
详见其Issue.
Cluster 支持采用 shinberg/cpp-hiredis-cluster。这是个CPP库,支持异步,
要求 hiredis >= 0.12.0。
jinq0123/cpp-hiredis-cluster 在 develop 分支上更改了接口,让它更好用。
因为网络库是boost asio, 所以需要asio适配器,采用 jinq0123/hiredis-boostasio-adapter。
cpp-hiredis-cluster 提供的是统一的Command接口,接收字符串命令,返回 redisReply.
对于常用命令,需要更简单的接口。
在Lua手游服务器代码中新建CRedis类,封装 cpp-hiredis-cluster,
为常用redis命令封装更好用的接口。
CRedis类封装了asio, 接口是根据应用需要定义的,所以是专用接口,
不在 cpp-hiredis-cluster 中实现。
bool CRedis::Init(io_service& rIos, const std::string& sHost, uint16_t uPort)
创建 RedisCluster 对象。
io_service 用于创建一个 redis 事件适配器,
RedisCluster创建需要一个适配器。
sHost, uPort 用于初始化连接redis cluster, 获取集群信息。
using Cmd = RedisCluster::AsyncHiredisCommand;
bool CRedis::Init(io_service& rIos, const std::string& sHost, uint16_t uPort)
{
m_pAdapter.reset(new Adapter(rIos));
try
{
m_pCluster.reset(Cmd::createCluster("127.0.0.1", 7000, *m_pAdapter));
}
catch (const RedisCluster::ClusterException &e)
{
LOG_ERROR("Cluster exception: " << e.what());
return false;
}
return true;
}
static Cmd::Action handleException(const RedisCluster::ClusterException &exception,
RedisCluster::HiredisProcess::processState state)
{
// Check the exception type.
// Retry in case of non-critical exceptions.
if (!dynamic_cast<const RedisCluster::CriticalException*>(&exception))
{
LOG_WARN("Exception in processing async redis callback: "
<< exception.what() << " Retry...");
// retry to send a command to redis node
return Cmd::RETRY;
}
LOG_ERROR("Critical exception in processing async redis callback: "
<< exception.what());
return Cmd::FINISH;
}
static void handleSetReply(const redisReply &reply, const CRedis::SetCb& setCb)
{
if (!setCb) return;
if (reply.type == REDIS_REPLY_STATUS)
{
const std::string OK("OK");
if (OK == reply.str)
{
setCb(true);
return;
}
}
LOG_WARN("Set reply: " << reply.str);
setCb(false);
}
void CRedis::Set(const string& sKey, const string& sValue, const SetCb& setCb)
{
assert(m_pCluster);
Cmd::commandf2(*m_pCluster, sKey,
[setCb](const redisReply& reply) {
handleSetReply(reply, setCb);
},
handleException,
"set %s %s", sKey.c_str(), sValue.c_str());
}
static void handleGetReply(const redisReply& reply,
const CRedis::ReplyStringCb& hdlStrReply)
{
if (!hdlStrReply) return;
using Rt = CRedis::ReplyType;
if (reply.type == REDIS_REPLY_NIL)
{
hdlStrReply(Rt::NIL, "");
return;
}
std::string sReply(reply.str, reply.len);
if (reply.type == REDIS_REPLY_STRING)
hdlStrReply(Rt::OK, sReply);
else
hdlStrReply(Rt::ERR, sReply);
}
void CRedis::Get(const std::string& sKey, const ReplyStringCb& hdlStrRepl)
{
assert(m_pCluster);
Cmd::commandf2(*m_pCluster, sKey,
[hdlStrRepl](const redisReply& reply) {
handleGetReply(reply, hdlStrRepl);
},
handleException,
"get %s", sKey.c_str());
}
handleException 是Cmd::command() 接口中需要的异常处理,这里是尽量重试。
Cmd::command() 中的第3个参数是 redis 应答的回调,读取 redisReply, 然后触发命令的回调。
CRedis::Get() 执行 redis GET 命令,固定1个参数,返回是字符串,nil, 或错误。
enum class ReplyType
{
OK = 0, // redis replys string/integer/array
NIL = 1, // redis replys nil
ERR = 2, // redis replys error status
};
// 简单的常用命令会自动解析reply, 使用更易用的回调。
using ReplyStringCb = function<void (ReplyType, const string& sReply)>;
CRedis::Get() 的回调就是 ReplyStringCb。
void CRedis::Set() 的回调只需知道成功或失败,SetCb 定义为:
using SetCb = function<void (bool ok)>;
失败时会有错误信息,已统一打印日志,不再暴露出来。
SET 命令完整的参数列表还有其他参数:
set key value [EX seconds] [PX milliseconds] [NX|XX]
因为常用的就 "set key value", 其他扩展的命令需要使用通用的 command() 接口,
并需要读取 redisReply.
使用 LuaIntf 导出 CRedis 到 Lua.
因为只有一个 CRedis, 所以导出为模块 c_redis:
#include <LuaIntf/LuaIntf.h>
namespace {
void Set(const string& sKey, const string& sValue, const LuaRef& luaSetCb)
{
// Default is empty callback.
auto setCb = ToFunction<CRedis::SetCb>(luaSetCb);
GetRedis().Set(sKey, sValue, setCb);
}
void Get(const string& sKey, const LuaRef& luaReplyStringCb)
{
auto replyStringCb = ToFunction<CRedis::ReplyStringCb>(
luaReplyStringCb);
GetRedis().Get(sKey, replyStringCb);
}
} // namespace
void Bind(lua_State* L)
{
LuaBinding(L).beginModule("c_redis")
.addFunction("set", &Set)
.addFunction("get", &Get)
.endModule();
}
需要将 lua 的回调函数转成 cpp 的回调:
template <class Function>
Function ToFunction(const LuaIntf::LuaRef& luaFunction)
{
// Default is empty.
if (!luaFunction)
return Function(); // skip nil
if (luaFunction.isFunction())
return luaFunction.toValue<Function>(); // Todo: catch
LOG_WARN_TO("ToFunction", "Lua function expected, but got "
<< luaFunction.typeName());
return Function();
}
Lua 这样调用:
c_redis.set("FOO", "1234")
c_redis.set("FOO", "1234", function(ok) print(ok) end)
c_redis.get("FOO", function(reply_type, reply)
assert("string" == type(reply))
if 0 == reply_type or 1 == reply_type then
print("FOO="..reply)
else
print("Error: "..reply)
end
end)
hiredis异步接口封装并导出到Lua的更多相关文章
- 基于C#的MongoDB数据库开发应用(3)--MongoDB数据库的C#开发之异步接口
在前面的系列博客中,我曾经介绍过,MongoDB数据库的C#驱动已经全面支持异步的处理接口,并且接口的定义几乎是重写了.本篇主要介绍MongoDB数据库的C#驱动的最新接口使用,介绍基于新接口如何实现 ...
- C++ Redis mset 二进制数据接口封装方案
C++ Redis mset 二进制数据接口封装方案 需求 C++中使用hiredis客户端接口访问redis: 需要使用mset一次设置多个二进制数据 以下给出三种封装实现方案: 简单拼接方案 在r ...
- NodeJS中常见异步接口定义(get、post、jsonp)
越来越多的人在使用nodeJS,作为一门服务端语言,我们不可避免的要写异步接口(ajax和jsonp).再次强调ajax和jsonp是两个概念,但是由于jquery的封装,使这两种异步接口的调用方式, ...
- vue2.0 + vux (五)api接口封装 及 首页 轮播图制作
1.安装 jquery 和 whatwg-fetch (优雅的异步请求API) npm install jquery --save npm install whatwg-fetch --save 2. ...
- javascript 异步请求封装成同步请求
此方法是异步请求封装成同步请求,加上token验证,环境试用微信小程序,可以修改文件中的ajax,进行封装自己的,比如用axios等 成功码采用标准的 200 到 300 和304 ,需要可以自行修改 ...
- java单元测试之如何实现异步接口的测试案例
测试是软件发布的重要环节,单元测试在实际开发中是一种常用的测试方法,java单元测试主要用junit,最新是junit5,本人开发一般用junit4.因为单元测试能够在软件模块组合之前尽快发现问题,所 ...
- Postman实现数字签名,Session依赖, 接口依赖, 异步接口结果轮询
Script(JS)为Postman赋予无限可能 基于Postman 6.1.4 Mac Native版 演示结合user_api_demo实现 PS 最近接到任务, 要把几种基本下单接口调试和持续集 ...
- Java微信公众平台接口封装源码分享
前言: 这篇博客是在三月初动手项目的时候准备写的,但是为了完成项目只好拖延时间写这篇博客,顺便也可以在项目中应用我自己总结的的一些经验.今天看来,这些方法的应用还是可以的,至少实现了我之前的 ...
- 基于Verilog的带FIFO输出缓冲的串口接收接口封装
一.模块框图及基本思路 rx_module:串口接收的核心模块,详细介绍请见“基于Verilog的串口接收实验” rx2fifo_module:rx_module与rx_fifo之间的控制模块,其功能 ...
随机推荐
- IDEA2017.3.4破解方式
下载jetbrainsCrack-2.7-release-str.jar包 下载地址: https://files.cnblogs.com/files/xifenglou/JetBrains.zip ...
- Shell自学二(参数传递和数组)
8.传递参数 1.使用$n来传递参数($0表示文件名) 例子: echo "执行的文件名:$0"; echo "第一个参数为:$1"; ...
- [SDOI2017]序列计数
题目描述 Alice想要得到一个长度为nn的序列,序列中的数都是不超过mm的正整数,而且这nn个数的和是pp的倍数. Alice还希望,这nn个数中,至少有一个数是质数. Alice想知道,有多少个序 ...
- [SHOI2014]概率充电器
Description 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品——概率充电器: “采用全新纳米级加工技术,实现元件与导线能否通电完全由真随机数决定!SHOI 概率充电器, ...
- linux内核中的链表
1.内核中的链表 linux内核链表与众不同,他不是把将数据结构塞入链表,而是将链表节点塞入数据,在2.1内核中引入了官方链表,从此内核中所有的链表使用都采用此链表,千万不要在重复造车轮子了!链表实现 ...
- bzoj 3673&3674: 可持久化并查集 by zky
Description n个集合 m个操作 操作: 1 a b 合并a,b所在集合 2 k 回到第k次操作之后的状态(查询算作操作) 3 a b 询问a,b是否属于同一集合,是则输出1否则输出0 0& ...
- bzoj1010[HNOI2008]玩具装箱toy 斜率优化dp
1010: [HNOI2008]玩具装箱toy Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 11893 Solved: 5061[Submit][S ...
- FFT模板(BZOJ2179)
实现了两个长度为n的大数相乘. #include <cstdio> #include <cmath> #include <complex> using namesp ...
- 用js来实现那些数据结构11(字典)
我们这篇文章来说说Map这种数据结构如何用js来实现,其实它和集合(Set)极为类似,只不过Map是[键,值]的形式存储元素,通过键来查询值,Map用于保存具有映射关系的数据,Map里保存着两组数据: ...
- linux 下文件误删恢复
linux 下文件误删恢复 0x01 事件背景 某天晚上写代码的时候,本来想删除当前目录下一个叫xxx的文件夹 rm -rdf ./xxx/*, 结果光顾着和人说话,一不留神手贱把命令敲成了rm -r ...