OpenResty从入门到实战
温铭
OpenResty软件基金会主席,《OpenResty 最佳实践》作者
立即订阅
4332 人已学习
课程目录
已完结 52 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | OpenResty,为你打开高性能开发的大门
免费
入门篇 (14讲)
01 | 初探OpenResty的三大特性
02 | 如何写出你的“hello world”?
03 | 揪出隐藏在背后的那些子项目
04 | 如何管理第三方包?从包管理工具luarocks和opm说起
05 | [视频]opm项目导读
06 | OpenResty 中用到的 NGINX 知识
07 | 带你快速上手 Lua
08 | LuaJIT分支和标准Lua有什么不同?
09 | 为什么 lua-resty-core 性能更高一些?
10 | JIT编译器的死穴:为什么要避免使用 NYI ?
11 | 剖析Lua唯一的数据结构table和metatable特性
12 | 高手秘诀:识别Lua的独有概念和坑
13 | [视频]实战:基于FFI实现的lua-resty-lrucache
14 | 答疑(一):Lua 规则和 NGINX 配置文件产生冲突怎么办?
API篇 (11讲)
15 | OpenResty 和别的开发平台有什么不同?
16 | 秒杀大多数开发问题的两个利器:文档和测试案例
17 | 为什么能成为更好的Web服务器?动态处理请求和响应是关键
18 | worker间的通信法宝:最重要的数据结构之shared dict
19 | OpenResty 的核心和精髓:cosocket
20 | 超越 Web 服务器:特权进程和定时任务
21 | 带你玩转时间、正则表达式等常用API
22 | [视频]从一个安全漏洞说起,探寻API性能和安全的平衡
23 | [视频]导读lua-resty-requests:优秀的lua-resty-*是如何编写的?
24 | 实战:处理四层流量,实现Memcached Server
25 | 答疑(二):特权进程的权限到底是什么?
测试篇 (5讲)
26 | 代码贡献者的拦路虎:test::nginx 简介
27 | test::nginx 包罗万象的测试方法
28 | test::nginx 还可以这样用?
29 | 最容易失准的性能测试?你需要压测工具界的“悍马”wrk
30 | 答疑(三)如何搭建测试的网络结构?
性能优化篇 (16讲)
31 | 性能下降10倍的真凶:阻塞函数
32 | 让人又恨又爱的字符串操作
33 | 性能提升10倍的秘诀:必须用好 table
34 | 特别放送:OpenResty编码指南
35 | [视频]实际项目中的性能优化:ingress-nginx中的几个PR解读
36 | 盘点OpenResty的各种调试手段
37 | systemtap-toolkit和stapxx:如何用数据搞定“疑难杂症”?
38 | [视频]巧用wrk和火焰图,科学定位性能瓶颈
39 | 高性能的关键:shared dict 缓存和 lru 缓存
40 | 缓存与风暴并存,谁说缓存风暴不可避免?
41 | lua-resty-* 封装,让你远离多级缓存之痛
42 | 如何应对突发流量:漏桶和令牌桶的概念
43 | 灵活实现动态限流限速,其实没有那么难
44 | OpenResty 的杀手锏:动态
45 | 不得不提的能力外延:OpenResty常用的第三方库
46 | 答疑(四):共享字典的缓存是必须的吗?
API网关篇 (4讲)
47 | 微服务API网关搭建三步曲(一)
48 | 微服务API网关搭建三步曲(二)
49 | 微服务API网关搭建三步曲(三)
50 | 答疑(五):如何在工作中引入 OpenResty?
结束语 (1讲)
结束语 | 行百里者半九十
OpenResty从入门到实战
登录|注册

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

温铭 2019-07-05
你好,我是温铭。
前面我们讲过,在 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/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《OpenResty从入门到实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(12)

  • 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
    2
    1
  • 阳光梦
    local ngx_ctx = ngx.ctx

    local function bar()
        ngx_ctx.host = 'test.com'
    end

    上面内容放在access.lua中,比如access阶段包含此文件。为何不能执行第一句?

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

    2019-07-05
    1
    1
  • 姚坤
    对于模块级别变量的测试,没有能够模拟出老师说的 race condition 状况。
    请老师解答
    同时在浏览器打开两个页面请求 localhost/testmodule
    多次刷新,每个页面都是稳定输出 4,5

    模块文件如下 m.lua:

    local _M = {}
    local color = {
          red = 1,
          blue = 2,
          green = 3,
    }
    _M.incGreen = function()
    color["green"] = color["green"] + 1
    return color["green"]
    end
    _M.getGreen = function()
    return color["green"]
    end
    return _M
    ----------------
    config 配置如下
    location /testmodule {
         content_by_lua_block {
            local m = require "m"
    ngx.say(m.incGreen())
    ngx.sleep(5)
    ngx.say(m.incGreen())
         }
    }
    2019-09-21
    1
  • helloworld
    老师,还有一个问题,local ngx_ctx = ngx.ctx 这个API缓存语句不能用于模块级别,要用于函数级别,那么local ngx_var = ngx.var 这个是不是也是同样的?

    作者回复: 是的

    2019-07-24
  • 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
  • 回家
    关于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
    1
  • 回家
    温铭老师,你好~有以下一些问题想请教一下~
    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
  • manatee
    想请问老师现在很多基于插件来实现的网关里面的插件配置是怎么保存在or里的呢?是sharedict吗

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

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

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

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

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

    2019-07-05
    1
  • Seven
    如果用Dict做一个pub/sub, 由于数据不能持久化,那为了在teload或重启时保证数据完整,是不是必须自己做ACK机制证实每个客户端都收到了每一条数据?
    2019-07-05
    1
收起评论
12
返回
顶部