OpenResty使用(一)

本文介绍了在Ubuntu 22.04环境下,如何手动安装OpenResty 1.21.4.1,包括需要的四个核心包。讲解了OpenResty的启动、配置文件编写、Nginx中Lua处理流程以及提供的Lua API。此外,还提到了如何引用外部Lua文件并展示了Lua脚本示例,用于读取request header。

一、简介

OpenResty是一个基于Nginx与Lua的高性能Web平台,Nginx本身是支持Lua扩展的,但缺省情况下,Nginx的编译版本未包含Lua扩展,因此可将openresty视为自带有支持lua功能的扩展版本的nginx,所有nginx适用的东西,openresty也适用。这种“扩展”包含两层意思:

  1.  缺省增加了对Lua的支持
  2.  采用Lua开发了若干Nginx 模块

因此,OpenResty极大地丰富了Nginx的功能,甚至可以快速构造出高性能 Web 应用系统。由于lua与C固有的“亲和性”,引入lua,也就意味着引入了C。
OpenResty支持的lua模块见http://openresty.org/cn/components.html,它们是我们在lua脚本中可以引用的模块,它们当中既有OpenResty团队开发的,也有其它Nginx“爱好者”开发的,从上面页面的列表中,可以看出对MySQL、PostgreSQL、Memcached、Redis的连接被支持,摘要/加解密算法也被支持,需要注意的是,有些模块是需要底层C库的,也体现了上文所说的lua与C的“亲和性”。
本文介绍ubuntu22.04环境下openresty1.21.4.1的使用。另外,出于安全业务而采用Openresty的场景也不少,但本文未涉及此类应用。

二、半手工安装

虽然可以自动安装,但最好知道安装了些什么,因此需要了解一下手动安装。
openresty所有包在http://openresty.org/package/ubuntu/pool/main/o/下,如果只安装核心部分,需要4个包:
openresty-zlib(zilib压缩)
openresty-openssl111(安全通道)
openresty-pcre(perl正则表达式)
Openresty(核心包)
分别进入以上4个包的目录,选择对应ubuntu版本名(如22.04 为Jammy,20.04 为Focal,18.04为Bionic等)的deb文件,下载,然后运行

sudo dpkg –i XXX(包文件名)

进行安装,注意安装的顺序为上面列出的包顺序。
如果没有错误的话,openresty被安装到/usr/local/openresty下,可以打开这个目录看看。

三、启动

在启动之前,需要编写一个配置文件。openresty的配置文件与nginx是一致的,以下是官方文档中的配置文件示例(nginx.conf):

worker_processes  1;
error_log error.log;
events {
    worker_connections 1024;
}
http {
    server {
        listen 8080;
        location / {
            default_type text/html;
            content_by_lua_block {
                ngx.say("<p>hello, world</p>")
            }
        }
    }
}

各行意义比较明显,可参考nginx的相关文档了解各行的意义。

Openresty建议将/usr/local/openresty/nginx/sbin加入PATH,因此可以写一个启动脚本(start.sh)来启动openresty(nginx),例如:

mypath=$(echo $PATH | grep -i /usr/local/openresty/nginx/sbin)
if test -z $mypath
then 
    PATH=/usr/local/openresty/nginx/sbin:$PATH
    export PATH
fi
echo $PATH
nginx -p `pwd`/ -c nginx.conf

以上脚本适用于start.sh与nginx.conf在同一目录,否则修改上面脚本中最后一行。如果按上面nginx.conf中error_log文件的位置,应在此目录下建立logs子目录,否则会报错,大概是当error_log前没有目录时,会自动添加logs目录,而此时logs目录应事先存在。

三、Nginx 中lua处理流程

 上图是OpenResty官网上在各个阶段lua脚本“可参与处理”的流程图,图中若干XX_by_lua可视为“lua块”,块中可以编写lua脚本,Openresty提供此脚本运行的“上下文环境”,通过这个预设的环境(若干接口函数),lua脚本可以与例如request,response等相关数据进行交互,进而定制Nginx的响应过程。
对于整个web响应流程,每个lua块所处的位置,以及它所能够处理的内容都是不同的,这也就是上图的意义。例如,上面content_by_lua*块的意义是web的应答内容由此块产生,而不会再转发了,可以在此块内编写response。Rewrite_by_lua*的意义是request内容可以被此块改写,再转发给后端。
关于各lua块的意义以及它可配置在哪个Nginx块,可参考官网https://openresty-reference.readthedocs.io/en/latest/Directives/,这个参考文档被称为“Directives(指令)”,是因为这些标记可以直接放到Nginx配置文件中。

