OpenResty 深度解析:构建高性能 Web 服务的终极方案

引言

openresty是什么?在我个人对它的理解来看相当于嵌入了lua的nginx;

我们在nginx中嵌入lua是为了不需要再重新编译,我们只需要重新修改lua脚本,随后重启即可;

一.lua指令序列

我们分别从初始化阶段,重写/访问阶段,内容阶段,日志阶段来介绍上述图中的信息

1.初始化阶段

**init_by_lua*:**Nginx 启动时,在 master 进程中仅执行一次,用于全局初始化,比如加载配置文件、初始化共享字典等。

**init_worker_by_lua* :**Nginx 启动时,每个 worker 进程初始化时执行,可用于 worker 进程级别的初始化,像设置特定于 worker 的变量等 。

2.重写/访问阶段

**ssl_certificate_by_lua* :**当请求是安全请求(涉及 SSL/TLS)时执行,可用于动态选择 SSL 证书等操作。

set_by_lua* :用于设置 Nginx 变量,可在请求处理早期计算变量值。

**rewrite_by_lua* :**用于 URL 重写相关操作,可修改请求的 URI 等。

access_by_lua* :用于访问控制,比如认证、鉴权、IP 黑白名单判断等,决定请求是否能继续处理。

3.内容阶段

balancer_by_lua* :在负载均衡阶段执行,可自定义负载均衡算法,选择合适的上游服务器。

content_by_lua* :用于生成响应内容,是核心的内容生成阶段,可查询数据库、组装数据并输出响应。

header_filter_by_lua* :用于过滤和修改响应头,比如添加自定义响应头、修改缓存相关头信息等。

**body_filter_by_lua* :**用于过滤和修改响应体,可对响应内容进行二次处理,如压缩、加密等。

4.日志阶段

log_by_lua* :请求处理完成后,在记录日志时执行,可自定义日志记录逻辑,比如将日志发送到特定存储或进行格式化处理。

5.conf代码案例

我们可以直接在conf文件里面写lua代码块从而实现一些功能

Lua 复制代码
worker_processes 8;

events {
    worker_connections 10240;
}

http {
    error_log ./logs/error.log info;
    server {
        listen 8989;
        location / {
            rewrite_by_lua_block {
                local args = ngx.req.get_uri_args()
                if args["jump"] == "1" then
                return ngx.redirect("http://baidu.com")
                elseif args["jump"] == "2" then
                return ngx.redirect("/jump_other")
                end
            }
            content_by_lua_block {
                ngx.say("hello","\t",ngx.var.remote_addr)
            }
        }
        location /jump_other {
            content_by_lua_block{
                ngx.say("jump other","\t",ngx.var.remote_addr)
            }
            body_filter_by_lua_block{
                local chunk =  ngx.arg[1]
                ngx.arg[1]=string.gsub(chunk,"other","lion")
            }
            log_by_lua_block{
                local request_method = ngx.var.request_method
                local request_uri = ngx.var.request_uri

                local status = ngx.var.status
                local response_time = ngx.var.request_time

                local msg  = string.fomat("[%s] %s -Status:%d,response time = %.2fms",
                os.data("%Y-%m-%d %H:%M:%S"),request_uri,status,response_time)
                ngx.log(ngx.INFO,msg)
            }
        }
    }
}

上述conf代码块起了8个工作线程 每个工作线程的最大连接数为10240
我们sever的监听端口为8989,当我们页面路由到location /时候会调用lua嵌入的**rewrite_by_lua_block(重写url)**接口;
当jump=1时会直接重定向到百度

当jump=2时会重定向到location /jumpother
当我们跳转到location /jump_other时 我们会调用content_by_lua_block(生成内容输出http响应) 接口 后面执行到body_filter_by_lua_block(对响应内容进行二次处理) 接口
后面执行到日志输出接口 log_by_lua_block(打印对应的日志信息)
++执行结果展示++

默认输出 不输入jump

输入jump=1 重定向到百度

输入jump=2 重定向到jump_other

