日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

APISIX proxy-cache 插件用法

發布時間:2023/11/20 windows 41 coder
生活随笔 收集整理的這篇文章主要介紹了 APISIX proxy-cache 插件用法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

APISIX 的 proxy-cache 插件可以對上游的查詢進行緩存,這樣就不需要上游的應用服務自己實現緩存了,或者也能少實現一部分緩存,通用的交給插件來做。

下面的操作都是基于 APISIX 3.2 版本進行配置,關于 proxy-cache 的詳細配置的可以參考文檔:https://apisix.apache.org/docs/apisix/3.2/plugins/proxy-cache/ 不過文檔很多地方說的不是太清楚,這里把重點的地方補充一下,首先是插件的參數:

  1. cache_strategy 這個表示我們插件的緩存策略,支持配置 disk 或 memory,默認是 disk
  2. cache_zone 這個表示我們使用的存儲區域,對于內存或者磁盤都可以詳細配置,這個需要在配置文件中進行配置。
  3. cache_key 這個是我們要緩存請求的 key,key 是判斷是否緩存的依據,可以指定多個 APISIX 或者 nginx 的變量,也可以指定常量字符串。需要注意的是不是所有的變量都可以使用,比如 request_body 變量就是不能使用的,因為如果 body 太大,上下文傳遞會有比較大的開銷,所以設置了結果也是空的。
  4. cache_bypass? 這個指定不進行緩存的情況,也是一個數組,可以寫多個變量,如果至少有 1 個變量的不為空并且不等于 0,那么就會跳過緩存。這個配置不太好理解,具體是什么變量并沒有說,通過查看插件源碼發現取的是 ctx.var 中的值,所以其實這個并不是請求的 URL 參數,也不是請求的 Header 內容,而是 APISIX 里面的變量,當然也包括 nginx 的變量,當變量存在時就會自動繞過請求,如果內置變量不滿足要求,我們可以通過實現自定義變量來解決。
  5. cache_method 這個好理解,就是哪些方法會被緩存,可以指定一個數組。
  6. cache_http_status 這個表示上游的哪些狀態碼會被緩存,也是一個數組。
  7. hide_cache_headers? 如果設置為 true,會將響應的 Expires 和 Cache-Control 頭響應到客戶端中,默認是會去掉的。
  8. cache_control 如果設置為 true,將按照 HTTP 規范中的行為進行緩存,這個僅對于內存策略生效。
  9. no_cache? 這個和 cache_bypass 非常類似,同樣是配置一個變量列表,不過這個是在響應階段處理,也就是上游服務主動告訴 APISIX 這個請求是否緩存,變量的含義和上面一樣,支持內置變量和自定義變量。
  10. cache_ttl? 緩存的過期時間,單位是秒,當上面的 cache_control 未啟用或者服務器未返回緩存控制頭時生效,如果啟動了 cache_control 則以響應的控制頭為準,同樣這個僅對內存策略生效。

根據官網的說明,有下面的幾點需要注意:

  1. 如果是基于磁盤的緩存,無法在插件中設置過期時間,默認就是 10s,但是可以通過服務的響應頭 Expires 和 Cache-Control 設置過期時間。
  2. 如果上游服務不可用時,那么 APISIX 會返回 502 和 504 狀態碼,這個時候緩存時間是默認的 10s。
  3. 在 cache_key, cache_bypass 以及 no_cache 中指定的變量,如果變量值不存在,則結果為空字符串。如果其中寫了常量,結果會將變量值和常量一塊拼接起來。

開啟插件之前,首先需要在本地配置文件添加緩存區域的配置,否則啟用插件以及后續調用時會報錯,首先編輯 config.yaml 添加配置如下:

apisix:
    # ...
    proxy_cache:
        # 磁盤緩存時間 默認是 10s,可以在這里修改
        cache_ttl: 10s
        zones:
            # 磁盤的 cache_zone 的名稱
          - name: disk_cache_one
            # 索引需要在內存中存儲,設置內存的大小限制
            memory_size: 50m
            # 磁盤緩存的大小限制
            disk_size: 1G
            # 緩存文件的路徑
            disk_path: "/tmp/disk_cache_one"
            # 緩存級別配置
            cache_levels: "1:2"
            # 內存的 cache_zone 名稱
          - name: memory_cache_one
            # 內存緩存的大小限制
            memory_size: 512m