四、Nginx中lua API

为便于开发,openresty提供了一系列api,可以获得web处理环节中的数据,但是由于环节不同,能够使用的api也不完全相同,也就是每个接口适用的场景不完全相同,有些可用于所有lua块,有些仅针对特定lua块,所以需要特别注意。
1.request内容相关
对于request,有ngx.req.*一系列api。
对uri,有ngx.req. get_uri_args /ngx. set_uri_args
对header, 有ngx.req.get_headers/ngx.req.set_headers或ngx.header.HEADER
对body,根据文档的,缺省是不读取的(大概是性能原因),虽然可设置lua_need_request_body来改变,但通常我们仅针对特定的uri才需要读取,此时先用ngx.req.read_body读取,然后再用ngx.req.get_body_data获得,这里采用的是将body全部读入内存的方法,因此可先设置读取的缓存区大小。OpenResty也提供了将body写入文件的方法。
如果要重构比较大的request body,可以使用ngx.req.init_body,ngx.req.append_body和ngx.req.finish_body。
2. response内容相关
OpenResty api对request的支持显然要比response好,可能“获得后端的response”这样的需求不多。
对header, 只有ngx.resp.get_headers,对于所有内容的捕获,可用ngx.location.capture/ngx.location.capture_multi,不过它们更像是web client的行为。
另外,body_filter_by_lua_block中,ngx.arg[1]是reponse body,这可能是唯一能够方便修改reponse内容的API。
如果不转发给后端应答,直接应答,有ngx.say/ngx.print。
3.跨块参数
有时候状态需要在lua块间转移,因此需要跨块参数,对于Nginx中定义的变量,采用ngx.var.VARIABLE获得,不过,建议采用ngx.ctx方式来定义和获取。ngx.ctx生命周期与request相同,因此ngx.ctx不能用于跨request来使用。如果需要跨request,可以考虑redis或文件进行缓存。
4.调试和日志
将有关信息写入日志是进行调试或运行维护的必要手段,将信息写入Nginx error.log的方法有ngx.log,此时最好在配置文件中设置日志的记录级别,例如

error_log error.log notice;

则日志级别等于或高于notice的才被写入日志。
关于可用的接口函数的意义及其适用于的lua块,可参考官网https://openresty-reference.readthedocs.io/en/latest/Lua_Nginx_API/

五、引用lua文件

原则上,业务逻辑lua脚本可以嵌入到nginx.conf,如"启动”节中

content_by_lua_block {  ngx.say("<p>hello, world</p>")    }

但是如果逻辑比较复杂,还是将lua脚本单独放置较好,因此,一般情况下,需要将外部的lua脚本文件引入Nginx的配置文件。例如,将上面的nginx.conf改写为(相同部分略去):

…
http
{
	…
	lua_package_path "$prefix/lua/?.lua;/home/user/lua/?.lua;";
	server{
		…
		location = /hello {
		…
			   content_by_lua_block {
					  local hello = require "capture"
					  hello.greet("a Lua module")
			   }
		}
	}
}

其中关键的是lua_package_path(如果引用C动态库,采用lua_package_cpath)和require,它们其实与lua引用其它模块的用法是一致的:lua_package_path设置了lua中的package.path,require指出lua文件名,当系统遇到require时,搜索package.path指定的路径以载入该文件。关于lua系统搜索用户定义的模块的详细流程,比较复杂,可参阅lua官方文档。这里简单说,lua_package_path后面是以;分割的若干路径,其中?将被require后的名字替代(还有更复杂的规则),$prefix是当前的运行目录,因此,按上面的写法,系统将按如下顺序进行搜索:
{当前目录}/lua/capture.lua
/home/user/lua/capture.lua
如果找到,就将capture.lua载入。
capture.lua是一个符合lua要求的正常脚本,对于本例,它应该像这样:

local _M = {}
function _M.greet(name)
	ngx.say("Host: ", ngx.req.get_headers()["Host"])
    ngx.say("Greetings from ", name)
end

return _M

这里增加了一个读取request header内容的接口。这个示例的运行结果就是当浏览器请求/hello时,返回如下图所示结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值