OpenResty 从入门到实战
温铭
OpenResty 软件基金会第一任主席,Apache APISIX 项目 VP
20903 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 52 讲
结束语 (1讲)
OpenResty 从入门到实战
15
15
1.0x
00:00/00:00
登录|注册

18 | worker间的通信法宝:最重要的数据结构之shared dict

free_space
capacity
get_keys
llen
lpop/rpop
lpush/rpush
delete
get_stale
get
replace
safe_add
add
safe_set
set
管理类
队列操作类
字典读写类
支持原子计数和队列操作
支持数据存放和读取
shared dict的API
shared dict
模块级别的变量
ngx.ctx
Nginx中的变量
共享内存字典shared dict
Lua中的table
共享字典
数据共享的几种方式
数据结构
worker间的通信法宝:最重要的数据结构之shared dict

该思维导图由 AI 生成,仅供参考

你好,我是温铭。
前面我们讲过,在 Lua 中, table 是唯一的数据结构。与之对应的一个事实是,共享内存字典 shared dict,是你在 OpenResty 编程中最为重要的数据结构。它不仅支持数据的存放和读取,还支持原子计数和队列操作。
基于 shared dict,你可以实现多个 worker 之间的缓存和通信,以及限流限速、流量统计等功能。你可以把 shared dict 当作简单的 Redis 来使用,只不过 shared dict 中的数据不能持久化,所以你存放在其中的数据,一定要考虑到丢失的情况。

数据共享的几种方式

在编写 OpenResty Lua 代码的过程中,你不可避免地会遇到,在一个请求的不同阶段、不同 worker 之间共享数据的情况,还可能需要在 Lua 和 C 代码之间共享数据。
所以,在正式介绍 shared dict 的 API 之前,先让我们了解一下,OpenResty 中常见的几种数据共享的方法;并学会根据实际情况,选择较为合适的数据共享方式。
第一种是 Nginx 中的变量。它可以在 Nginx C 模块之间共享数据,自然的,也可以在 C 模块和 OpenResty 提供的 lua-nginx-module 之间共享数据,比如下面这段代码:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

