OpenResty 使用示例

发布时间: 更新时间: 总字数:1007 阅读时间:3m 作者: IP属地: 分享 复制网址

OpenResty 使用示例

protocal 转换

  • 示例
location ~ ^/api/([-_a-zA-Z0-9/]+).json {
    access_by_lua_file  /path/to/lua/api/protocal_decode.lua;
    content_by_lua_file /path/to/lua/api/$1.lua;
    body_filter_by_lua_file  /path/to/lua/api/protocal_encode.lua;
}
  • API 设计,添加自己 的 HTTP api 规范地址
    location ~ ^/app/([-_a-zA-Z0-9/]+) {
        set $path $1;
        content_by_lua_file /path/to/lua/app/root/$path.lua;
    }

实现 IP 限制

init_by_lua_block {
  local iputils = require("resty.iputils")
  iputils.enable_lrucache()
  local whitelist_ips = {
      "127.0.0.1",
      "10.10.10.0/24",
      "192.168.0.0/16",
  }

  -- WARNING: Global variable, recommend this is cached at the module level
  -- https://github.com/openresty/lua-nginx-module#data-sharing-within-an-nginx-worker
  whitelist = iputils.parse_cidrs(whitelist_ips)
}

access_by_lua_block {
    local iputils = require("resty.iputils")
    if not iputils.ip_in_cidrs(ngx.var.remote_addr, whitelist) then
      return ngx.exit(ngx.HTTP_FORBIDDEN)
    end
}
access_by_lua_block {
    if ngx.var.remote_addr == "1.1.1.1" then
        local headers = ngx.req.get_headers()
        for k,v in pairs(headers) do
            ngx.log(ngx.ERR , "header ", k, ": ", v)
        end
        ngx.log(ngx.ERR , "request_body: ", ngx.var.request_body)
    end
}

代理其他API

使用 http 客户端代理 http 请求

  • 安装
opm install pintsized/lua-resty-http
  • 示例代码
    location /test {
        content_by_lua_block {
            ngx.req.read_body()
            local args, err = ngx.req.get_uri_args()

            local http = require "resty.http"
            local httpc = http.new()
            httpc:set_timeout(1000)  -- 1000ms
            -- https://docs.github.com/zh/rest/guides/getting-started-with-the-rest-api?apiVersion=2022-11-28&tool=curl#using-body-parameters
            local res, err = httpc:request_uri("https://api.github.com/repos/octocat/Spoon-Knife/issues", {
                method = "POST",
                body = args.data,
                headers = {
                    ["Accept"] = "Accept",
                    ["Authorization"] = "Bearer YOUR-TOKEN",
                }
            })

            if 200 ~= res.status then
                ngx.exit(res.status)
            end

            if args.key == res.body then
                ngx.say("valid request")
            else
                ngx.say("invalid request")
            end
        }
    }

快速响应终端

ngx.say("xxx")
ngx.eof()
- 下面处理其它逻辑

限速

限速分以下几种

  • limit_rate 限制响应速度
  • limit_conn 限制连接数
  • limit_req 限制请求数

限制响应速度

access_by_lua_block {
    -- 设定当前请求的响应上限是 每秒 300K 字节
    ngx.var.limit_rate = "300K"
}

限制连接数和请求数

需要使用 openresty/lua-resty-limit-traffic 库,用于限制和控制OpenResty流量,包括请求、连接数、流量

  • nginx.conf
lua_code_cache on;
# 注意 limit_conn_store 的大小需要足够放置限流所需的键值。
# 每个 $binary_remote_addr 大小不会超过 16 字节(IPv6 情况下),算上 lua_shared_dict 的节点大小,总共不到 64 字节。
# 100M 可以放 1.6M 个键值对
lua_shared_dict limit_conn_store 100M;
server {
    listen 8080;
    location / {
        access_by_lua_file src/access.lua;
        content_by_lua_file src/content.lua;
        log_by_lua_file src/log.lua;
    }
}
  • utils/limit_conn.lua
local limit_conn = require "resty.limit.conn"

-- new 的第四个参数用于估算每个请求会维持多长时间,以便于应用漏桶算法
local limit, limit_err = limit_conn.new("limit_conn_store", 10, 2, 0.05)
if not limit then
    error("failed to instantiate a resty.limit.conn object: ", limit_err)
end

local _M = {}

