[学习笔记 Day02]Redis + OpenResty + Lua 实现多级缓存

Lua

Lua 是一种轻量级、高效的脚本语言,由巴西里约热利卡天主教大学(PUC-Rio)的 Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo 于 1993 年开发。名字来源于葡萄牙语,意为”月亮”。

核心特点:

  1. 轻量与高效
    整个解释器仅约 200KB,是目前最轻量的脚本语言之一。
    基于寄存器的虚拟机,执行速度快。
    自动内存管理(增量式垃圾回收)。
  2. 简洁的语法
    语法极为精简,保留关键字不到 20 个。
    学习曲线平缓,容易上手。
  3. 唯一的数据结构:表(Table)
    Lua 的核心设计哲学是用 table 一种结构统一表示数组、字典、对象、模块等几乎所有概念:
  4. 元表与元方法(Metatable)
    通过元表机制可以自定义表的行为,实现运算符重载、继承等高级特性:
  5. 协程(Coroutine)
    Lua 原生支持协程,适合异步编程和状态机:

主要应用场景

领域典型案例
游戏开发World of Warcraft 插件、Roblox(Luau)、《愤怒的小鸟》、大量国产手游的热更新逻辑
嵌入式脚本Redis(内置 Lua 脚本引擎)、Nginx(OpenResty)、Wireshark
配置与扩展Neovim (Vimscript 替代者)、AwesomeWM 窗口管理器、Lightroom
物联网NodeMCU / ESP8266 上运行 Lua 固件

Lua 语法

  • centos 自带,文件后缀名:xxx.lua
  • 打印:print(text)
  • 运行命令: lua xxx.lua
  • 获取变量 / 值的类型:type(变量 / 表达式)
  • lua 命令行:lua
  • 字符串拼接:str1 .. str2

数据类型

类型说明
nil无效值(表达式中相当于 false)
booleanfasle 和 true
number表双精度类型的实浮点数
string字符串(单、双引号)
function由 C / lua 编写的函数
table关联数组,索引可以是数字(从 1 开始),字符串或表类型,创建时通过 构造表达式 完成,最简单构造表达式 {},用来创建空表
  • 声明变量:local 变量名 = 值
# table 类型
local map = {name = 'Jack' ,age = 21}

local arr = {1,2,3}
  • for 循环:for index,value in ipairs(arr) do ... end
# index => key
# ipairs:解析数组 table,声明 key 为索引的 table
# pairs():JSON 类似的table

for index,value in ipairs(arr) do
      代码块
end
  • 函数:local function 函数名(参数列表) ... end
local function 函数名(参数列表)
      //方法体
end
  • 条件判断:
if (表达式)
then 

else

end
  • 操作符
操作符示例说明
andA and BA 为 false,返回 A,否则返回 B
orA or BA 为 true,返回 A,否则返回 B
notnot A相反运算

OpenResty

一个基于 Nginx 的高性能 web 平台

特点:

  • 具备 Nginx 的完整性能
  • 基于 lua 语言继续宁扩展,集成大量精良的 Lua 库,第三方模块
  • 允许使用 Lua 自定义业务逻辑,自定义库

安装(Linux 环境)

依赖库:

yum install -y pcre-devel openssl-devel gcc --skip-broken

OpenResty 库:

yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo

安装 opm 工具:

yum install -y openresty-opm

OpenResty 目录:/usr/local/openresty

配置 Nginx 的环境变量:

vi /etc/profile
# 在上述路径文件中加入以下内容
export NGINX_HONE = /usr/local/openresty/nginx
export PATH = ${NGINX_HOME}/sbin:$PATH

配置生效

source /etc/profile

启动

nginx

重新加载配置

nginx -s reload

停止

nginx -s stop

入门

在 nginx.conf 的 http 下面添加对 OpenResty 的 Lua 模块的加载:

# 加载 Lua 模块
lua_package-path "/usr/local/openresty/lualib/?.lua;;";

# 加载 C 模块
lua_package-path "/usr/local/openresty/lualib/?.so;;";

在 nginx.conf 的 server 下面,添加某个路径的监听

local /path {
      # 响应类型,如返回 JSON
      default_type application/json;
      # 响应数据由 lua/xx.lua 文件决定
      context_by_lua.file lua/xx.lua;
}

添加假内容:

ngx.say('{"id":1,"name":"Mark"}')
  • 返回指定内容:ngx.say(text)