上面就分別配置了磁盤和內存的 cache_zone 當然可以配置多個,比如大小限制不一樣或者存儲路徑不一樣,針對于不同插件的配置。再比如我們這里只使用內存作為緩存,所以也可以不配置磁盤的。總之,插件中需要用到的配置,在配置文件中必須找得到才可以,修改好之后我們保存配置,然后重啟 APISIX 服務。

我們這里打算通過自定義一個 header 頭來判斷請求是否走緩存,由于變量在 APISIX 或 nginx 的內置變量中不存在,所以我們編寫一個自定義變量的插件來解決,插件內容如下:

--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements.  See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License.  You may obtain a copy of the License at
--
--     http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local ngx = ngx
local core = require("apisix.core")
local plugin = require("apisix.plugin")

local schema = {
    type = "object",
    properties = {
        name = {type = "string"},
	label = {type = "integer"}
    },
    required = {"name"},
}

local plugin_name = "custom-vars"

local _M = {
    version = 0.1,
    priority = 99,
    name = plugin_name,
    schema = schema,
}

defined_var_names = {"custom_username", "cache_bypass"}

core.ctx.register_var("custom_username", function(ctx)
    return get_custom_username()
end)

core.ctx.register_var("cache_bypass", function(ctx)
    local bypass = core.request.header(ctx, "cache-bypass")
    if not bypass then
        return ""
    end
    return bypass
end)

function get_custom_username()
    local req_headers = ngx.req.get_headers()
    local username = req_headers.user
    if username ~= "" then
        return username
    end
    return nil
end


function _M.check_schema(conf, schema_type)
    if schema_type == core.schema.TYPE_METADATA then
        return core.schema.check(metadata_schema, conf)
    end
    return core.schema.check(schema, conf)
end


function _M.init()
    -- call this function when plugin is loaded
    local attr = plugin.plugin_attr(plugin_name)
    if attr then
        core.log.info(plugin_name, " get plugin attr val: ", attr.val)
    end
end


function _M.destroy()
    -- call this function when plugin is unloaded
end

-- sorted phase:
-- rewrite -> access -> before_proxy -> header_filter -> body_filter -> delayed_body_filter -> log
function _M.rewrite(conf, ctx)
    -- core.log.warn("plugin rewrite phase, conf: ", core.json.encode(conf))
    -- core.log.warn("plugin rewrite phase, ctx: ", core.json.encode(ctx, true))
    -- core.log.warn("plugin rewrite phase, username: ", get_username())
end


function _M.access(conf, ctx)
    -- core.log.warn("plugin access phase, conf: ", core.json.encode(conf))
    -- core.log.warn("plugin access phase, ctx: ", core.json.encode(ctx, true))
    -- core.log.warn("plugin access phase, ngx headers: ", core.json.encode(ngx.req.get_headers()))
end

function _M.before_proxy(conf, ctx)
    -- After access and before the request goes upstream
end

function _M.header_filter(conf, ctx)
    -- Response header filter
end

function _M.body_filter(conf, ctx)
    -- Response body filter
end

function _M.delayed_body_filter(conf, ctx)
    -- delayed_body_filter is called after body_filter
    -- it is used by the tracing plugins to end the span right after body_filter
end

function _M.log(conf, ctx)
    -- Log processing after response
end

local function list_vars()
    local args = ngx.req.get_uri_args()
    if args["json"] then
        return 200, defined_var_names
    else
        return 200, table.concat(defined_var_names, "\n") .. "\n"
    end
end


function _M.control_api()
    return {
        {
            methods = {"GET"},
            uris = {"/v1/plugin/custom-vars"},
            handler = list_vars,
        }
    }
end

return _M

這里插件名稱我們叫 custom-vars,是專門注冊自定義變量的插件,我們注冊了 custom_usernamecache_bypass 這兩個變量,并且添加了 Control API,我們將源碼保存為 custom-vars.lua 并放到 APISIX 的 plugins 目錄下,然后在配置文件中添加插件,如果之前沒有添加過需要復制 config-default.yaml 中所有的插件,然后再補充上我們的插件。

具體如何加載插件可以參考之前寫過的插件開發的文章。

由于我們在插件中注冊了全局變量,只要插件被加載就可以,我們無需使用它也可以使用其中的自定義變量,但是假如我們要訪問插件的 Control API 那么則必須在某個路由上啟用插件。

