美文网首页
Kong 插件开发 - 缓存自定义实体

Kong 插件开发 - 缓存自定义实体

作者: 重剑无锋_Augustine | 来源:发表于2017-02-14 16:22 被阅读1802次

    缓存自定义实体

    如果有自定义的实体,可以通过 database_cache这个依赖把实体缓存到内存中。

    local cache = require "kong.tools.database_cache"
    

    这个模块暴露出来的方法如下:

    • ok, err = cache.set(key, value)

      根据指定的key把一个lua对象缓存到内存中。这个value可以是任何lua的数据类型,包括table。返回true或者 false和操作错误引起的 err。

    • value = cache.get(key)

      根据制定的 key 从被存储的 Lua 对象中检索

    • cache.delete(key)

      根据指定的 key 删除缓存的对象

    • newvalue, err = cache.incr(key, amount)

      将指定键中存储的数字增加指定的单位数量。该数字需要已经存在于缓存中,否则将返回错误。如果成功,则返回新的递增值,否则返回错误

    • value = cache.get_or_set(key, function)

      这是一个实用方法,它使用指定的键检索对象,但是如果对象是nil,那么将执行传递的函数,它的返回值将用于存储指定键的对象。这有效地确保对象仅从数据存储加载一次,因为每个其他调用将从内存中高速缓存加载对象

    回过头来看我们authentication插件这个例子,用一个指定的api-key 来检索证书,写的代码如下:

    -- access.lua
    
    local credential
    
    -- Retrieve the apikey from the request querystring
    local apikey = request.get_uri_args().apikey
    if apikey then -- If the apikey has been passed, we can check if it exists
    
      -- We are using cache.get_or_set to first check if the apikey has been already stored
      -- into the in-memory cache at the key: "apikeys."..apikey
      -- If it's not, then we lookup the datastore and return the credential object. Internally
      -- cache.get_or_set will save the value in-memory, and then return the credential.
      credential = cache.get_or_set("apikeys."..apikey, function()
        local apikeys, err = dao.apikeys:find_by_keys({key = apikey}) -- Lookup in the datastore
        if err then
          return responses.send_HTTP_INTERNAL_SERVER_ERROR(err)
        elseif #apikeys == 1 then
          return apikeys[1] -- Return the credential (this will be also stored in-memory)
        end
      end)
    end
    
    if not credential then -- If the credential couldn't be found, show an error message
      return responses.send_HTTP_FORBIDDEN("Invalid authentication credentials")
    end
    

    这样做之后,不管客户端用特定的 api-key 进行多少次请求,从第一次请求之后的每次搜索都会在内存中进行,不需要再查询数据库。

    更新或者删除一个自定义的实体

    每当在数据存储上更新或删除缓存自定义实体时,例如使用Admin API,它都会在数据存储区中的数据与在Kong节点内存中缓存的数据之间产生不一致。为了避免这种不一致,我们需要从内存存储中删除缓存的实体,并强制Kong从数据存储中再次请求它。为了这样做,我们必须实现一个处理无效钩子

    无效的自定义实体

    当一个实体被创建、更新或者删除的时候,Kong 会把这些操作通知到所有的nodes,并告知执行了哪些命令或者哪些实体会受到影响。这些不仅针对 APIs\Plugins\Consumers,而且也对自定义的实体有效。

    感谢这一特性,我们可以监听这些事件和根据一些适当的动作做出响应,当被缓存的实体在数据库中改动之后,我们可以明确的从缓存中移除它,避免数据库与缓存中数据不一直的问题。从内存中移除缓存之后,会触发再次请求数据库,重新缓存实体。

    Kong 传播的事件如下:

    event name description
    ENTITY_CREATED When any entity is being created.
    ENTITY_UPDATED When any entity is being updated.
    ENTITY_DELETED When any entity is being deleted.

    为了监听这些事件,我们需要实现 hooks.lua 文件,并且在我们的插件中发布它,例如:

    -- hooks.lua
    
    local events = require "kong.core.events"
    local cache = require "kong.tools.database_cache"
    
    local function invalidate_on_update(message_t)
      if message_t.collection == "apikeys" then
        cache.delete("apikeys."..message_t.old_entity.apikey)
      end
    end
    
    local function invalidate_on_create(message_t)
      if message_t.collection == "apikeys" then
        cache.delete("apikeys."..message_t.entity.apikey)
      end
    end
    
    return {
      [events.TYPES.ENTITY_UPDATED] = function(message_t)
        invalidate_on_update(message_t)
      end,
      [events.TYPES.ENTITY_DELETED] = function(message_t)
        invalidate_on_create(message_t)
      end
    }
    

    在上边这个例子中,我们监听了 ENTITY_UPDATED 和 ENTITY_DELETED 两个事件,并且调用了适当的方法来相应。其中,message_t table 包含的事件属性如下:

    property name type description
    collection String 受操作影响的数据存储的集合
    entity Table 最近更新、删除或者创建的实体
    old_entity Table 只对更新事件有效,更新之前的实体

    这里 entity 和 old_entity的属性,并不是 schema 中定义的所有属性,而是一个子集。这个情况是因为每个事件是通过 UDP 包发送的,受到 512 bytes的大小限制。这个子集是通过 schema 中的 marshall_event 函数来返回的。

    marshall_event

    这个函数序列化了在hooks.lua中用到的最小版本需要的字段的实体。如果这个函数没有被实现,默认 Kong 不会发送任何实体的字段。

    例如:

    -- daos.lua
    
    local SCHEMA = {
      primary_key = {"id"},
      -- clustering_key = {}, -- none for this entity
      fields = {
        id = {type = "id", dao_insert_value = true},
        created_at = {type = "timestamp", dao_insert_value = true},
        consumer_id = {type = "id", required = true, queryable = true, foreign = "consumers:id"},
        apikey = {type = "string", required = false, unique = true, queryable = true}
      },
      marshall_event = function(self, t) -- This is related to the invalidation hook
        return { id = t.id, consumer_id = t.consumer_id, apikey = t.apikey }
      end
    }
    

    上边这个例子在自定义的实体中提供了 marshall_event,返回一个包含 id, consumer_id和 apikey的对象。在我们的hooks中不需要 creation_date来无效这个实体,所以我们不关心在事件中传播它。参数 t table是一个包含全部字段的原对象。

    注意:这个lua table的 json 序列化串被返回的时候不能大于512 bytes, 以保证整个时间在一个 UDP 包中。

    相关文章

      网友评论

          本文标题:Kong 插件开发 - 缓存自定义实体

          本文链接:https://www.haomeiwen.com/subject/dhaxwttx.html