nginx 内部发送 http 请求:

local rep = ngx.location.capture("/path",{
      method = ngx.HTTP_GET,      # 请求方式
      args = {a = 1 , b = 2},     # get 请求方式传参
      body = "c:3 & b:4"          # post 请求方式传参
})

返回的响应内容:

  • rep.status:响应状态码
  • rep.header:响应体

反向代理:

local /item/path {
      # 防火墙需要关闭
      proxy_pass http://192.168.1.x:8001;
}

查询请求函数查询:

vi /usr/local/openresty/lualib/common.lua
local fucntion read_http(path.params)
      local rep = ngx.location.capture(path,{
            method = ngx.HTTP_GET,
            args = params
      })

      if not rep then
            ngx.log(ngx.ERR,"http not found")
            ngx.exit(404)
      end
      return rep.body
end

# 将方法导出
local _M = {read.http = read_http}
return _M
# 导入包
local common = require('common')

# 返回结果
ngx.say(xxx)

扩展:JSON 结果处理

引入 cjson 模块:

local cjson = require("json")

序列化:

local obj = {name = 'jack' , age = 21}
local json = cjson.encode(obj)

反序列化:

local obj = cjson.decode(json)

定义集群:

upstream xx-cluster{
      # 缓存
      hash $request_url;
      server 192.168.1.x:8001
      server 192.168.1.x:8002
}

反向:

local /path {
      proxy_pass http://xx-cluster
}

Redis 冷启动与缓存预热:

  • 冷启动:服务刚启动,Redis 并没有缓存
  • 缓存预热:将热点数据提前查询并保存

Redis 模块

引入并初始化:

local redis = require('restry.redis')

# 初始化对象
local red = redis:new()
# 设置超时时间
red:set_timeouts(1000,1000,1000)

封装释放 Redis 连接的函数

local function close_redis(red)
      # 连接空闲时间(ms)
      local pool_max_idle_time = 10000
      # 连接池大小
      local pool_size = 100
      local ok,error = red:set_keepalive(pool_max,idle_time,pool_size)

      if not ok then
            ngx.log(ngx.ERR,"xxx",err)
      end
end

封装读取并返回数据的函数:

local fucntion read_redis(ip,port,key)
      local ok,err = red:connect(ip,port)
      if not ok then
            ngx.log(ngx.ERR,"xxx",err)
            return nil;
      end

      local rep,err = red:get(key)
      if not rep then
            ngx.log(ngx.Err,"xxx",err,",key = ".key)
      end

      if rep == ngx.null then
            rep = nil
            ngx.log(ngx.ERR,'xxx,key = '.key)
      end
      close_redis(red)
      return rep
end

Nginx 本地缓存

OpenRestry 提供了 shard dict 的功能

开启共享字典:

# lua_shard_dict 变量名 大小
lua_shard_dict item_cacha 150mb

操作共享字典:

# 获取本地缓存对象
local item_cache = ngx.shard.item_cache
# 存储(最后一个参数如果设为 0 表示永不过期,单位 秒)
item_cache = set('key','value',1000)
local val = item_cache:get('key')

缓存同步

设置有效期:

  • 优点:简单方便
  • 缺点:时效差,过期之前可能不一致
  • 场景:更新频率低,时效性低的

同步双写:修改数据库的同时更新缓存

  • 优点:时效性强,缓存与数据库强一致
  • 缺点:有代码侵入,耦合性高
  • 场景:对一致性,时效性高的

异步通知:该数据库的同时发送通知事件,相关服务监听到后修改

  • 优点:低耦合,可同时通知多个缓存数据
  • 缺点:时效性一般,可能存在中间不一致状态
  • 场景:时效性一般,有多个服务需要同步

请求参数处理

请求参数类型示例代码
路径占位符/item/1001# ~:表示正则表达式
local ~ /item/(\d+){}
=>
local id = ngx.val[1]
请求头id:1001# 返回 table 类型
local headers = ngx,req.get_headers()
Get 请求参数?id = 1001local getParams = ngx.req.get_url_args()
Post 请求参数id = 1001ngx.req.read_body();
local post = ngx.req.get_post_args()
JSON 参数{“id”:1001}ngx.req.read_body()
# 返回 json 数据
local json = ngx.req.get_body_data()
https://wp.itdka.cn/1513.html
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容