OpenResty中的shared dict是一种重要的数据结构,支持存储、读取、原子计数和队列操作。在编写OpenResty Lua代码时,常见的数据共享方式包括Nginx中的变量、ngx.ctx、模块级别的变量和shared dict。shared dict基于红黑树实现,性能良好,可以在多个worker之间共享数据。然而,它只能缓存字符串类型的数据,不支持复杂的Lua数据类型。共享字典提供了20多个Lua API,包括字典读写类、队列操作类和管理类,这些API都是原子操作。队列操作类提供了类似Redis的接口,包括lpush/rpush、lpop/rpop和llen等操作。管理类API是后续新增的功能,包括get_keys、capacity和free_space等,用于管理共享内存的使用情况。了解shared dict的API和官方文档对工程师和架构师至关重要,通过shared dict可以实现worker间的缓存和通信,以及限流限速、流量统计等功能。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《OpenResty 从入门到实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(17)

  • 最新
  • 精选
  • helloworld
    老师有两个问题请教: 1. ngx.var API是否是lua代码和Nginx C模块之间共享数据的唯一方法?有其他方法吗 2. 关于ngx.ctx,官方文档也提到说这个API 的查询也相对有点昂贵:"The ngx.ctx lookup requires relatively expensive metamethod calls and it is much slower than explicitly passing per-request data along by your own function arguments.",如果这样是否还能说ng.ctx速度很快呢 另外补充一个文中未提到的,ngx.var可以在子请求中有效,也就是说可以跨location使用,而ngx.ctx不可以。

    作者回复: 其他的方式就是借助外部的储存了,比如 memcached 扥。 ngx.ctx 的快是相对于 ngx.var 而言的。 多谢补充,ngx.ctx 确实不能跨 location,有一个库 lua-resty-ctxdump 可以解决这个问题。

    2019-07-24
    2
    2
  • HelloBug
    温铭老师,你好~有以下一些问题想请教一下~ 1.ngx.var变量的作用域在nginx C模块之间、nginx C和lua-nginx-module模块之间。这个不太理解,从请求的角度来看,是一个工作进程中的单个请求吗? 2.文中有描述ngx.ctx是一种昂贵的调用,是虽然ngx.ctx访问速度快,但ngx.ctx在每一个请求中都会占据内存空间的昂贵吗?还有一个问题,ngx.ctx占据的内存空间的大小是动态增长的还是有大小限制的呢? 3.操作模块内的变量时,如果两个操作之间有阻塞操作,可能出现竞争。如果两个操作之间没有阻塞操作,恰好CPU时间到,当前进程进入就绪队列,也可能产生竞争的对吧?

    作者回复: 1. 是的,ngx.var 的生命周期和请求一致,请求结束它也就消失了。它的优势是数据可以在 C 模块和 Lua 代码中传递。 2. 原文中没有提到 ngx.ctx 是昂贵的操作吧?可能是我没有表达清楚,是我们会用 ngx.ctx 来替代 ngx.var 这种昂贵的操作,后者才是昂贵的。 3. 两个操作之间有 `yield 操作`,可能出现竞争,而不是`阻塞操作`,有阻塞操作是不会出现竞争的。只要不把主动权交给 Nginx 的事件循环,就不会有竞争。

    2019-07-07
    2
    2
  • zyonline
    local ngx = ngx local function bar() ngx_ctx.host = 'test.com' end 中 ngx_ctx.host = 'test.com' 是不是应该是ngx.ctx.host = 'test.com'

    作者回复: 是的,本来想写的是: local ngx_ctx = ngx.ctx

    2019-07-05
    4
    2
  • 阳光梦
    local ngx_ctx = ngx.ctx local function bar() ngx_ctx.host = 'test.com' end 上面内容放在access.lua中,比如access阶段包含此文件。为何不能执行第一句?

    作者回复: 有什么报错吗?ngx.ctx 是可以在 access 阶段使用的

    2019-07-05
    3
    2
  • helloworld
    老师,还有一个问题,local ngx_ctx = ngx.ctx 这个API缓存语句不能用于模块级别,要用于函数级别,那么local ngx_var = ngx.var 这个是不是也是同样的?

    作者回复: 是的

    2019-07-24
    1
  • manatee
    想请问老师现在很多基于插件来实现的网关里面的插件配置是怎么保存在or里的呢?是sharedict吗

    作者回复: 有些是共享字典,有些是 lrucache

    2019-07-05
    1
  • zhang
    老师,luajit最大可用内存是2g,在openrety中如何将它提高?

    作者回复: 1.15.8 这个最新版本中,默认已经提高了,支持了 64 位

    2019-07-05
    2
    1
  • Rye
    请教老师个问题,我想在请求返回给各户端前 拿到上游服务器的IP和端口,也就是upstream的具体哪个节点负责处理的请求,有什么办法么?我在 ngx.balancer 里看大部分是set的方法。

    作者回复: 你在 ngx.balancer 的 set_current_peer 时,保存下上游的节点信息?

    2019-07-05
    2
  • HelloBug
    关于ngx.ctx不要做模块级别的缓存,我做了以下的测试。 myctx.lua文件: local _M = {} local ngx_ctx = ngx.ctx function _M.bar() ngx_ctx.host = 'test.com' end return _M nginx.conf文件: worker_processes 1; server { listen 9999; location ~/(?<myurl>.*) { content_by_lua_block { local myctx = require "myctx" if ngx.var.myurl == "1" then ngx.say(ngx.ctx.host) ngx.ctx.host = "who" ngx.say(ngx.ctx.host) myctx.bar() ngx.say(ngx.ctx.host) else ngx.say(ngx.ctx.host) end } } } 测试结果如下: [root@localhost nginx]# curl localhost:9999/1 nil who test.com [root@localhost nginx]# curl localhost:9999/1 nil who who [root@localhost nginx]# curl localhost:9999/1 nil who who [root@localhost nginx]# curl localhost:9999/2 nil [root@localhost nginx]# curl localhost:9999/2 nil 第一个请求可以理解,第二、第三个请求证明不要做模块级别的缓存。但是为什么这样呢?即使此时ngx_ctx保留的是第一个请求的ngx.ctx,为什么再次设置变量的值ngx_ctx.host就不管用了呢?
    2019-07-07
    2
    2
  • Geek_xiaoer
    老师您好,`yield`有可能导致race。哪些API(比如ngx.sleep)或哪些操作(比如访问redis)会yield?请问这个有参考文档,或者整理吗,谢谢!
    2021-05-14
    1
收起评论
显示
设置
留言
17
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部