OpenResty介绍与实战
一、概述
OpenResty是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web应用、Web服务和动态网关。
简单地说 OpenResty 的目标是让你的Web服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。
从上面官网的描述信息中,可以看出OpenResty主要包含两方面的技术:
Nginx:一款轻量级、高性能、高并发的Web服务器。Lua:一种轻量、小巧、可移植、快速的脚本语言;LuaJIT即时编译器会将频繁执行的Lua代码编译成本地机器码交给CPU直接执行,执行效率更高,OpenResty会默认启用LuaJIT。

Github组织:https://github.com/openresty
nginx 与 lua 介绍与安装可以参考我以下几篇文章:
- NGINX - 高级负载均衡器、Web服务器、反向代理
- NGINX 路由配置与参数详解(https配置、跨域配置、socket配置)
- NGINX sub_filter 和 subs_filter 指令讲解
- lua 语法介绍与 NGINX lua 高级用法实战操作
二、OpenResty 安装
1yum install pcre-devel openssl-devel gcc curl 2wget https://openresty.org/package/centos/openresty.repo 3sudo mv openresty.repo /etc/yum.repos.d/openresty.repo 4 5# update the yum index: 6sudo yum check-update 7yum -y install openresty
验证安装
1/usr/local/openresty/nginx/sbin/nginx -v
启动 OpenResty
1/usr/local/openresty/nginx/sbin/nginx
测试
1# 创建一个 NGINX 配置文件(例如 /usr/local/openresty/nginx/conf/nginx.conf)并添加一个包含 Lua 代码的 location 块: 2server { 3 listen 80; 4 server_name localhost; 5 6 location /test { 7 content_by_lua_block { 8 ngx.say("Hello, LuaJIT!") 9 } 10 } 11}
保存配置文件,然后重新加载 NGINX:
1# 先检查语法 2sudo /usr/local/openresty/nginx/sbin/nginx -t 3# 重新加载配置 4sudo /usr/local/openresty/nginx/sbin/nginx -s reload
访问 http://localhost/test 应该返回 "Hello, LuaJIT!"。

三、OpenResty 的工作原理
OpenResty 是基于 Nginx 的高性能Web平台,所以其高效运行与 Nginx 密不可分。 Nginx 处理HTTP请求有11个执行阶段,我们可以从ngx_http_core_module.h 的源码中看到:
1typedef enum { 2 NGX_HTTP_POST_READ_PHASE = 0, 3 4 NGX_HTTP_SERVER_REWRITE_PHASE, 5 6 NGX_HTTP_FIND_CONFIG_PHASE, 7 NGX_HTTP_REWRITE_PHASE, 8 NGX_HTTP_POST_REWRITE_PHASE, 9 10 NGX_HTTP_PREACCESS_PHASE, 11 12 NGX_HTTP_ACCESS_PHASE, 13 NGX_HTTP_POST_ACCESS_PHASE, 14 15 NGX_HTTP_PRECONTENT_PHASE, 16 17 NGX_HTTP_CONTENT_PHASE, 18 19 NGX_HTTP_LOG_PHASE 20} ngx_http_phases;
巧合的是,OpenResty 也有 11 个 *_by_lua 指令,它们和 NGINX 的11个执行阶段有很大的关联性。指令是使用Lua编写Nginx脚本的基本构建块,用于指定用户编写的Lua代码何时运行以及运行结果如何使用等。下图显示了不同指令的执行顺序,这张图可以帮助理清我们编写的脚本是按照怎样的逻辑运行的。

