高性能网关基石——OpenResty

什么是 OpenResty
OpenResty 一个基于 Nginx 的高性能 Web 平台,能够方便地搭建处理超高并发的动态 Web 应用、 Web 服务和动态网关。例如有名的 Kong 网关和国产新秀 ApiSIX 网关都是基于 OpenResty 来进行打造的。

OpenResty 通过实现 ngx_lua 和 stream_lua 等 Nginx 模块,把 Lua/LuaJIT 完美地整合进了 Nginx,从而让我们能够在 Nginx 内部里嵌入 Lua 脚本,用 Lua 语言来实现复杂的 HTTP/TCP/UDP 业务逻辑,同时依然保持着高度的并发服务能力。

处理阶段
一个正常的 Web 服务的生命周期可以分成三个阶段:

initing:服务启动,读取配置文件,初始化内部数据结构
running:服务运行,接受客户端的请求,返回响应结果
exiting:服务停止,做一些必要的清理工作,如关闭监听端口
OpenResty 主要关注的是 initing 和 running 这两个阶段,并做了更细致的划分

OpenResty 的 initing 阶段
configuration:读取配置文件,解析配置指令,设置运行参数
master-initing:配置文件解析完毕,master 进程初始化公用的数据
worker-initing:worker 进程初始化自己专用的数据
OpenResty 的 running 阶段
在 running 阶段,收到客户端的请求后,OpenResty 对每个请求都会使用下面这条流水线进行处理:

ssl:SSL/TLS 安全通信和验证
preread: 在正式处理之前预读数据,接收 HTTP 请求头
rewrite:检查、改写 URI ,实现跳转重定向
access:访问权限控制
content:产生响应内容
filter:对 content 阶段产生的内容进行过滤加工处理
log: 请求处理完毕,记录日志,或者其他的收尾工作。
openresty-phase.png

OpenResty 执行程序
OpenResty 根据上面的处理阶段提供了一些指令,在开发时使用它们就可以在这些阶段里面插入 Lua 代码,执行业务逻辑:

init_by_lua_file:master-initing 阶段,初始化全局配置或模块
init_worker_by_lua_file:worker-initing 阶段,初始化进程专用功能
ssl_certificate_by_lua_file:ssl 阶段,在握手时设置安全证书
set_by_lua_file:rewrite 阶段,改写 Nginx 变量
rewrite_by_lua_file:rewrite 阶段,改写 URI ,实现跳转或重定向
access_by_lua_file:access 阶段,访问控制或限速
content_by_lua_file:content 阶段,产生响应内容
balancer_by_lua_file:content 阶段,反向代理时选择后端服务器
header_filter_by_lua_file:filter 阶段,加工处理响应头
body_filter_by_lua_file:filter 阶段,加工处理响应体
log_by_lua_file:log 阶段,记录日志或其他的收尾工作
这些指令通常有三种形式:

xxx_by_lua:执行字符串形式的 Lua 代码:
xxx_by_lua_block:功能相同,但指令后是{ ...}的 Lua 代码块
xxx_by_lua_file:功能相同,但执行磁盘上的 Lua 源码文件。这边推荐使用 xxx_by_lua_file,它彻底分离了配置文件与业务代码,让两者可以独立部署,而且文件形式也让我们更容易以模块的方式管理组织 Lua 程序。
下面是 OpenResty 指令所在的阶段和执行的先后顺序图

openrestyflow.png

Demo 编写
为了能够直观的看到上面的处理阶段,接下来编写一个 OpenResty 的 小 demo. 先在本地电脑上安装 OpenResty 然后执行下面命令看看有没有安装成功,如果安装成功了,就会出现版本号

$ sudo openresty -v
nginx version: openresty/1.21.4.1
复制代码
然后执行下面命令创建一些文件夹:

mkdir testresty &&
cd testresty &&
mkdir logs conf service &&
cd logs && touch error.log && touch access.log
复制代码
创建完成后,文件目录结构就像下面这样:

├── conf
├── logs
│ ├── access.log
│ └── error.log
└── service
复制代码
其中, conf 文件夹是存放 nginx.conf 等配置的地方,然后自己编写的 lua 代码文件可以放在 service 文件夹下. 接下来,创建和编写每个阶段所需的 lua 脚本文件, 只在里面编写一条打印日志的代码, 然后放进 service 文件夹下

