• 缓存利器、Lua模块下的共享内存


    上一节讲到了worker进程的共享内存,它利用丰富的指令使数据的缓存操作变得非常简单,但它也存在一些缺点。

    1.worker进程之间会有锁竞争,在高并发的情况下会增加性能开销。
    2.只支持Lua布尔值、数字、字符串和nil类型的数据,无法支持table类型的数据。
    3.在读取数据时有反序列化操作,会增加CPU开销。

    共享内存在Ngx_Lua中作为缓存工具还是非常出色的。笔者在生产环境中,曾多次使用lua_shared_dict的各种特性,并未感受到存在明显的性能问题。但如果读者还是介意这些缺点或需要缓存更复杂的数据的话,可以使用lua-resty-lrucache。

    10.2.1 安装lua-resty-lrucache

    lua-resty-lrucache是基于Ngx_Lua的缓存利器,它拥有如下优点。

    1.支持更丰富的数据类型,可以把table存放在value中,这对数据结构复杂的业务非常有用。

    2.可以预先分配key的数量,不用设置固定的内存空间,在内存的使用上更为灵活。

    3.每个worker进程独立缓存,所以当worker进程同时读取同一个key 时不存在锁竞争。

    但它与lua_shared_dict相比也有一些缺点。

    1.因为数据不在worker之间共享,所以无法保证在更新数据时,数据在同一时间的不同worker进程上完全一致。

    2.虽然可以支持复杂的数据结构,但可使用的指令却很少,如不支持消息队列功能。

    3.重载Nginx配置时,缓存数据会丢失。如果使用lua_shared_dict,则不会如此。

    有利就有弊,读者在使用时可以根据自身需求进行选择。lua-resty-lrucache的安装方式和其他的lua-resty模块一样,如下所示:

    # git clone https://github.com/openresty/lua-resty-lrucache.git
    # cp -r lua-resty-lrucache/lib/resty/lrucache* 
     /usr/local/nginx_1.12.2/conf/lua_modules/resty/

    10.2.2 使用lua-resty-lrucache进行缓存的方法

    通过下面的例子来了解一下lua-resty-lrucache的使用方式,首先需要对模块进行加载,方法如下:
    local lrucache = require "resty.lrucache"
    local lrucache = require "resty.lrucache.pureffi"
    读者在加载lua-resty-lrucache时,需要把上面的2个文件复制到lua_package_path所设置的路径上。它们的作用是一样的,但性能有所区别:resty.lrucache适合用来缓存命中率高或读操作远远大于写操作的缓存业务;resty.lrucache.pureffi适合用来缓存命中率低或需要对key进行频繁增、删操作的缓存业务。请根据业务需求进行选择。
    然后,将下面的代码写入test_m.lua中,并将此文件放到lua_package_path的路径下,代码如下:

    local _M = {}
    local lrucache = require "resty.lrucache"
    --在缓存上声明1个1000个key的列表
    local cache, err = lrucache.new(1000)
    if not cache then
        return error("failed to create the cache: " .. (err or "unknown"))
    end
    --此函数用来往缓存中存储key/value的值
    local function mem_set()
        --set()中的内容从左到右顺序依次是key、value、有效期(2s)
        cache:set("a", 19, 2)
        cache:set("b", {"1","2","3"},0.001)  --支持插入table类型的数据
        return
    end
    --此函数用来获取缓存里的value。 a即value的值,如果a为nil,则表示value不存在或已过期;如果stale_data有值,也说明value已过期
    local function mem_get(key)
        local a,stale_data = cache:get(key)
        return a,stale_data
    end
    function _M. fromcache ()
        --获取a的值
    local a,stale_data  =  mem_get("a")
        --如果a存在,就输出a的值
        if a then
           ngx.say("a: ", a)
        --如果a不存在且stale_data有值,就输出过期的value,并重新执行存储操作,然后再次输出value
        elseif stale_data then
           ngx.say("a 已经过期: " , stale_data)
           mem_set()
           local a_again  =  mem_get("a")
           ngx.say("a: ", a_again )
        --如果a 和 stale_data都不存在,则执行存储操作后再输出value
        else
           ngx.say("no found a")
           mem_set()
           local a_again  =  mem_get("a")
           ngx.say("a: ", a_again )
    
        end
    end
    return _M
    修改nginx.conf文件,代码如下:
    location / {
        content_by_lua_block {
            --加载模块,执行数据的读取操作
            require("test_m").fromcache()
        }
    }

    重载Nginx配置,执行结果如下:

    # curl   'http://testnginx.com/'
    no found a
    a: 19
    [root@testnginx ~]# curl   'http://testnginx.com/'
    a: 19
    [root@testnginx ~]# curl   'http://testnginx.com/'
    a: 19
    [root@testnginx ~]# curl   'http://testnginx.com/'
    a 已经过期: 19
    a: 19

    从执行结果可以看出:
    1.第1次请求,因为a没有值,所以先输出“no found a”,然后又执行了存储操作。

    2.第2次请求,因为有缓存值,直接输出value。

    3.第3次请求,仍然有缓存值,直接输出value。

    4.第4次请求,因为为缓存数据设置的有效期很短,此时已经过期,所以输出了过期的value,并再次执行存储操作,又输出了value。

    如果尝试重载Nginx配置,会发现每次重启(restart)后a都没有值,因为在重载配置的过程中,缓存数据会丢失。
    下面将对lua-resty-lrucache的常见指令进行说明。


    new
    语法:cache, err = lrucache.new(max_items [, load_factor])
    含义:创建1个缓存实例。如果创建失败会返回nil,并将错误信息返回给err。
    max_items用来声明缓存key的数量,从这个设置可以看出它虽然没有规定内存的使用大小,但规定了key的数量。
    load_factor参数是加载resty.lrucache.pureffi模块时才会用到的,它基于FFI(Foreign Function Interface,外部功能接口)的hash表的负载因子,值的区间在0.1~1之间,默认值是0.5。负载因子与hash数据的读取时间和对内存空间大小的权衡有关,有兴趣的读者可以自行查询相关信息。


    set
    语法:cache:set(key, value, ttl)
    含义:把key/value存储到缓存中。ttl是缓存的有效期,以秒为单位,默认值是0,表示不会过期;支持设置为0.001s。


    get
    语法:data, stale_data = cache:get(key)
    含义:获取指定key的值,如果key不存在或已过期,就返回nil;如果存在过期数据,过期的值会赋值给stale_data。


    delete
    语法:cache:delete(key)
    含义:从缓存中移除指定的key。


    flush_all
    语法:cache:flush_all(key)
    含义:刷新整个缓存区域的数据,等于清空内存中的数据。这种方式比创建新的缓存实例要快得多。

  • 相关阅读:
    LDAP个人理解
    webpack-dev-middleware 与 webpack-hot-middlware
    RFC、EMCA-262、TC-39等名词
    贝塞尔曲线
    Async/await语法糖实现(Generator)
    Promise嵌套问题/async await执行顺序
    JS对象中,在原型链上找到属性后 最终将值拷贝给原对象 而不是引用
    三列布局中 float引发的一个问题-当“非float的元素”和“float的元素”在一起的时候,如果非float元素在先,那么float的元素将受到排斥。
    05-Linux系统编程-第02天(文件系统、目录操作、dup2)
    降低30%视频码率,窄带高清技术实现揭秘
  • 原文地址:https://www.cnblogs.com/lgj8/p/12498031.html
Copyright © 2020-2023  润新知