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代码块从而实现一些功能

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" thenreturn ngx.redirect("http://baidu.com")elseif args["jump"] == "2" thenreturn 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_methodlocal request_uri = ngx.var.request_urilocal status = ngx.var.statuslocal response_time = ngx.var.request_timelocal 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文件

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] thenreturn 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文件

--只在第一个worker进程里面初始化
if ngx.worker.id() ~= 0 thenreturn
endlocal redis = require("resty.redis")
local bklist = ngx.shared.blkslocal function updata_blacklist()local red = redis:new()local ok, err = red:connect("127.0.0.1", 6379)if not ok thenreturnendlocal black_list, err = red:smembers("black_list")bklist:flush_all()for _, value in pairs(black_list) dobklist:set(value, true)endngx.timer.at(5, updata_blacklist)
endngx.timer.at(5, updata_blacklist) --每5s把redis黑名单里面的内容更新到共享内存里

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

black_v2.lua 文件

 

local bklist = ngx.shared.blkslocal ip = ngx.var.remote_addrif bklist:get(ip) thenreturn 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文件

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

local sock, err = ngx.req.socket()
if not sock thenreturn
endlocal upsock = ngx.socket.tcp()local ok, err = upsock:connect("192.168.217.148", 8080)
if not ok thenreturn
endupsock:send(ngx.var.remote_addr .. "\n")
-- local function handler_upstream()-- end
六.kong总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/83520.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多商户商城系统源码解析:开发直播电商APP的技术底层实战详解

随着直播电商的火爆,越来越多的创业者和企业都在寻求打造自己的多商户商城系统,以实现“人、货、场”三者的深度融合。然而,从一个简单的电商平台到一个功能完善的直播电商APP,其技术底层架构和实现过程并非一蹴而就。本文将从架构…

桌面端进程通信

