likes
comments
collection
share

lua脚本的妙用

作者站长头像
站长
· 阅读数 14

Lua语言

先介绍下背景,Lua是一种强大、高效、轻量级、可嵌入的脚本语言。它支持过程式编程、面向对象编程、函数式编程、数据驱动编程和数据描述。有个比较特殊的点是下标从1开始。

数据结构

local my_table = {}
table 字典、数组;metaTable

生态

  • require "test"
  • luarocks:包管理
  • Lapis:web框架
  • luasql:数据库操作

应用

  1. redis,相关命令:EVAL、script load、script exists版本7之后支持lua的函数(function),对应命令,FUNCTION LOAD、FCALL

  2. nginx,可以嵌入到nginx的生命周期中,用于操作header、cookie等非常方便

demo

#!/usr/local/bin/lua

print("hello world")

_table = {key1=100, value1=200, "name"}
_table2 = {"sife","sife"}

function _table.fun01( name, age )
    print(name,age)
end

for i,v in pairs(_table) do
    print(i,v)
end

for i,v in pairs(_table2) do
    print(i,v)
end

print(_table.fun01("uptown", 18))

return _table

redis中的应用

最近搞了一个小工具,其中用到了一个类似白名单的东西,为了做的轻量级,设计成将白名单放到redis中,然后用后台任务不断的从数据库中刷新到redis从而保证更新。

这里有一个问题,白名单用queue保存,理论上每次都要先删除再新增,众所周知redis是不保证原子性的。为了保证redis原子性需要引入lua脚本。

结合springboot引入spring-boot-starter-data-redis依赖,使用StringRedisTemplate 提交lua脚本

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

为了保证删除与push的原子性

 "redis.call('del', 'topic_uptown')" +
 "local topics = KEYS " +
 "for i, v in pairs(topics) do " +
 "  redis.call('lpush', 'topic_uptown', v) " +
 "end";

Long result = stringRedisTemplate.execute(redisScript, topics);

入参是从数据库查出来的List。

nginx中的应用

最近遇到的就是,客户现场验收测试某个接口的并发性能,由于来不及申请资源,直接在nginx层指定接口,返回特定的数据即可。

location = /api/tb/preview {
  set $upstream 'fe_full_long';
  access_by_lua_block {
    ngx.req.read_body()
    local data = ngx.req.get_body_data()
    if ngx.re.match(ngx.var.request_body, "tb_92a433c3fd18411383c463072ba282b1") then
      ngx.var.upstream = "127.0.0.1:xxx/xxxx?filename=a.txt"
    end
  }
  proxy_pass http://$upstream;
  proxy_set_header Host $host:$server_port;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}

access_by_lua在请求访问阶段处理,用于访问控制,适用于http、server、location、location if。

Lua为什么可以保证原子性

使用lua脚本可以在执行一串redis命令时,实现一定原子性,脚本中多条指令执行过程中不会被插入新的指令,但是并不能在命令执行出错时回滚之前的结果。在Redis实例中共用同一个Lua解释器某一个lua脚本在被执行的时候,其他lua脚本无法执行。从而保证原子性