四、OpenResty 核心模块
OpenResty 是一个基于 NGINX 的全功能 Web 平台,它集成了许多模块和库,为 NGINX 增加了额外的功能和能力。以下是 OpenResty 的一些核心模块:
1)ngx_lua 模块
ngx_lua 模块是 OpenResty 的核心模块之一,提供了对 Lua 脚本的支持。它允许开发者在 NGINX 配置中嵌入Lua代码,实现高级的请求处理逻辑、动态内容生成、访问控制等功能。
ngx_lua 模块示例:
1server { 2 listen 80; 3 server_name example.com; 4 5 location /lua_example { 6 default_type 'text/plain'; 7 content_by_lua_block { 8 ngx.say("Hello, ngx_lua!") 9 } 10 } 11}
在这个例子中,当访问 http://example.com/lua_example 时,将返回 "Hello, ngx_lua!"。这里使用了 ngx_lua 模块的 content_by_lua_block 指令,将 Lua 代码嵌入 NGINX 配置文件。
2)ngx_stream_lua 模块
ngx_stream_lua 模块 与 ngx_lua 类似,但专门用于处理 TCP 和 UDP 流量。它允许开发者在 NGINX 配置中嵌入 Lua 代码以处理流量。
以下是一个简单的 ngx_stream_lua 模块的示例:
1stream { 2 server { 3 listen 12345; 4 5 content_by_lua_block { 6 local data, err = ngx.req.socket() 7 if not data then 8 ngx.log(ngx.ERR, "Failed to read request: ", err) 9 return 10 end 11 12 ngx.say("Received data: ", data) 13 } 14 } 15}
在这个示例中:
- 使用
content_by_lua_block指令定义了一个Lua代码块,用于处理TCP流量。 - 通过
ngx.req.socket()获取连接的套接字,然后读取请求数据。 - 输出接收到的数据。
此配置监听在端口 12345 上,当有 TCP 连接到达时,Lua 代码将读取并输出接收到的数据。
3)ngx_http_lua_module 模块
ngx_http_lua_module 模块是 ngx_lua 模块的一部分,为 NGINX 提供了强大的 HTTP 服务和 Lua 扩展。
以下是一个简单的 ngx_http_lua_module 模块的示例:
1server { 2 listen 80; 3 server_name example.com; 4 5 location /lua_example { 6 default_type 'text/plain'; 7 content_by_lua_block { 8 ngx.say("Hello, ngx_http_lua!") 9 } 10 } 11 12 location /lua_variable { 13 default_type 'text/plain'; 14 set $my_variable "NGINX with Lua"; 15 content_by_lua_block { 16 local my_variable = ngx.var.my_variable 17 ngx.say("Value of my_variable: ", my_variable) 18 } 19 } 20}
这个示例中:
/lua_example路径下的请求将返回"Hello, ngx_http_lua!"。这里使用了content_by_lua_block指令,将Lua代码嵌入NGINX配置文件,实现了简单的响应内容输出。/lua_variable路径下的请求将输出一个自定义变量的值。使用了set指令设置了一个名为my_variable的变量,然后在Lua代码块中通过ngx.var获取并输出了这个变量的值。
4)ngx_http_headers_more 模块
ngx_http_headers_more 模块提供了更多的操作 HTTP 头部的指令,使得在 NGINX 中更容易操作和修改 HTTP 头信息。
以下是一个简单的使用 ngx_http_headers_more 模块的示例:
1server { 2 listen 80; 3 server_name example.com; 4 5 location /add_custom_header { 6 more_set_headers "Custom-Header: OpenResty"; 7 return 200 "Custom header added!"; 8 } 9 10 location /remove_server_header { 11 more_clear_headers Server; 12 return 200 "Server header removed!"; 13 } 14}
在这个示例中:
/add_custom_header路径下的请求将返回"Custom header added!",同时响应头中包含了一个自定义的头部"Custom-Header: OpenResty"。这是通过more_set_headers指令添加的。/remove_server_header路径下的请求将返回"Server header removed!",同时响应头中不再包含"Server"头部。这是通过more_clear_headers指令移除的默认的"Server"头部。
这些指令允许你更灵活地配置 NGINX 的响应头信息,添加或删除特定的头部字段。请注意,使用这些指令时应谨慎,确保符合安全性和隐私性的最佳实践。
5)ngx_http_echo 模块
ngx_http_echo 模块 提供了更丰富的内容输出和变量替换的功能。可以通过指定响应内容、设置 HTTP 状态码等,以及支持类似 PHP 的变量替换。
1server { 2 listen 80; 3 server_name example.com; 4 5 location /echo_example { 6 echo "Hello, ngx_http_echo!"; 7 } 8} 9
在这个示例中:
/echo_example路径下的请求将返回"Hello, ngx_http_echo!"。这是通过ngx_http_echo模块的echo指令实现的。ngx_http_echo模块的echo指令允许你更方便地输出内容,支持类似PHP的变量替换等功能。你可以在echo指令中直接使用变量,也可以使用其他模块提供的一些特殊的变量。
6)ngx_http_lua_upstream 模块
ngx_http_lua_upstream 模块用于在 Lua 脚本中进行向上游服务器(后端服务器)的请求,并处理来自上游的响应。
以下是一个简单的示例,演示了如何使用 ngx_http_lua_upstream 模块向上游服务器发起请求,并处理来自上游服务器的响应:
1http { 2 upstream backend { 3 server backend1; 4 server backend2; 5 } 6 7 server { 8 listen 80; 9 server_name example.com; 10 11 location /proxy_example { 12 content_by_lua_block { 13 -- 初始化 upstream 对象 14 local upstream = require "ngx.upstream" 15 local backend = upstream.backend 16 17 -- 创建一个新的请求对象 18 local request = upstream.request() 19 20 -- 设置请求的方法、URI和头部 21 request.method = ngx.HTTP_GET 22 request.uri = "/path/to/resource" 23 request.headers["Host"] = "backend.example.com" 24 25 -- 发送请求到上游服务器 26 local status, headers, body = request.send(backend) 27 28 -- 处理上游服务器的响应 29 if status == 200 then 30 ngx.say("Response from backend: ", body) 31 else 32 ngx.say("Error from backend. Status: ", status) 33 end 34 } 35 } 36 } 37}
在这个示例中:
- 定义了一个名为
backend的上游服务器块,包含两个后端服务器backend1和backend2。 - 在
/proxy_example路径下的请求中,通过Lua脚本使用ngx_http_lua_upstream模块创建了一个新的上游请求对象。 - 设置了请求的方法、
URI和头部信息。 - 调用
request.send(backend)发送请求到上游服务器。 - 根据上游服务器的响应状态码,输出响应内容或显示错误信息。
请注意,实际使用时,你可能需要根据具体的业务需求和上游服务器的特性进行更复杂的 Lua 脚本编写。此示例仅为演示基本用法。
7)ngx_http_redis 模块
ngx_http_redis 模块提供了与 Redis 数据库的交互功能,允许 NGINX 通过 Lua 脚本与 Redis 通信。
以下是一个简单的示例,演示了如何使用 ngx_http_redis 模块与 Redis 交互:
1http { 2 server { 3 listen 80; 4 server_name example.com; 5 6 location /redis_example { 7 # 定义 Redis 服务器的地址和端口 8 set $redis_host "127.0.0.1"; 9 set $redis_port 6379; 10 11 # 使用 ngx_http_redis 模块向 Redis 发送 GET 请求 12 redis_pass $redis_host:$redis_port; 13 redis_query GET my_key; 14 15 # 处理 Redis 的响应 16 content_by_lua_block { 17 local redis = require "ngx.redis" 18 local red = redis:new() 19 20 red:set_timeout(1000) -- 设置超时时间 21 22 local ok, err = red:connect(ngx.var.redis_host, ngx.var.redis_port) 23 if not ok then 24 ngx.say("Failed to connect to Redis: ", err) 25 return 26 end 27 28 local res, err = red:get("my_key") 29 if not res then 30 ngx.say("Failed to get value from Redis: ", err) 31 return 32 end 33 34 ngx.say("Value from Redis: ", res) 35 36 -- 释放连接 37 local ok, err = red:set_keepalive(10000, 100) 38 if not ok then 39 ngx.say("Failed to set keepalive: ", err) 40 return 41 end 42 } 43 } 44 } 45}
在这个示例中:
/redis_example路径下的请求首先通过redis_pass和redis_query指令设置了Redis服务器的地址和端口,并发送了一个GET请求获取键为"my_key"的值。- 在
Lua脚本中,使用ngx.redis模块创建了一个Redis对象,连接到Redis服务器,并通过get方法获取了"my_key"的值。 - 最后,输出从
Redis获取的值。
请注意,实际使用时,你需要根据你的 Redis 服务器配置和业务需求进行适当的修改。此示例仅为演示基本用法。
8)ngx_http_proxy_connect_module 模块
ngx_http_proxy_connect_module 模块允许 NGINX 充当 CONNECT 代理,用于处理 TLS/SSL 连接的代理请求。
以下是一个简单的使用示例:
1http { 2 server { 3 listen 80; 4 server_name example.com; 5 6 location /proxy_connect_example { 7 proxy_pass http://backend; 8 } 9 } 10 11 server { 12 listen 443 ssl; 13 server_name example.com; 14 15 ssl_certificate /path/to/certificate.crt; 16 ssl_certificate_key /path/to/private_key.key; 17 18 location /proxy_connect_example { 19 proxy_pass http://backend; 20 } 21 } 22}
在这个示例中:
- 配置了两个
server块,分别监听了80端口和443端口。第一个server块用于处理HTTP请求,第二个server块用于处理HTTPS请求。 - 对于
/proxy_connect_example路径,使用了proxy_pass指令,将请求代理到名为backend的上游服务器。 - 对于
HTTPS的情况,需要提供SSL证书和私钥的路径。实际上,这个配置使NGINX具有CONNECT代理的能力,可以处理TLS/SSL握手并将请求代理到上游服务器。
9)ngx_http_js_module 模块
ngx_http_js_module 模块提供了对 JavaScript 的支持,使得可以在 NGINX 中使用 JavaScript 来编写请求处理逻辑。
以下是一个简单的使用 ngx_http_js_module 模块的示例:
1http { 2 server { 3 listen 80; 4 server_name example.com; 5 6 location /js_example { 7 js_content main; 8 } 9 } 10 11 js_include main; 12 13 function main(r) { 14 r.return(200, "Hello, ngx_http_js_module!"); 15 } 16}
在这个示例中:
- 定义了一个监听
80端口的server块。 - 在
/js_example路径下的请求中,使用了js_content指令,将请求处理的逻辑指定为JavaScript脚本。 - 使用
js_include指令引入了一个名为main的JavaScript函数。 main函数中使用了r.return方法返回了一个HTTP 200响应,并带有相应的消息。
请注意,使用 ngx_http_js_module 模块时,需要确保已经正确安装并启用了该模块。此示例只是一个基本的演示,实际使用时可能需要根据具体的业务需求编写更复杂的 JavaScript 脚本。
10)ngx_http_geoip2_module 模块
ngx_http_geoip2_module 模块用于通过 MaxMind GeoIP2 数据库来获取客户端的地理位置信息。
以下是一个简单的示例,演示了如何使用 ngx_http_geoip2_module 模块获取客户端的地理位置信息:
1http { 2 geoip2 /path/to/GeoIP2-City.mmdb { 3 $geoip2_city_country_iso_code country iso_code; 4 $geoip2_city_country country names en; 5 $geoip2_city_city city names en; 6 $geoip2_city_latitude latitude; 7 $geoip2_city_longitude longitude; 8 } 9 10 server { 11 listen 80; 12 server_name example.com; 13 14 location /geoip_example { 15 default_type 'text/plain'; 16 content_by_lua_block { 17 local country = ngx.var.geoip2_city_country 18 local city = ngx.var.geoip2_city_city 19 local latitude = ngx.var.geoip2_city_latitude 20 local longitude = ngx.var.geoip2_city_longitude 21 22 ngx.say("Country: ", country) 23 ngx.say("City: ", city) 24 ngx.say("Latitude: ", latitude) 25 ngx.say("Longitude: ", longitude) 26 } 27 } 28 } 29}
ngx_http_geoip2_module 模块用于在 NGINX 中获取客户端的地理位置信息,基于 MaxMind 的 GeoIP2 数据库。以下是一个简单的示例,演示了如何使用 ngx_http_geoip2_module 模块获取客户端的地理位置信息:
1http { 2 geoip2 /path/to/GeoIP2-City.mmdb { 3 $geoip2_city_country_iso_code country iso_code; 4 $geoip2_city_country country names en; 5 $geoip2_city_city city names en; 6 $geoip2_city_latitude latitude; 7 $geoip2_city_longitude longitude; 8 } 9 10 server { 11 listen 80; 12 server_name example.com; 13 14 location /geoip_example { 15 default_type 'text/plain'; 16 content_by_lua_block { 17 local country = ngx.var.geoip2_city_country 18 local city = ngx.var.geoip2_city_city 19 local latitude = ngx.var.geoip2_city_latitude 20 local longitude = ngx.var.geoip2_city_longitude 21 22 ngx.say("Country: ", country) 23 ngx.say("City: ", city) 24 ngx.say("Latitude: ", latitude) 25 ngx.say("Longitude: ", longitude) 26 } 27 } 28 } 29}
在这个示例中:
- 使用
geoip2指令配置了GeoIP2数据库的路径,并定义了一些变量用于存储地理位置信息,如国家 ISO 代码、国家名称、城市名称、纬度和经度。 - 在
/geoip_example路径下的请求中,通过Lua脚本获取了客户端的地理位置信息,并输出了国家、城市、纬度和经度等信息。
请注意,在实际使用中,你需要确保已经获取并配置了正确的 GeoIP2 数据库文件路径。此示例只是一个基本的演示,实际场景中可能需要根据业务需求进一步处理和使用这些地理位置信息。
11)ngx_brotli 模块
ngx_brotli 模块用于支持 Brotli 压缩算法,提供更高效的内容压缩。
以下是一个简单的使用 ngx_brotli 模块的示例:
1http { 2 brotli on; 3 brotli_comp_level 6; 4 brotli_static on; 5 6 server { 7 listen 80; 8 server_name example.com; 9 10 location /brotli_example { 11 default_type 'text/plain'; 12 content_by_lua_block { 13 ngx.say("Hello, ngx_brotli!"); 14 } 15 } 16 } 17} 18
在这个示例中:
- 使用
brotli指令开启了Brotli压缩功能。 - 使用
brotli_comp_level指令设置了Brotli压缩级别。 - 使用
brotli_static指令开启了静态文件的Brotli压缩。 - 在
/brotli_example路径下的请求中,返回了一个简单的文本内容"Hello, ngx_brotli!"。
请注意,在实际使用中,你需要确保已经安装了支持
Brotli压缩的库,并且 NGINX 配置中启用了对应的模块。此示例只是一个基本的演示,实际配置中可能需要根据业务需求调整压缩级别等参数。
这些模块共同构成了 OpenResty 的核心,使得 NGINX 变得更加强大和灵活。使用这些模块,你可以实现更高级的请求处理、负载均衡、反向代理、动态内容生成等功能。详细信息和用法可以参考 OpenResty 官方文档:https://openresty.org/。
五、OpenResty 示例讲解
OpenResty 是一个基于 NGINX 的全功能 Web 平台,集成了大量的第三方模块和库,其中最重要的是 ngx_lua 模块,它允许在 NGINX 配置中嵌入 Lua 脚本,实现高级的请求处理逻辑、动态内容生成、访问控制等功能。下面是一个简单的 OpenResty 示例:
1http { 2 server { 3 listen 80; 4 server_name example.com; 5 6 location /hello { 7 default_type 'text/plain'; 8 content_by_lua_block { 9 ngx.say("Hello, OpenResty!") 10 } 11 } 12 } 13}
在这个示例中:
- 配置了一个监听
80端口的server块,处理example.com的请求。 - 当访问路径
/hello时,通过content_by_lua_block指令执行Lua代码,输出"Hello, OpenResty!"。
这只是一个最简单的演示,OpenResty 的强大之处在于它允许在 NGINX 配置中使用 Lua 脚本,从而实现更复杂的逻辑。以下是一个稍复杂的示例,演示了如何通过 OpenResty 实现简单的 API 访问控制:
1http { 2 lua_shared_dict my_limit 10m; 3 4 server { 5 listen 80; 6 server_name api.example.com; 7 8 location /api { 9 access_by_lua_block { 10 local limit = ngx.shared.my_limit 11 local key = ngx.var.remote_addr 12 local reqs, err = limit:get(key) 13 if reqs then 14 if reqs > 10 then 15 ngx.exit(ngx.HTTP_TOO_MANY_REQUESTS) 16 else 17 limit:incr(key, 1) 18 end 19 else 20 limit:set(key, 1, 60) 21 end 22 } 23 24 default_type 'application/json'; 25 content_by_lua_block { 26 ngx.say('{"message": "API response"}') 27 } 28 } 29 } 30}
在这个示例中:
- 配置了一个共享内存字典
my_limit用于存储请求计数。 - 当访问路径
/api时,通过access_by_lua_block指令执行Lua代码,实现了一个简单的请求频率限制,每个IP地址在60秒内最多允许10次请求。 - 如果超过请求限制,将返回
HTTP 429 (TOO MANY REQUESTS)状态码;否则,继续执行后续Lua代码返回JSON响应。
这只是 OpenResty 的一小部分功能展示,实际使用中可以结合更多的模块和功能,如 ngx_http_lua_upstream、ngx_http_headers_more、ngx_stream_lua 等,以实现更复杂的 Web 应用和服务。
《零基础从头教学Linux(Day 45)》 是转载文章,点击查看原文。
