redis-2.6支持通过EVAL命令来执行lua脚本,对lua脚本的支持扩展了redis的应用场景,redis支持路脚本需要做2件事
接下来,我将通过一个简单的实例来解析redis如何完成上述两个工作的。
#define DICT_SIZE 100 struct redisDict { char* key[DICT_SIZE]; char* value[DICT_SIZE]; int idx; }; static void setCommand(const char *key, const char *value) { /* ignore memory issue for simple */ if (dict.idx + 1 <= DICT_SIZE) { dict.key[dict.idx] = (char *)malloc(strlen(key) + 1); strcpy(dict.key[dict.idx], key); dict.value[dict.idx] = (char *)malloc(strlen(value) + 1); strcpy(dict.value[dict.idx], value); dict.idx += 1; } } static const char *getCommand(const char *key) { int j; for (j = 0; j <= dict.idx; j++) { if (strcmp(dict.key[j], key) == 0) { return dict.value[j]; } } return "KeyNotFound"; }
上述代码实现了一个伪redis,支持setCommand、getCommand。
具体例子参考http://lua-users.org/wiki/SimpleLuaApiExample
/* * All Lua contexts are held in this structure. We work with it almost * all the time. */ lua_State *L = luaL_newstate(); luaL_openlibs(L); /* Load Lua libraries */ /* Load the file containing the script we are going to run */ status = luaL_loadfile(L, "script.lua"); /* Ask Lua to run our little script */ result = lua_pcall(L, 0, LUA_MULTRET, 0);
上述代码片段中,其中script.lua是一个lua脚本。redis里稍有不同,redis里的脚本是通过EVAL命令传递到服务器端,redis将脚本拼成一个lua函数,然后调用loadbuffer,而这里从文件执行脚本调用的loadfile。
下面的lua代码里,调用的是redis的setCommand和getCommand。
redis.call("set", "foo", "bar");
return redis.call("get", "foo");
要想lua脚本能调用C代码,需要现在lua环境注册对应的C函数,参考redis的scriptingInit函数。
static int call(lua_State *L) { int argc = lua_gettop(L); const char *cmd = lua_tostring(L, 1); const char *key = lua_tostring(L, 2); if (strcmp(cmd, "set") == 0) { assert(argc == 3); const char *value = lua_tostring(L, 3); setCommand(key, value); return 0; } else if (strcmp(cmd, "get") == 0) { assert(argc == 2); lua_pushstring(L, getCommand(key)); return 1; } lua_pushstring(L, "Invalid Command"); return 1; } static void scriptingInit() { L = luaL_newstate(); luaL_openlibs(L); /* Register the redis commands table and fields */ lua_newtable(L); /* redis.call */ lua_pushstring(L, "call"); lua_pushcfunction(L, call); lua_settable(L, -3); /* Finally set the table as 'redis' global var. */ lua_setglobal(L, "redis"); }