使用專門的自定義變量插件的好處是我們不需要修改 proxy-cache 的源碼在里面注冊變量,這樣假如 APISIX 升級了并且 proxy-cache 的源碼有所變化我們也不需要再進行更新,只需要加入我們的 custom-vars 插件即可,對 APISIX 原有插件不會有任何影響,也是為了解耦。

加入插件后不要忘記重啟 APISIX,然后我們來添加一個路由:

curl -X PUT http://127.0.0.1:9180/apisix/admin/routes/100 \
    -H 'X-API-KEY: <api-key>' -d '
{
  "uri": "/hello",
  "name": "示例路由",
  "plugins": {
    "custom-vars": {
      "name": "vars"
    },
    "proxy-cache": {
      "cache_bypass": [
        "$cache_bypass"
      ],
      "cache_control": false,
      "cache_http_status": [
        200
      ],
      "cache_key": [
        "$uri",
        "-cache-id"
      ],
      "cache_method": [
        "GET",
        "PURGE" 
      ],
      "cache_strategy": "memory",
      "cache_ttl": 30,
      "cache_zone": "memory_cache_one",
      "hide_cache_headers": false
    }
  },
  "upstream": {
    "nodes": [
      {
        "host": "10.0.1.12",
        "port": 1980,
        "weight": 1
      }
    ],
    "type": "roundrobin",
    "hash_on": "vars",
    "scheme": "http",
    "pass_host": "pass"
  },
  "status": 1
}'

現在我們就添加了路由,然后我們訪問路由添加 -i 參數就可以看到 APISIX 響應的字段,比如:

curl localhost:9080/hello -i

第一次會看到 APISIX-Cache-Status: MISS 因為數據未緩存,然后再次請求就可以看到 APISIX-Cache-Status: HIT 表示緩存已經命中,同時會返回 Age 響應頭,表示當前緩存的存活時間,當時間超過 TTL 時,緩存就會被刪除。

然后我們也可以選擇不使用緩存,比如:

curl localhost:9080/hello -i -H 'Cache-Bypass: 1'

這時候我們會看到 APISIX-Cache-Status: BYPASS 表示沒有使用緩存,而是直接請求上游服務。

假如我們要緩存 POST 之類的請求,那么這個時候 $request_body 肯定也要作為 cache_key 的一部分,但是這個時候上下文中又沒有這個變量,那么怎么辦呢?可以換一種方式,由于 $request_body 本身可能比較大,我們可以使用它做一個 Hash,只要請求體內容不變,那么 Hash 結果也是確定的,而且緩存的 key 也比較小,由于同時有 $uri 進行區分,選用 md5 這樣的函數完全夠了,碰撞的概率也是極小的,我們可以在上面插件中注冊一個標識請求體的變量,比如:

core.ctx.register_var("request_body_uuid", function(ctx)
    local body = core.request.get_body()
    if not body then
        return ""
    end
    return ngx.md5(body)
end)

這樣我們就可以使用 $request_body_uuid 這樣的變量的,那么我們在創建路由的時候 cache_key 配置如下:

{
    "cache_key": [
       "$uri",
       "$request_body_uuid"
     ]
}

這樣就可以緩存 POST 請求了,如果要緩存帶參數的 GET 請求可以將 $uri 變量替換為 $request_uri 變量,后者是包含參數并且未規范化的。

最后我們還可以刪除路由的緩存,使用 HTTP 的 PURGE 方法發起請求:

curl localhost:9088/hello -X PURGE -i

如果成功刪除緩存會返回 200 OK,否則如果不存在緩存則會返回 404,但是前提路由配置中一定要允許 PURGE 方法,我們上面創建時就指定了,如果不指定則無法使用上面的命令刪除緩存,并且啟用了之后這個請求也是用于 APISIX 刪除緩存,并不會請求上游的服務。

另外下面會給出 OpenResty 中兩個 Lua 模塊的倉庫,其中有很多好用的函數可以參考,并且由于 APISIX 是基于 OpenResty 的,所以在 APISIX 插件開發中都是可用的。

Reference:

  1. APISIX 官方中文版文檔參考
  2. OpenResty 提供的 Lua 模塊
  3. OpenResty 的 Lua core API 參考

總結

以上是生活随笔為你收集整理的APISIX proxy-cache 插件用法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。