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

完整示例代码

1
2


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

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

 来自CODE的代码片

script.lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116


/* 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;

}

 来自CODE的代码片

lua.c

时间: 2024-11-16 02:12:20

redis lua原理分析的相关文章

Redis Lua脚本原理

2.6版本之后支持嵌入Lua脚本,客户端使用Lua脚本,直接在服务器端原子的执行多条命令 Lua脚本执行过程 创建并修改Lua环境 1 创建基础Lua环境 2 载入函数库 3 创建全局表格Lua 4 替换随机函数 5 创建排序辅助函数 6 创建redis.pcall函数 7 全局环境保护 8 修改后的Lua环境保存到服务器状态的Lua属性,等待脚本执行 Redis中带有不确定性的命令: SINTER SUNION SDIFF SMEMEBERS HKEYS HVALS KEYS 注意: Redi

如何用redis+lua+jsp实现一个预约系统

问题描述 如何用redis+lua+jsp实现一个预约系统 我打算做一个停车场预约系统:用户通过JSP页面查询每个A-B-C-D区剩余的车位然后去预约它,然后服务器根据用户预约的数目从总数中减少,服务器是TOMCAT,想问问大牛们 总体设计是怎么实现的 解决方案 首先就是HTML语言 HTML作为一个入口,要实现预约,就需要知道谁预约的,预约的哪个位置. 实现预约就需要用到事件,你要知道点击的是哪个座位! 解决方案二: redis是做数据缓存,提高效率

ASP组件上传的三种机制和实现原理分析

上传 ASP 组件 FILE对象 当前,基于浏览器/服务器模式的应用比较流行.当用户需要将文件传输到服务器上时,常用方法之一是运行FTP服务器并将每个用户的FTP默认目录设为用户的Web主目录,这样用户就能运行FTP客户程序并上传文件到指定的 Web目录.这就要求用户必须懂得如何使用FTP客户程序.因此,这种解决方案仅对熟悉FTP且富有经验的用户来说是可行的. 如果我们能把文件上传功能与Web集成,使用户仅用Web浏览器就能完成上传任务,这对于他们来说将是非常方便的.但是,一直以来,由于File

搜索引擎判断网站是否作弊的原理分析(三)

广州SEO陈永继续为大家讲解搜索引擎判断网站如何判断网站是否作弊的原理,上节讲解完TrustRank算法,这一节将详细讲解BadRank算法. BadRank据传是Google采用的反链接作弊算法.它是一种典型的不信任传播模型,即首先构建作弊网页集合,之后利用链接关系来讲这种不信任分值传递到其他网页. BadRank包含的基本假设是:如果一个网页将其链接指向作弊页面,则这个网页也很可能是作弊网页:而如果一个网页被作弊网页指向,则不能说明这个网页是有问题的,因为作弊网页也经常将其链接指向一些知名网

搜索引擎判断网站是否作弊的原理分析(二)

承接搜索引擎判断网站是否作弊的原理分析(一) 广州SEO陈永继续为大家分析信任传播模型.不信任传播模型及异常发现模型3个代表算法,它们分别是TrustRank算法.BadRank算法和SpamRank算法. 我们先详细介绍TrustRank算法 TrustRank算法属于信任传播模型,基本遵循信任传播模型的流程,即算法流程如下两个步骤组成. 步骤一:确定值得信任的网页集合 TrustRank算法需要靠人工审核来判断某个网页应该被放入网页集合,考虑到人工审核工作量大,所以提出了两种初选信任网页集合

IOS开发:Cocos2d触摸分发原理分析

  触摸是iOS程序的精髓所在,良好的触摸体验能让iOS程序得到非常好的效果,例如Clear.鉴于同学们只会用cocos2d的 CCTouchDispatcher 的 api 但并不知道工作原理,但了解触摸分发的过程是极为重要的.毕竟涉及到权限.两套协议等的各种分发. 本文以cocos2d-iphone源代码为讲解.cocos2d-x 于此类似,就不过多赘述了. 零.cocoaTouch的触摸 在讲解cocos2d触摸协议之前,我觉得我有必要提一下CocoaTouch那四个方法.毕竟cocos2

Photoshop图层与色彩原理分析

  PS入门教程 Photoshop图层与色彩原理分析   教程结束,以上就是Photoshop图层与色彩原理分析,希望大家看完这篇教程之后能有一定的帮助! 分类: PS入门教程

web上存漏洞及原理分析、防范方法(文件名检测漏洞)

我们通过前篇:<web上存漏洞及原理分析.防范方法(安全文件上存方法) >,已经知道后端获取服务器变量,很多来自客户端传入的.跟普通的get,post没有什么不同.下面我们看看,常见出现漏洞代码.1.检测文件类型,并且用用户上存文件名保存 复制代码 代码如下: if(isset($_FILES['img'])) { $file = save_file($_FILES['img']); if($file===false) exit('上存失败!'); echo "上存成功!"

色彩搭配原理分析及技巧

色彩搭配原理分析及技巧: 研究色彩是为了使用色彩,也就是说最大限度地发挥色彩的作用.色彩的意义与内容在艺术创造和表现方面是复杂多变的,但在欣赏和解释方面又有共通的国际特性,可见它在人们心目中不但是活的,也是一种很美的大众语言.所以,通过对色彩的各种心理分析,找出它们的各种特性,可以做到合理而有效地使用色彩. 一.红色系 红色系是从色相环上的红紫色到朱红色之间的色彩.它的刺激作用很大,具有很高的注目性和视认性.大红色是暖色系里温度最高的色彩,红色系原色彩对人的心理能产生很大的鼓舞作用. 1.纯色使