Loading... # Nginx与redis对Lua的简单集成 ## 什么是Lua Lua 是由巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组于1993年开发的一种轻量、小巧的脚本语言,用标准 C 语言编写,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。官网:http://www.lua.org/ ## redis与Lua整合 ### 优点 - 减少网络开销:本来需要多次请求的操作,可以一次请求完成,从而节约网络开销 - 原子操作:redis 会将整个脚本作为一个整体执行,中间不会执行其它命令 - 复用:客户端发送的脚本会存储在 redis 中,从而实现脚本的复用。 ### 在redis中执行简单脚本 登录到客户端后执行 **hello world** ``` eval "return 1+1" 0 # 命令 脚本 参数个数 ``` **参数** ``` EVAL "local msg='hello world' return msg..KEYS[1]" 1 AAA BBB ``` 表是基于1的,也就是说索引以数值1开始。所以在表中的第一个元素就是mytable[1],第二个就是mytable[2]等等。 表中不能有nil值。如果一个操作表中有[1, nil, 3, 4],那么结果将会是[1]——表将会在第一个nil截断。KEYS表示key,ARGV表示value,上面代码中 1 AAA BBB 表示有一个参数key是AAA,value是BBB。 ### 独立脚本 **获取key的value** ``` local key=KEYS[1] local list=redis.call("get",key); return list; ``` **读取redis集合中的数据** ``` local key=KEYS[1] local list=redis.call("lrange",key,0,-1); return list; ``` **统计点击次数** ``` local msg='count:' local count = redis.call("get","count") if not count then redis.call("set","count",1) end redis.call("incr","count") return msg..count+1 ``` **执行lua脚本** 本地执行 ``` redis-cli --eval test.lua aaa,bbb ``` 远程执行 ``` redis-cli -h 192.168.2.161 -a密码 --eval /usr/local/luascript/test.lua name age , xiao6 ``` ### Lua 与 redis 交互 #### Lua 脚本获取 EVAL & EVALSHA 命令的参数 通过 Lua 脚本的全局变量 KEYS 和 ARGV,能够访问 EVAL 和 EVALSHA 命令的 key [key ...] 参数和 arg [arg ...] 参数。 作为 Lua Table,能够将 KEYS 和 ARGV 作为一维数组使用,其下标从 1 开始。 #### Lua 脚本内部执行 redis 命令 Lua 脚本内部允许通过内置函数执行 Redis 命令: ``` redis.call() ``` ``` redis.pcall() ``` 两者非常相似,区别在于: 若 redis 命令执行错误,redis.call() 将错误抛出(即 EVAL & EVALSHA 执行出错); redis.pcall() 将错误内容返回。 #### redis WATCH/MULTI/EXEC 与Lua redis 原生支持 监听、事务、批处理,那么还需要lua吗? - 两者不存在竞争关系,而是增强关系,lua可以完成redis自身没有的功能 - 在lua中可以使用上一步的结果,也就是可以开发**后面操作依赖前面操作的执行结果的应用**,MULT中的命令都是独立操作 - redis可以编写模块增强功能,但是c语言写模块,太难了,lua简单的多 - 计算向数据移动 - 原子操作 lua脚本尽量短小并且尽量保证同一事物写在一段脚本内,因为redis是单线程的,过长的执行会造成阻塞,影响服务器性能。 #### redis Lua 脚本管理 1. script load 此命令用于将Lua脚本加载到Redis内存中 2. script exists ```scripts exists sha1 [sha1 …]``` 此命令用于判断sha1是否已经加载到Redis内存中 3. script flush 此命令用于清除Redis内存已经加载的所有Lua脚本,在执行script flush后,sha1不复存在 4. script kill 此命令用于杀掉正在执行的Lua脚本 #### 死锁 下面代码会进入死循环,导致redis无法接受其他命令。 ``` eval "while true do end" 0 ``` ``` 127.0.0.1:6379> keys * (error) BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE. ``` 但是可以接受 SCRIPT KILL or SHUTDOWN NOSAVE. 两个命令 SHUTDOWN NOSAVE 不会进行持久化的操作 SCRIPT KILL 可以杀死正在执行的进程 ### 生产环境下部署 #### 加载到redis ``` redis-cli script load "$(cat test.lua)" ``` 得到sha1值 执行 ``` redis-cli evalsha "7a2054836e94e19da22c13f160bd987fbc9ef146" 0 ``` ## Nginx + Lua (Openresty) OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。官网:http://openresty.org/cn OpenResty 就是Nginx增加了luajit,并默认集成了大量的lua库,可以说是一个Nginx的Lua增强版。 ### 安装 #### 预编译安装 以CentOS举例 其他系统参照:http://openresty.org/cn/linux-packages.html 你可以在你的 CentOS 系统中添加 openresty 仓库,这样就可以便于未来安装或更新我们的软件包(通过 yum update 命令)。运行下面的命令就可以添加我们的仓库: ``` yum install yum-utils yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo ``` 然后就可以像下面这样安装软件包,比如 openresty: ``` yum install openresty ``` 如果你想安装命令行工具 resty,那么可以像下面这样安装 openresty-resty 包: ``` sudo yum install openresty-resty ``` #### 源码编译安装 **下载** http://openresty.org/cn/download.html 然后再进入 `openresty-VERSION/ `目录, 然后输入以下命令配置: ``` ./configure ``` 默认, `--prefix=/usr/local/openresty` 程序会被安装到`/usr/local/openresty`目录。 依赖 `gcc openssl-devel pcre-devel zlib-devel` 安装:`yum install gcc openssl-devel pcre-devel zlib-devel postgresql-devel` 您可以指定各种选项,比如 ``` ./configure --prefix=/opt/openresty \ --with-luajit \ --without-http_redis2_module \ --with-http_iconv_module \ --with-http_postgres_module ``` 试着使用 `./configure --help` 查看更多的选项。 ``` make && make install ``` ### 测试lua脚本 ``` 在Nginx.conf 中写入 location /lua { default_type text/html; content_by_lua ' ngx.say("<p>Hello, World!</p>") '; } ``` #### 在Nginx.conf下引入lua配置 ``` include lua.conf; ``` #### 创建外部lua脚本 ``` content_by_lua_file conf/lua/hello.lua; ``` 内容: ``` ngx.say("<p>Hello, World!</p>") ``` #### 获取Nginx uri中的单一变量 ``` location /nginx_var { default_type text/html; content_by_lua_block { ngx.say(ngx.var.arg_a) } } ``` #### 获取Nginx uri中的所有变量 ``` local uri_args = ngx.req.get_uri_args() for k, v in pairs(uri_args) do if type(v) == "table" then ngx.say(k, " : ", table.concat(v, ", "), "<br/>") else ngx.say(k, ": ", v, "<br/>") end end ``` #### 获取Nginx请求头信息 ``` local headers = ngx.req.get_headers() ngx.say("Host : ", headers["Host"], "<br/>") ngx.say("user-agent : ", headers["user-agent"], "<br/>") ngx.say("user-agent : ", headers.user_agent, "<br/>") for k,v in pairs(headers) do if type(v) == "table" then ngx.say(k, " : ", table.concat(v, ","), "<br/>") else ngx.say(k, " : ", v, "<br/>") end end ``` #### 获取post请求参数 ``` ngx.req.read_body() ngx.say("post args begin", "<br/>") local post_args = ngx.req.get_post_args() for k, v in pairs(post_args) do if type(v) == "table" then ngx.say(k, " : ", table.concat(v, ", "), "<br/>") else ngx.say(k, ": ", v, "<br/>") end end ``` #### http协议版本 ``` ngx.say("ngx.req.http_version : ", ngx.req.http_version(), "<br/>") ``` #### 请求方法 ``` ngx.say("ngx.req.get_method : ", ngx.req.get_method(), "<br/>") ``` #### 原始的请求头内容 ``` ngx.say("ngx.req.raw_header : ", ngx.req.raw_header(), "<br/>") ``` #### body内容体 ``` ngx.say("ngx.req.get_body_data() : ", ngx.req.get_body_data(), "<br/>") ``` Last modification:June 11th, 2020 at 06:09 pm © 允许规范转载
建议在揭露问题时提供建设性解决方案。