function _M.incoming()
    local key = ngx.var.binary_remote_addr
    local delay, err = limit:incoming(key, true)
    if not delay then
        if err == "rejected" then
            return ngx.exit(503)
        end
        ngx.log(ngx.ERR, "failed to limit req: ", err)
        return ngx.exit(500)
    end

    if limit:is_committed() then
        local ctx = ngx.ctx
        ctx.limit_conn_key = key
        ctx.limit_conn_delay = delay
    end

    if delay >= 0.001 then
        ngx.log(ngx.WARN, "delaying conn, excess ", delay,
                "s per binary_remote_addr by limit_conn_store")
        ngx.sleep(delay)
    end
end

function _M.leaving()
    local ctx = ngx.ctx
    local key = ctx.limit_conn_key
    if key then
        local latency = tonumber(ngx.var.request_time) - ctx.limit_conn_delay
        local conn, err = limit:leaving(key, latency)
        if not conn then
            ngx.log(ngx.ERR,
            "failed to record the connection leaving ",
            "request: ", err)
        end
    end
end

return _M
  • src/access.lua
local limit_conn = require "utils.limit_conn"


-- 对于内部重定向或子请求,不进行限制。因为这些并不是真正对外的请求。
if ngx.req.is_internal() then
    return
end
limit_conn.incoming()
  • src/log.lua
local limit_conn = require "utils.limit_conn"
limit_conn.leaving()

域名解析

  • 使用 resolver 1.1.1.1 223.5.5.5 valid=600s;
  • resty.dns.resolver

安装 lua-casbin

/usr/local/openresty/luajit/bin/luarocks install lrexlib-pcre PCRE_DIR=/usr/local/openresty/pcre
GIT_SSL_NO_VERIFY=1 /usr/local/openresty/luajit/bin/luarocks install casbin

代理&cache vscode 源

  • cache-update-visualstudio.lua
-- from https://github.com/xiexianbin/openresty-demo/blob/master/lua/cache-update-visualstudio.lua
-- vscode node_server 加速实现
-- 获取数据,原始数据在 https://update.code.visualstudio.com/commit:<ID>/server-linux-x64/stable
-- 若存在缓存,返回缓存,否则去国内cdn地址 https://vscode.cdn.azure.cn/stable/<commit-id>/<vscode-server-linux-x64.tar.gz> 获取,并缓存
-- opm install ledgetech/lua-resty-http
-- mkdir -p /data/vscode-server
-- chmod -R 777 /data/vscode-server
-- ref https://www.xiexianbin.cn/software/dev/vscode/index.html

local http = require "resty.http"
local ngx = require("ngx")

local cache_dir = "/data/vscode-server"

-- download file from cdn and cache it and response to client
local function download_file(commit_id, file_dir, file_name)
    local url = "https://vscode.cdn.azure.cn/stable/" .. commit_id .. "/vscode-server-linux-x64.tar.gz"
    ngx.log(ngx.ERR, "cdn url is " .. url)

    -- create dir
    os.execute('whoami && mkdir -p ' .. file_dir)

    -- http client
    local httpc = http.new()
    httpc:set_timeout(10000)
    local res, err = httpc:request_uri(url, {
        method = "GET",
        ssl_verify = false,
    })

    -- cache file to local
    local file_path = file_dir .. "/" .. file_name
    if res and res.status == 200 then
        local file = io.open(file_path, "w")
        file:write(res.body)
        file:close()

    -- 获取失败
    else
        ngx.log(ngx.ERR, "Failed to download file: ", err)
    end

    httpc:close()
end

local function main(commit_id)

  local file_dir = cache_dir .. "/" .. commit_id .. "/"
  local file_name = "vscode-server-linux-x64.tar.gz"
  local file_path = file_dir .. "/" .. file_name
  local file = io.open(file_path, "r")
  if file then
      ngx.say(file:read("*all"))
      file:close()
  else
      download_file(commit_id, file_dir, file_name)
      ngx.exec(ngx.var.uri)
  end
end

local request_uri = ngx.var.uri
ngx.log(ngx.ERR, "request uri: ", request_uri)
local commit_id = string.sub(ngx.var.uri, 9, 48)
ngx.log(ngx.ERR, "commit_id is: ", commit_id)

main(commit_id)
  • update.code.visualstudio.com.conf
server {
    listen 8002;
    server_name update.code.visualstudio.com;
    resolver 8.8.8.8;

    access_log logs/update.code.visualstudio.com_access.log debug;
    error_log logs/update.code.visualstudio.com_error.log;

    # curl http://update.code.visualstudio.com:8002/commit:3c4e3df9e89829dce27b7b5c24508306b151f30d/server-linux-x64/stable
    location ^~ /commit: {
        content_by_lua_file lua/cache-update-visualstudio.lua;
    }

    location / {
        proxy_pass https://update.code.visualstudio.com;
    }
}
Home Archives Categories Tags Statistics
本文总阅读量 次 本站总访问量 次 本站总访客数