前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >亿级流量多级缓存 - Lua整合Redis/Nginx

亿级流量多级缓存 - Lua整合Redis/Nginx

作者头像
Parker
发布2020-07-21 14:45:35
1.2K0
发布2020-07-21 14:45:35
举报
文章被收录于专栏:在码圈在码圈

文档整理于 马士兵教育

Lua +

主要内容

l 多级缓存架构模型

l Redis整合Lua

l 利用Redis+Lua开发分布式锁

l Openresty 安装部署

l Nginx下lua脚本访问Nginx内核变量

l Nginx下利用Lua脚本访问Redis

Lua 是由巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组于1993年开发的一种轻量、小巧的脚本语言,用标准 C 语言编写,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

官网:http://www.lua.org/

Redis 在 2.6 版本中推出了脚本功能,允许开发者将 Lua 语言编写的脚本传到 Redis 中执行。使用 Lua 脚本的优点有如下几点:

l 减少网络开销:本来需要多次请求的操作,可以一次请求完成,从而节约网络开销;

l 原子操作:Redis 会将整个脚本作为一个整体执行,中间不会执行其它命令;

l 复用:客户端发送的脚本会存储在 Redis 中,从而实现脚本的复用。

Redis 与 Lua 整合

测试lua执行

在redis中执行简单脚本

登录到客户端后执行

hello world
代码语言:javascript
复制
eval   "return 1+1"    0
#命令    脚本        参数个数
参数
代码语言:javascript
复制
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截断。

独立脚本

获取key的value
代码语言:javascript
复制
local key=KEYS[1]  

local list=redis.call("get",key);  

return list;
读取redis集合中的数据
代码语言:javascript
复制
local key=KEYS[1]

local list=redis.call("lrange",key,0,-1);

return list;
统计点击次数
代码语言:javascript
复制
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脚本
本地执行
代码语言:javascript
复制
redis-cli --eval test.lua aaa,bbb
远程执行
代码语言:javascript
复制
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() 将错误内容返回。

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

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无法接受其他命令。

代码语言:javascript
复制
eval "while true do end" 0 
代码语言:javascript
复制
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
代码语言:javascript
复制
redis-cli script load "$(cat test.lua)"

得到sha1值

执行

代码语言:javascript
复制
redis-cli evalsha "7a2054836e94e19da22c13f160bd987fbc9ef146" 0

Openresty Nginx + Lua

Nginx是一个主进程配合多个工作进程的工作模式,每个进程由单个线程来处理多个连接。

在生产环境中,我们往往会把cpu内核直接绑定到工作进程上,从而提升性能。

安装

预编译安装

以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

./configure

然后在进入 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

您可以指定各种选项,比如

代码语言:javascript
复制
./configure --prefix=/opt/openresty \

           --with-luajit \

           --without-http_redis2_module \

           --with-http_iconv_module \

           --with-http_postgres_module

试着使用 ./configure --help 查看更多的选项。

make && make install

服务命令
启动

Service openresty start

停止

Service openresty stop

检查配置文件是否正确

Nginx -t

重新加载配置文件

Service openresty reload

查看已安装模块和版本号

Nginx -V

测试lua脚本

代码语言:javascript
复制
在Nginx.conf 中写入
   location /lua {

        default_type text/html;
        content_by_lua '
           ngx.say("<p>Hello, World!</p>")
         ';
      }

lua-nginx-module

创建配置文件lua.conf
代码语言:javascript
复制
   server {
        listen       80;
        server_name  localhost;

   location /lua {

        default_type text/html;

        content_by_lua_file conf/lua/hello.lua;

         }
}
在Nginx.conf下引入lua配置

include lua.conf;

创建外部lua脚本

conf/lua/hello.lua

内容:

ngx.say("<p>Hello, World!</p>")

获取Nginx uri中的单一变量
代码语言:javascript
复制
    location /nginx_var {

         default_type text/html;

        content_by_lua_block {

            ngx.say(ngx.var.arg_a)

        }
    }
获取Nginx uri中的所有变量
代码语言:javascript
复制
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请求头信息
代码语言:javascript
复制
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请求参数
代码语言:javascript
复制
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协议版本
代码语言:javascript
复制
ngx.say("ngx.req.http_version : ", ngx.req.http_version(), "<br/>")
请求方法
代码语言:javascript
复制
ngx.say("ngx.req.get_method : ", ngx.req.get_method(), "<br/>")  
原始的请求头内容
代码语言:javascript
复制
ngx.say("ngx.req.raw_header : ",  ngx.req.raw_header(), "<br/>")  
body内容体
代码语言:javascript
复制
ngx.say("ngx.req.get_body_data() : ", ngx.req.get_body_data(), "<br/>")
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-05-28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Lua +
  • 主要内容
  • Redis 与 Lua 整合
    • 测试lua执行
      • 在redis中执行简单脚本
      • hello world
      • 参数
    • 独立脚本
      • 获取key的value
      • 读取redis集合中的数据
      • 统计点击次数
      • 执行lua脚本
    • Lua 与 Redis 交互
      • Lua 脚本获取 EVAL & EVALSHA 命令的参数
      • Lua 脚本内部执行 Redis 命令
    • redis WATCH/MULTI/EXEC 与Lua
      • Redis Lua 脚本管理
        • 死锁
          • 生产环境下部署
            • 加载到redis
        • Openresty Nginx + Lua
          • 安装
            • 预编译安装
            • 源码编译安装
            • 下载
            • 服务命令
          • 测试lua脚本
            • lua-nginx-module
              • 创建配置文件lua.conf
              • 在Nginx.conf下引入lua配置
              • 创建外部lua脚本
              • 获取Nginx uri中的单一变量
              • 获取Nginx uri中的所有变量
              • 获取Nginx请求头信息
              • 获取post请求参数
              • http协议版本
              • 请求方法
              • 原始的请求头内容
              • body内容体
          相关产品与服务
          云数据库 Redis
          腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档