Yukang's Page

Kong源码分析: 缓存

2017-07-22

Nginx里的缓存使用

在Kong里面我们缓存的内容大部分是配置,不管是API本身的配置还是插件相关的配置,缓存之后就存储在内存中。

Kong里的缓存基础代码在tools/database_cache.lua文件里面。这里又分两种类型的缓存,一种是shared dict, 一种是使用
lua-resty-lrucache。这两者之间是有区别的: shared dict如同其名字一样是Nginx worker之间共享的,而lrucache是worker级别的,内存空间在Lua VM里由GC管理,不能在进程之间共享,自然也不会在Nginx worker之间共享。

具体我们开发中使用哪一种由具体场景分析,比如在Kong的插件rate-limiting里就使用了共享缓存,因为我们需要针对一个Nginx所有的worker做请求数统计。

share dict最常规的使用方法是:

http {
lua_shared_dict dogs 10m;
server {
location /set {
content_by_lua_block {
local dogs = ngx.shared.dogs
dogs:set("Jim", 8)
ngx.say("STORED")
}
}
location /get {
content_by_lua_block {
local dogs = ngx.shared.dogs
ngx.say(dogs:get("Jim"))
}
}
}
}

lrucache的使用方法如文档所示

Kong里的多级缓存实现

有了上面的了解,看database_cache.lua这个文件就比较直观了,这里Kong会分多类缓存: apis, consumers, plugins等。具体这样分是因为如果我们对配置做了修改,需要发出serf消息来指名这次改动涉及到哪些,其他Kong节点收到消息后自然只更新对应的缓存部分。所以Kong里申明了一个列表CACHE_KEYS来存要缓存的数据类别,同时写了不少生成缓存key的方法,比如: api_key,plugin_key等。

仔细查看database_cache.lua,我们发现其实这里是做了两级缓存。Kong要从缓存里取出一个key/value,首先从lrucache里取,如果有则返回。如果没有则从share dict里去取,如果取到则deserialize然后存储在lrucache里,然后返回。如果shared dict里也没有,则返回nil。标准的两级缓存流程,这样做的好处在于减少deserialize的次数,而且shared dict可能被多个worker同时修改,要修改的时候需要加互斥锁。

这里最常用的方法是get_or_set,尝试获取一个key的值,如果没有就执行对应的callback,返回结果当做value设置到缓存里,并把value作为最后的返回结果。这里的callback函数通常做的当然是从数据库里读取内容。

如何避免缓存失效风暴

我们在实现缓存的时候缓存失效风暴问题需要谨慎考虑。agentzh在这里详细描述了加锁解决的策略ngx.shcache这里也使用了相同的方法,具体可以好好研究一下那个图。

主要注意的是在加锁后,再尝试去读取一次key,因为可能在加锁之前其他worker刚好把数据更新到了缓存里。

Tags: Lua
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章