全书目录

第五十四篇:OpenResty 完全指南 —— 可编程网关锻造厂宇宙

五、怎样把它做成一个“可编程网关”

1 分钟 202 字 第 940 / 962 个阅读单元

网关不是“转发器”这么简单。它通常至少要做五件事:

  • 入口统一接入
  • 认证鉴权
  • 流量治理
  • 动态路由
  • 可观测性

OpenResty 的强项在于:这些能力你可以按自己业务把它们拼成一条插件链。

先看一个简化版可编程网关配置:

nginx
worker_processes auto;

events {
    worker_connections 8192;
}

http {
    lua_package_path "lua/?.lua;;";
    lua_shared_dict rate_store 20m;

    upstream backend_main {
        server 127.0.0.1:9001;
    }

    upstream backend_canary {
        server 127.0.0.1:9002;
    }

    server {
        listen 8080;
        set $target_upstream backend_main;

        location / {
            rewrite_by_lua_block {
                require("gateway.rewrite").run()
            }

            access_by_lua_block {
                require("gateway.access").run()
            }

            proxy_set_header X-Request-Id $request_id;
            proxy_set_header X-User-Id $http_x_user_id;
            proxy_pass http://$target_upstream;

            header_filter_by_lua_block {
                require("gateway.response").header()
            }

            log_by_lua_block {
                require("gateway.log").run()
            }
        }
    }
}

#gateway/rewrite.lua

lua
local _M = {}

function _M.run()
    local uri = ngx.var.uri
    if uri == "/ping" then
        ngx.req.set_uri("/health")
    end
end

return _M

#gateway/access.lua

lua
local _M = {}

local function unauthorized(msg)
    ngx.status = 401
    ngx.header["Content-Type"] = "application/json"
    ngx.say('{"error":"' .. msg .. '"}')
    return ngx.exit(401)
end

function _M.run()
    local headers = ngx.req.get_headers()
    local token = headers["authorization"]

    if token ~= "Bearer demo-token" then
        return unauthorized("unauthorized")
    end

    if headers["x-canary"] == "1" then
        ngx.var.target_upstream = "backend_canary"
    end
end

return _M

#gateway/response.lua

lua
local _M = {}

function _M.header()
    ngx.header["X-Gateway"] = "openresty"
end

return _M

#gateway/log.lua

lua
local _M = {}

function _M.run()
    ngx.log(
        ngx.NOTICE,
        "request_id=", ngx.var.request_id,
        " upstream=", ngx.var.target_upstream,
        " status=", ngx.status
    )
end

return _M

这个例子已经体现出网关的基本骨架:

text
rewrite   负责改写入口
access    负责放行决策
proxy     负责转发
filter    负责回程统一加工
log       负责收尾记录