来源:Node.js

Lua

Lua是一种轻量级、高效、可嵌入的脚本语言,具有简洁的语法和灵活的语义,被广泛应用于嵌入式系统、游戏开发、Web应用、脚本编写等领域

Lua的设计目标之一是作为扩展和嵌入式脚本语言,可以与其他编程语言无缝集成

Lua的核心只有很小的代码库,但通过使用模块和库可以轻松地扩展其功能

Lua的特点:

  • 简洁高效:
    • 语法简单清晰,语义灵活高效
    • 使用动态类型和自动内存管理
    • 支持面向过程和函数式编程风格
    • 提供了强大的协程支持
  • 嵌入式脚本语言:
    • Lua是一种可嵌入的脚本语言,可以轻松地与其他编程语言集成
    • 它提供了C API,允许开发者将Lua嵌入到C/C++程序中,或者通过扩展库将Lua嵌入到其他应用程序中
  • 游戏开发:
    • Lua在游戏开发中广泛应用,许多游戏引擎(如Unity和Corona SDK)都支持Lua作为脚本语言
    • 开发者可以使用Lua编写游戏逻辑、场景管理和AI等
  • 脚本编写:
    • 由于其简洁性和易学性,Lua经常被用作脚本编写语言
    • 可以用于编写各种系统工具、自动化任务和快速原型开发
  • 配置文件:
    • Lua的语法非常适合用作配置文件的格式
    • 许多应用程序和框架使用Lua作为配置文件语言,因为它易于阅读、编写和修改

为了增强性能和扩展性,可以将Lua与Redis和Nginx结合使用,从而构建高性能的Web应用程序或API服务

使用

数据类型

  • nil:无效值或缺失值
  • boolean:布尔值,true或false
  • number:数字,包括整数和浮点数
  • string:字符串,由字符序列组成
  • table:表(对象),一种关联数组,用于存储和组织数据
  • function:函数,用于封装可执行的代码块
  • userdata:用户自定义数据类型,通常与C语言库交互使用
  • thread:协程,用于实现多线程编程
  • metatable:元表,用于定义表的行为

变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- 定义全局变量 不需要修饰符,直接定义 动态类型 类似python
name = 'Cheriko'

-- 打印
print(name)

-- 代码块 类似JavaScript的{}
do
-- 定义局部变量,需要加local
local age = 23
-- 定义全局变量
phone = '114514'
end
print(age, phone) -- -> nil 114514
-- 字符串拼接
print('yajue' .. 'senpai') -- -> yajuesenpai

数组和对象

1
2
3
4
5
6
7
-- 数组 下标从1开始
arr = {1,1,4,5,1,4}
print(arr[0], arr[1]) -- -> nil 1

-- 对象
obj = {name = 'Cheriko', age = 23}
print(obj.name, obj.phone) -- -> Cheriko nil

函数

1
2
3
4
5
-- 函数
function add(a, b)
return a + b
end
print(add(1, 2)) -- -> 3

判断

1
2
3
4
5
6
7
8
-- 判断
if name == 'Cheriko' then
print('Hello Cheriko')
elseif name == 'yajue' then
print('Hello yajue')
else
print('Hello World')
end

循环

1
2
3
4
5
6
7
8
9
10
11
12
-- 循环 初始值 结束值 步进值(不写时默认为1)
for i = 1, 10, 2 do
print(i) -- -> 1 3 5 7 9
end

-- 循环数组或对象 paris是迭代器
for i, v in ipairs(arr) do
print(i, v) -- -> 1 1 2 1 3 4 4 5 5 1 6 4
end
for k, v in pairs(obj) do
print(k, v) -- -> name Cheriko age 23
end

读写文件

1
2
3
4
5
6
7
8
9
10
11
12
-- 读取文件
local file = io.open('./index.txt', 'r')
-- *a 读取全部内容
print(file.read(file, '*a'))
-- 这个语法是上面的语法糖
print(file:read('*a'))
file:close()

-- 写入文件
local file = io.open('./index.txt', 'w')
file:write('Hello World')
file:close()

模块化

utils.lua:

1
2
3
4
5
6
7
8
9
10
11
12
-- 模块
local Modules = {}

-- 向模块中添加函数
function Modules.add(a, b)
return a + b
end

Modules.PI = 3.14

-- 导出模块
return Modules

index.lua:

1
2
3
-- 导入模块
local M = require('./utils')
print(M.add(3, 4), M.PI) -- -> 7 3.14

结合ioredis

使用ioredis和lua实现一个redis节流阀

index.js:

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
import express from 'express'
import Redis from 'ioredis'
import fs from 'node:fs'

const lua = fs.readFileSync('index.lua', 'utf8')

const redis = new Redis({
port: 6379,
host: '127.0.0.1'
})

const app = express()


// 限流阀,如果在30秒内操作了超过5次,则应该限流
const KEY = 'lottery' // 存入redis的key
const TIME = 30 // 限流时间
const LIMIT = 5 // 限流次数

// 跨域
app.use('*', (req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*')
next()
}
)

// 抽奖接口
app.get('/lottery', (req, res) => {
// 将限流阀参数传给lua脚本,再将执行后的结果存入redis
// eval的参数:lua脚本名, key个数, ...符合key个数的keys , ...args
redis.eval(lua, 1, KEY, TIME, LIMIT, (err, result) => {
if (err) {
console.log(err)
}

if(result === 1) {
res.send('抽奖成功')
} else {
res.send('频繁操作,请稍后再试')
}
})
})

app.listen(11451, () => {
console.log('server start at 11451')
})

index.lua:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-- redis调用lua时才能获取到这些
local key = KEYS[1]
local interval = tonumber(ARGV[1])
local count = tonumber(ARGV[2])

-- 使用redis.call操作redis

-- 从redis中获取key被调用的次数,默认为'0'
local limit = tonumber(redis.call("get", key) or '0')

-- 如果超过限制次数,返回1,否则返回0
if limit + 1 >= count then
return 0
else
redis.call("incr", key) -- 限制次数加1
redis.call("expire", key, interval) -- 设置key的过期时间
return 1
end

index.html:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button id="lottery">抽奖</button>
<script>
document.getElementById('lottery').addEventListener('click', () => {
fetch('http://localhost:11451/lottery').then(res => {
return res.text()
}).then(res => {
alert(res)
})
})
</script>
</body>
</html>