转载

redis lua原理分析

redis-2.6支持通过EVAL命令来执行lua脚本,对lua脚本的支持扩展了redis的应用场景,redis支持路脚本需要做2件事

  1. redis能执行lua脚本
  2. 在lua脚本里能执行redis的命令

接下来,我将通过一个简单的实例来解析redis如何完成上述两个工作的。

构建一个简单的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。

C调用lua脚本

具体例子参考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调用C函数

下面的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"); } 

完整示例代码

正文到此结束
Loading...