以下是关于 Electron 桌面端进程通信的基本知识点总结: 一、Electron 进程模型基础 1. 进程类型与职责 进程类型职责权限主进程(Main)创建窗口、系统级操作、IPC中枢完全Node.js访问权限渲染进程(Renderer)展示Web内容、UI交互默认受限(可配置开启Node.js)预加载脚本(Prelo…

openEuler24.03 LTS下安装MySQL8.0.42

目录 前提步骤 删除原有mysql及maridb数据库 安装MySQL 启动MySQL 启动查看MySQL状态 设置MySQL开机自启动 查看登录密码 登录MySQL 修改密码及支持远程连接 远程连接MySQL 前提步骤 拥有openEuler24.03 LTS环境,可参考:Vmware下安装openEule…

idea 保证旧版本配置的同时,如何从低版本升到高版本

文章目录 前言idea 保证旧版本配置的同时,如何从低版本升到高版本1. 备份项目2. 下载最新的idea3. 安装安装包4. 导入idea2019旧配置5. 验证前言 如果您觉得有用的话,记得给博主点个赞,评论,收藏一键三连啊,写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差,…

填坑记: 古董项目Apache POI 依赖异常排除

当你看到NoSuchMethodError的时候,不要慌,深呼吸,这可能只是JAR包版本的问题… 引子:一个平静的周二下午 那是一个看似平常的周二下午,系统运行良好,开发团队在有条不紊地推进着新功能的开发。突然&#x…

CAPL Class: TcpSocket (此类用于实现 TCP 网络通信 )

目录 Class: TcpSocketacceptopenclosebindconnectgetLastSocketErrorgetLastSocketErrorAsStringlistenreceivesendsetSocketOptionshutdown函数调用的基本流程服务器端的基本流程客户端的基本流程Class: TcpSocket学习笔记。来自CANoe帮助文档。 Class: TcpSocket accept /…

微信小程序的开发及问题解决

HttpClient 测试例子 SpringBootTest public class HttpClientTest {/*** 测试通过httpclient发送get方式的请求*/Testpublic void testGET() throws IOException {//创建httpclient对象CloseableHttpClient httpClient HttpClients.createDefault();//创建请求对象HttpGet ht…

foreach中使用await的问题

目录 1.说明 2.示例 3.解决方案 1.说明 在foreach中调用异步方法,即使使用了await,不会依次执行每个异步任务,也就是说Array.prototype.forEach不会等待 Promise 完成,即使你在回调函数中返回一个 Promise,forEach …

Linux调试生成核心存储文件

1.核心存储文件配置: 不知道理解对不对,Linux中的核心存储文件的配置是在/proc/sys/kernel/core_pattern中的,使用 cat /proc/sys/kernel/core_pattern # 打印出 |/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E表示核…

Compose笔记(二十三)--多点触控

这一节主要了解一下Compose中多点触控,在Jetpack Compose 中,多点触控处理需要结合Modifier和手势API来实现,一般通过组合 pointerInput、TransformableState 和 TransformModifier 来创建支持缩放、旋转和平移的组件。 一、 API 1. Pointer…

【Java ee初阶】HTTP(4)

构造HTTP请求 1)开发中,前后端交互。浏览器运行的网页中,构造出HTTP请求 2)调试阶段,通过构造HTTP请求测试服务器 朴素的方案: 通过tcp socket 的方式构造HTTP请求 按照HTTP请求格式,往TCP…

STM32 __main

STM32开发中__main与用户main()函数的本质区别及工作机制 在STM32开发中,__main和用户定义的main()函数是启动过程中的两个关键节点,分别承担运行时初始化和用户程序入口的职责。以下是它们的核心差异及协作机制: 一、定义与层级差异 ​__ma…

什么是PMBus

一、PMBus的定义与背景 PMBus(Power Management Bus,电源管理总线) 是一种基于SMBus(System Management Bus)的开放标准数字通信协议,专为电源设备的监控、配置和控制设计。由PMBus联盟(现并入…

Python OOP核心技巧:如何正确选择实例方法、类方法和静态方法

Python方法类型全解析:实例方法、类方法与静态方法的使用场景 一、三种方法的基本区别二、访问能力对比表三、何时使用实例方法使用实例方法的核心场景:具体应用场景:1. 操作实例属性2. 对象间交互3. 实现特定实例的行为 四、何时使用类方法使…

业务中台-典型技术栈选型(微服务、容器编排、分布式数据库、消息队列、服务监控、低代码等)

在企业数字化中台建设中,业务中台是核心支撑平台,旨在通过技术手段将企业核心业务能力抽象、标准化和复用,以快速响应前端业务需求。其核心技术流涉及从业务抽象到服务化、治理和持续优化的全流程。以下是业务中台建设中的核心技术体系及关键…

期望是什么:(无数次的均值,结合概率)21/6=3.5

https://seeing-theory.brown.edu/basic-probability/cn.html 期望是什么:(无数次的均值,结合概率)21/6=3.5 一、期望(数学概念) 在概率论和统计学中,**期望(Expectation)**是一个核心概念,用于描述随机变量的长期平均取值,反映随机变量取值的集中趋势。 (一…

matlab官方免费下载安装超详细教程2025最新matlab安装教程(MATLAB R2024b)

文章目录 准备工作MATLAB R2024b 安装包获取详细安装步骤1. 文件准备2. 启动安装程序3. 配置安装选项4. 选择许可证文件5. 设置安装位置6. 选择组件7. 开始安装8. 完成辅助设置 常见问题解决启动失败问题 结语 准备工作 本教程将帮助你快速掌握MATLAB R2024b的安装技巧&#x…

第3章 自动化测试:从单元测试到硬件在环(HIL)

在前两章中,我们已完成从环境搭建到流水线编译的自动化配置。为了真正保障软件质量、降低回归风险,本章将聚焦测试自动化,涵盖从最基础的单元测试,到集成测试,再到硬件在环(Hardware-in-the-Loop, HIL)测试的全流程。通过脚本驱动、测试报告可视化和与 CI 平台深度集成,…

信息收集+初步漏洞打点

目标:理解信息收集在渗透测试中的意义,熟悉常用工具用法,完成基本打点测试 一.理论学习: 模块内容说明信息收集分类主动信息收集 vs 被动信息收集目标发现子域名、IP、端口、子站点、目录、接口技术指纹识别Web框架(如…

uniapp+vue3开发项目之引入vuex状态管理工具

前言: 我们在vue2的时候常用的状态管理工具就是vuex,vue3开发以后,又多了一个pinia的选项,相对更轻便,但是vuex也用的非常多的,这里简单说下在uni-app中vuex的使用。 实现步骤: 1、安装&#x…