通过 lua_shared_dict 指令可以声明一个共享内存区域,可以在多个 worker 进程间共享,单位支持 k、m,然后配合 ngx.shared.DICT api函数来操作。nginx -s reload 后共享内存的数据还在。
这个共享内存功能非常有用,极大的便利了worker 进程间的通信和协作,而且还提供了类似 redis 的数据结构,可以当做一个简易的数据库,而且没有通信开销。
共享内存在 Openresty 里用途很广,基于它可以实现流量统计、缓存、进程锁等高级功能。
http {lua_shared_dict dogs 10m;server {location /set {content_by_lua_block {local dogs = ngx.shared.dogsdogs:set("Jim", 8)ngx.say("STORED")}}location /get {content_by_lua_block {local dogs = ngx.shared.dogsngx.say(dogs:get("Jim"))}}}}
[root@192 ~]# curl localhost/set
STORED
[root@192 ~]# curl localhost/get
8
[root@192 ~]# openresty -p /var/openresty -s reload
[root@192 ~]# curl localhost/get
8
下面将介绍一些操作共享内存的函数,这些函数都是原子操作,只要在同一个内存区域,在多个worker 进程间并发都是安全的。
ngx.shared.DICT.set
syntax: success, err, forcible = ngx.shared.DICT:set(key, value, exptime?, flags?)
无条件设置一个共享内存的值,返回3个值:
success:布尔值,是否成功
err:错误信息
forcible:布尔值,是否由于内存不足把其他有效的值挤出去了
value 值可以是 布尔值、数字、字符串、nil,同过get返回的保留这些类型。
exptime 可选值用来设置过期时间(秒),精度为 0.001s。默认值 0 代表不过期。
flags 可选值,一个标记,无符号32位整形,默认为0,通过 get 也可以返回。
当内存不够分配,那么会强制移除最老的项根据过期时间排序,如果移除了还是由于某种原因不能新增数据,那么 success 为fasle,错误信息存入 err。
local cats = ngx.shared.cats
local succ, err, forcible = cats.set(cats, "Marry", "it is a nice cat!")
也可以是下面这种方式
local cats = ngx.shared.cats
local succ, err, forcible = cats:set("Marry", "it is a nice cat!")
ngx.shared.DICT.safe_set
syntax: ok, err = ngx.shared.DICT:safe_set(key, value, exptime?, flags?)
同上,只是当内存不足时不会强制移除没过期的项目来腾出空间,直接返回 nil 和 错误信息。
ngx.shared.DICT.add
syntax: success, err, forcible = ngx.shared.DICT:add(key, value, exptime?, flags?)
同 ngx.shared.DICT.set,只是如果 key 已经存在且没过期,返回 fasle,err 设置为 "exists"。
ngx.shared.DICT.safe_add
syntax: ok, err = ngx.shared.DICT:safe_add(key, value, exptime?, flags?)
同上,只是当内存不足时不会强制移除没过期的项目来腾出空间,直接返回 nil 和 错误信息。
ngx.shared.DICT.get
syntax: value, flags = ngx.shared.DICT:get(key)
获取共享内存的值,如果 key 不存在或者过期,返回 nil。出错返回 nil 和 错误信息。
local sh_dog = ngx.shared.dogs
local value,flags = sh_dog.get(sh_dog, "Jim")
另一种获取方式
local sh_dog = ngx.shared.dogs
local value,flags = sh_dog:get("Jim")
ngx.shared.DICT.get_stale
syntax: value, flags, stale = ngx.shared.DICT:get_stale(key)
同上,就算过期了也返回(只要没清除)。stale 代表是否过期了。
ngx.shared.DICT.replace
syntax: success, err, forcible = ngx.shared.DICT:replace(key, value, exptime?, flags?)
类似 set,只是 key 必须存在且没过期才会替换成功,否则返回 false,err为 "not found"。
ngx.shared.DICT.delete
syntax: ngx.shared.DICT:delete(key)
无条件删除,等同于
ngx.shared.DICT:set(key, nil)
ngx.shared.DICT.incr
syntax: newval, err, forcible? = ngx.shared.DICT:incr(key, value, init?, init_ttl?)
将 key 里面的数字值增加 value,并返回增加后的数值。value 和 init 可以是正负整数,正负浮点数。
如果 key 里面原来的值不是数字,返回 nil,err设置为 "not a number"。
如果 key 不存在或者过期:
1.如果提供了 init,那么将会新增 key,并初始化值为 init+value,如果内存不足,还是会挤掉最老的值,如果挤掉了,那么 forcible 就为 true。init_ttl 代表新增的 key 的过期时间,默认为 0,不过期。
2.如果没提供,返回 nil,err设置为 "not found"。
操作列表
ngx.shared.DICT.lpush
syntax: length, err = ngx.shared.DICT:lpush(key, value)
从key对应的列表的最左边插入 value,返回插入后列表的元素个数。如果内存不足,直接返回 nil,err设置为 "no memory"。
如果 key 不存在,那么会新建空列表后再插入,如果 key 已经存在但是不是列表,返回 nil,err为 "value not a list"。
ngx.shared.DICT.rpush
syntax: length, err = ngx.shared.DICT:rpush(key, value)
同上,只是从右边插入。
ngx.shared.DICT.lpop
syntax: val, err = ngx.shared.DICT:lpop(key)
移除由key指定的列表最左边的值并返回,key 不存在返回 nil,key存在但是不是列表,返回nil,err为 "value not a list"。
ngx.shared.DICT.rpop
syntax: val, err = ngx.shared.DICT:rpop(key)
同上,只是从右边移除。
ngx.shared.DICT.llen
syntax: len, err = ngx.shared.DICT:llen(key)
返回指定 key 列表的元素个数,key 不存在返回0,存在但是不是列表返回 nil,err 为 "value not a list"。
ngx.shared.DICT.ttl
syntax: ttl, err = ngx.shared.DICT:ttl(key)requires: resty.core.shdict or resty.core
返回指定 key 的过期时间(秒),精度为 0.001,返回 0 代表永不过期。如果 key 不存在或者过期,返回 nil,err为 "not found"。
lua_shared_dict dogs 10m;server {location /set {content_by_lua_block {local dogs = ngx.shared.dogsdogs:set("Jim", 8, 100)ngx.say("STORED")}}location /get {content_by_lua_block {require "resty.core"local dogs = ngx.shared.dogslocal ttl,err = dogs:ttl("Jim")ngx.say(ttl)}}}
[root@192 ~]# curl localhost/set
STORED[root@192 ~]# curl localhost/get
98.622
[root@192 ~]# curl localhost/get
96.542
[root@192 ~]# curl localhost/get
95.744
ngx.shared.DICT.expire
syntax: success, err = ngx.shared.DICT:expire(key, exptime)requires: resty.core.shdict or resty.core
更新 key 的过期时间单位秒,精度0.001,0代表永不过期。如果 key 不存在返回 nil,err为 "not found"。
ngx.shared.DICT.flush_all
syntax: ngx.shared.DICT:flush_all()
标记所有的key为过期。
ngx.shared.DICT.flush_expired
syntax: flushed = ngx.shared.DICT:flush_expired(max_count?)
将过期的key删除并释放内存,max_count 限制删除的数量,默认为0,代表不限制。
ngx.shared.DICT.get_keys
syntax: keys = ngx.shared.DICT:get_keys(max_count?)
从字典里获取key,默认只会返回前 1024 个,如果指定了 max_count,那么返回 max_count 个,如果 max_count 为0,返回全部。
注意:因为该操作会锁住共享内存,如果 key 很多不建议使用该函数,因为会让需要用到该共享内存的 worker 进程阻塞。
ngx.shared.DICT.capacity
syntax: capacity_bytes = ngx.shared.DICT:capacity()requires: resty.core.shdict or resty.core
返回共享内存的容量(由 lua_shared_dict 指定),单位字节。
ngx.shared.DICT.free_space
syntax: free_page_bytes = ngx.shared.DICT:free_space()requires: resty.core.shdict or resty.core
获取共享内存的空闲空间,由于内部 nginx 是用 slab 分配器分配的内存,返回0可能还是有空间来存储新的值。
简单的例子
lua_shared_dict dogs 10m;server {location /set {content_by_lua_block {local dogs = ngx.shared.dogsdogs:set("Jim", 8, 100)dogs:lpush("list_1", 3)dogs:lpush("list_1", 2)dogs:lpush("list_1", 1)dogs:rpush("list_1", 4)dogs:incr("Jim", 4)}}location /get {content_by_lua_block {require "resty.core"local dogs = ngx.shared.dogslocal ttl,err = dogs:ttl("Jim")local l = dogs:get("list_1")local val1,err = dogs:lpop("list_1")local val2,err = dogs:lpop("list_1")ngx.say("ttl:",ttl)ngx.say("get list type:",type(l))ngx.say("lpop1:", val1)ngx.say("lpop2:", val2)}}}
[root@192 ~]# curl localhost/set
[root@192 ~]# curl localhost/get
ttl:98.44
get list type:nil
lpop1:1
lpop2:2