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

完整示例代码




redis           .           call           (           "           set"           ,            "           foo"           ,            "           bar"           );


return            redis           .           call           (           "           get"           ,            "           foo"           );



来自CODE的代码片



script.lua



 


/* gcc -o test_lua lua.c -llua -lm -ldl */


#include <lua.h>


#include <lauxlib.h>


#include <stdlib.h>


#include <stdio.h>


#include <string.h>


#include <assert.h>


              


#define DICT_SIZE 100


struct        redisDict        {


char       *        key       [       DICT_SIZE       ];


char       *        value       [       DICT_SIZE       ];


int        idx       ;


};


              


struct        redisDict        dict       ;


lua_State        *       L       ;


              


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"       ;


}


              


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        scriptingRun       (       const        char       *        filename       )


{


int        status        =        luaL_loadfile       (       L       ,        filename       );


if        (       status       )        {


fprintf       (       stderr       ,        "Couldn't load file: %s       \n       "       ,        lua_tostring       (       L       ,        -       1       ));


exit       (       1       );


}


              


/* Ask Lua to run our little script */


int        result        =        lua_pcall       (       L       ,        0       ,        LUA_MULTRET       ,        0       );


if        (       result       )        {


fprintf       (       stderr       ,        "Failed to run script: %s       \n       "       ,        lua_tostring       (       L       ,        -       1       ));


exit       (       1       );


}


              


/* Get the returned value at the top of the stack (index -1) */


const        char        *       value        =        lua_tostring       (       L       ,        -       1       );


              


printf       (       "%s       \n       "       ,        value       );


              


lua_pop       (       L       ,        1       );        /* Take the returned value out of the stack */


lua_close       (       L       );        /* Cya, Lua */


}


              


static        void        scriptingInit       ()


{


/*


 * All Lua contexts are held in this structure. We work with it almost


 * all the time.


 */


L        =        luaL_newstate       ();


              


/* Load Lua libraries */


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"       );


}


              


int        main       (       void       )


{


scriptingInit       ();


scriptingRun       (       "script.lua"       );


return        0       ;


}