二.openresty 中 嵌入原理和 责任链模式
1.OpenResty 嵌入原理

OpenResty 本质是将 LuaJIT 虚拟机嵌入到 Nginx 的管理进程(master 进程)和工作进程(worker 进程)中 。具体表现为:

进程内虚拟机共享:每个 worker 进程使用一个 LuaVM ,同一个进程内的所有协程共享该虚拟机。当请求分配到 worker 进程时,会在 LuaVM 中创建一个 coroutine 协程来处理请求。比如,在处理 HTTP 请求时,不同请求的协程都在所属 worker 进程的 LuaVM 里运行 Lua 代码。

与 Nginx 事件模型结合:ngx_lua 模块使 Lua 内建协程能和 Nginx 的事件驱动模型深度协作。Lua 代码中的 IO 操作委托给 Nginx 事件模型,实现非阻塞调用。像网络请求等 IO 操作时,Lua 协程挂起,Nginx 事件处理机制接管,操作完成后恢复协程上下文继续执行,对开发者透明。

模块与 API 注入 :Nginx 的 I/O 原语等功能经封装后注入 Lua 虚拟机,让 Lua 代码能直接访问。例如,通过ngx.reqngx.resp等 API 可在 Lua 代码里方便操作 Nginx 的请求和响应相关功能 。

2.OpenResty 责任链模式

在 OpenResty 中,责任链模式是一种用于请求处理的设计模式 :

特点:解耦合和中断

节点构建:把请求处理逻辑拆分成一个个独立节点,每个节点完成单一功能,如鉴权、限流、日志记录等。例如在 API 网关场景,鉴权节点验证用户身份合法性,限流节点控制请求频率。

链式调用:节点按特定顺序组成责任链,请求到达后依次流经各节点。前一节点处理完决定是否将请求传递给下一节点。比如鉴权通过才将请求传给限流节点,否则直接返回错误响应。

动态调整:可根据业务需求灵活添加、删除或调整节点顺序。像某些高安全要求业务,在责任链中增加更严格加密和多重认证节点;普通业务则简化节点链。

三.cosocket

cosocket指的是协程和socket的结合实现特定的功能

四.openresty总结
1.我们使用openresty的三板斧

①背靠nginx,嵌入到各个阶段的lua函数

②cosocket可同步非阻塞在多个阶段访问第三方库服务,在nginx上实现业务成为可能

③ngx,shared.dict共享内存可在多个worker进程共享数据,数据实时生效

2.conf和lua代码案例

conf文件

Lua 复制代码
worker_processes 8;

events {
    worker_connections 10240;
}

http {
    error_log ./logs/black.log info;
    lua_shared_dict blks 1m;
    init_worker_by_lua_file ./app/init_worker.lua;
    server {
        listen 8989;
        location / {
            access_by_lua_block {
                local black_list= {
                    ["192.168.217.2"]=true
                }
                if black_list[ngx.var.remote_addr] then
                return ngx.exit(404)
                end
            }
            content_by_lua_block {
                ngx.say("hello","\t",ngx.var.remote_addr)
            }
        }
        location /black_v1 {
            access_by_lua_file ./app/black_v1.lua;
            content_by_lua_block {
                ngx.say("black_v1","\t",ngx.var.remote_addr)
            }
        }
        location /black_v2 {
            access_by_lua_file ./app/black_v2.lua;
            content_by_lua_block {
                ngx.say("black_v2","\t",ngx.var.remote_addr)
            }
        }

    }
}

我们在worker线程初始化的时候通过lua_shared_dict接口实现一块1m的共享内存 后我们调用init_worker_by_lua_file接口 进入到 init_worker.lua 文件中

我们通过conf代码块中的

location /black_v2 {

access_by_lua_file ./app/black_v2.lua;

content_by_lua_block {

ngx.say("black_v2","\t",ngx.var.remote_addr)

}

}

进行详解

init_worker.lua文件