rewrite.lua
ngx.log(ngx.ALERT,"this is rewrite")
复制代码
access.lua
ngx.log(ngx.ALERT,"this is access")
复制代码
content.lua
ngx.log(ngx.ALERT,"this is content")
-- 响应内容
ngx.say('hello world')
复制代码
header_filter.lua
ngx.log(ngx.ALERT,"this is header_filter")
复制代码
body_filter.lua
ngx.log(ngx.ALERT,"this is body_filter")
复制代码
log.lua
ngx.log(ngx.ALERT,"this is log")
复制代码
编写 nginx.conf 配置文件, 放进 conf 文件夹下

user root;

worker_processes 1;
events {

worker_connections  512;

}

http {

server {
    listen  80;

    location / {
        rewrite_by_lua_file service/rewrite.lua;
        access_by_lua_file service/access.lua;
        content_by_lua_file service/content.lua;
        header_filter_by_lua_file service/header_filter.lua;
        body_filter_by_lua_file service/body_filter.lua;
        log_by_lua_file service/log.lua;
    }

}

}
复制代码
然后启动 OpenResty, 使用 -p 选项, 传入你上面创建的文件夹地址

$ sudo openresty -p $HOME/testresty
复制代码
启动完成后, 然后用浏览器访问 http://localhost, 可以看到上面通过 ngx.say('hello world') 的响应内容设置返回的 hello world. 打开 logs/error.log 文件,可以看到 Lua 代码里打印的日志:

2022/12/26 15:59:26 [alert] 31700#0: *119 [lua] rewrite.lua:1: this is rewrite, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host: "localhost"
2022/12/26 15:59:26 [alert] 31700#0: *119 [lua] access.lua:1: this is access, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host: "localhost"
2022/12/26 15:59:26 [alert] 31700#0: *119 [lua] content.lua:1: this is content, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host: "localhost"
2022/12/26 15:59:26 [alert] 31700#0: *119 [lua] header_filter.lua:1: this is header_filter, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host: "localhost"
2022/12/26 15:59:26 [alert] 31700#0: *119 [lua] body_filter.lua:1: this is body_filter, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host: "localhost"
2022/12/26 15:59:26 [alert] 31700#0: *119 [lua] body_filter.lua:1: this is body_filter, client: 127.0.0.1, server: , request: "GET / HTTP/1.1", host: "localhost"
2022/12/26 15:59:26 [alert] 31700#0: *119 [lua] log.lua:1: this is log while logging request, client: 127.0.0.1, se


10 声望
0 粉丝
0 条评论
推荐阅读
软件测试和开发哪个好?软件测试就业前景怎样
软件测试和开发两个职业都是IT行业中非常重要的角色,但是两者的工作内容和职责存在着明显的区别,所以关于软件测试和开发哪一个更好,需要根据你们自己的兴趣和能力来决定。 软件测试和开发都是IT行业的热门职业...

视角线

都什么年代了,你居然还连不上GitHub?
前言众所周知,GitHub是我们程序员在上班或者学习的时候经常会逛的一个地方[手动狗头],而且如果我们想参与开源项目的话,GitHub也是一个很好的平台。可问题是,GitHub网页总是进不去,提交代码到GitHub也总是超...

小源学算法阅读 777

MyBatisPlus代码生成器
代码生成器AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。环境准备创建一个employe...

视角线阅读 675

Spring中11个最常用的扩展点,你知道几个?
前言在使用spring的过程中,我们有没有发现它的扩展能力很强呢? 由于这个优势的存在,使得spring具有很强的包容性,所以很多第三方应用或者框架可以很容易的投入到spring的怀抱中。今天我们主要来学习Spring中很...

运维社阅读 414

封面图
插件功机制,能让 开源 API 管理工具使用简单么?
API 管理工具,基本的功能是文档和测试,但如果 API 管理工具也能安装插件,任何你想要的 API 管理相关的其他功能都可以通过安装插件来满足,是不是就很酷呢?

圆圆大姐头阅读 365

微服务的版本号要怎么设计?
MAJOR(X):这个是主版本号,一般是涉及到不兼容的 API 更改时,这个会变化。MINOR(Y):这个是次版本号,当我们对 API 进行向后兼容的增强时,这个版本号会变化,换句话说,也就是有新增的功能时,这里会变化...

一口鸭梨阅读 317

真的看不下去了!!!字节的table组件写成啥了!
前言先不说别的,上两个arco design table的bug。本来是写react table组件,然后看源码学习思路,结果看的我真的很想吐槽。(其他组件我在学习源码上受益匪浅,尤其是工程化arco-cli那部分,我自己尝试写的轮子也...

数据先声阅读 297

10 声望
0 粉丝
宣传栏