Lua 复制代码
--只在第一个worker进程里面初始化
if ngx.worker.id() ~= 0 then
    return
end

local redis = require("resty.redis")
local bklist = ngx.shared.blks

local function updata_blacklist()
    local red = redis:new()
    local ok, err = red:connect("127.0.0.1", 6379)
    if not ok then
        return
    end
    local black_list, err = red:smembers("black_list")
    bklist:flush_all()
    for _, value in pairs(black_list) do
        bklist:set(value, true)
    end
    ngx.timer.at(5, updata_blacklist)
end

ngx.timer.at(5, updata_blacklist) --每5s把redis黑名单里面的内容更新到共享内存里

我们在上述lua代码中实现了 对redis的连接以及实时把redis里面黑名单的内容更新到共享内存块中

black_v2.lua 文件

Lua 复制代码
local bklist = ngx.shared.blks

local ip = ngx.var.remote_addr

if bklist:get(ip) then
    return ngx.exit(404)
end

当我们路由到 location /black_v2时候 通过access_by_lua_file 接口 会执行black_v2.lua文件

从而拿到共享内存中的黑名单ip地址从而进行判断

实现黑白名单结果演示

启动openresty

启动redis

我们在balck_list中添加两个ip地址

http请求访问

把本地ip地址从 black_list中删除

五.kong在openresty进一层的封装
1.概念和接口

kong和konga-->web服务在开发中经常使用的

我们只介绍其中的反向代理来演示其功能

kong在反向代理的两个接口分别是

proxy_pass;

proxy_protocol on;

2.conf和lua代码案例

conf文件

Lua 复制代码
worker_processes 2;

events {
    worker_connections 1024;
}

# #http
##动态
http {
    error_log ./logs/proxy.log info;
    lua_shared_dict shm 1m;
    lua_shared_dict urls 1m;


    ################
    ###############
    ###########
    ########
    #####
    ##
    #
}


#静态---->问题--->添加删除需要重启生效
http {
    error_log ./logs/proxy.log info;
    upstream ups {
        server 192.168.217.148:7001;
        server 192.168.217.148:7002;
        server 192.168.217.148:7003;
    }
    server {
        listen 9001;
        location / {
            proxy_pass http://ups;
            proxy_protocol on;
        }
    }
}



#tcp
stream {
    upstream tcp_ups {
        server 192.168.217.148:8080;
    }
    server {
        listen 9002;
        proxy_pass tcp_ups;
        proxy_protocol on;
    }
    server {
        listen 9003;
        content_by_lua_file ./app/proxy.lua;
    }
}

proxy.lua

Lua 复制代码
local sock, err = ngx.req.socket()
if not sock then
    return
end

local upsock = ngx.socket.tcp()

local ok, err = upsock:connect("192.168.217.148", 8080)
if not ok then
    return
end

upsock:send(ngx.var.remote_addr .. "\n")
-- local function handler_upstream()

-- end
六.kong总结

Kong 是一款基于 NGINX 和 OpenResty 构建的开源 API 网关,支持 API 管理、流量控制、身份验证、监控等功能,可实现对 API 的全生命周期管理与流量治理。

相关推荐
chen1108____2 小时前
Nginx 实用知识大全:从模块作用到高并发承载
运维·nginx
慢慢沉3 小时前
Lua元表(Metatable)
lua
狒狒的代码王国3 小时前
Nginx简单介绍
运维·nginx
DreamJia915 小时前
Nginx 服务器 SSL 证书安装部署并配置(已实测)
服务器·nginx·ssl
慢慢沉1 天前
Lua(字符串)
开发语言·lua
慢慢沉1 天前
Lua(数组)
开发语言·lua
慢慢沉1 天前
Lua(迭代器)
开发语言·lua
慢慢沉1 天前
Lua基本语法
开发语言·lua
土豆丶杨1 天前
linxu CentOS 配置nginx
linux·nginx·centos
Feng.Lee1 天前
接口测试Postman工具高级使用技巧
功能测试·测试工具·lua·postman·可用性测试