这是Web开发平台OpenResty系列文章中的一篇。
OpenResty是什么?被扩展的Nginx,扩展到可以直接执行Lua代码,处理业务逻辑,访问缓存和数据库等。
可以先看一下Nginx、OpenResty和Kong的基本概念与使用方法,对OpenResty有一个整体感知。
使用OpenResty,需要对Nginx和Lua比较熟悉。
Nginx是OpenResty的执行引擎,Lua是OpenResty平台上使用的开发语言。
OpenResty的网站给出了几本关于Lua、Nginx、OpenResty的电子书:
编程语言Lua(一):入门学习资料、基本语法与项目管理工具中收集了更多关于Lua的内容。
在mac上可以直接安装:
brew untap homebrew/nginx
brew install openresty/brew/openresty
在CentOS上的安装以及源代码编译安装,参考OpenResty编译安装。
需要安装lua5.1:
brew install [email protected]
需要安装luarocks,这里不使用brew安装luarocks
,直接下载源代码安装:
brew中的luarocks使用的是lua5.3,openresty使用的是lua5.1,系统上同时存在lua5.3和lua5.1,后续用luarocks管理依赖的package、运行openresty应用时可能会遇到麻烦。
wget https://luarocks.org/releases/luarocks-3.0.3.tar.gz
tar zxpf luarocks-3.0.3.tar.gz
cd luarocks-3.0.3
$ ./configure
$ sudo make bootstrap
OpenResty应用可以用openresty定制的nginx(命令openresty)运行,也可以用resty命令运行(本质上是一样的,resty是一个perl脚本,最终使用的还是openresty定制的nginx)。
例如在直接写一个Lua文件
$cat hello.lua
ngx.say("hello world")
然后用OpenResty的Resty命令执行:
$ resty hello.lua
hello world
可以写一个包含lua代码的nginx.conf,用openresty命令或者openresty带的nginx启动
mkdir -p hello-world/logs
cd hello-world
创建hello-world/nginx.conf
:
worker_processes 1; #nginx worker 数量
error_log logs/error.log; #指定错误日志文件路径
events {
worker_connections 1024;
}
http {
server {
#监听端口,若你的6699端口已经被占用,则需要修改
listen 6699;
location / {
default_type text/html;
content_by_lua_block {
ngx.say("HelloWorld")
}
}
}
}
在hello-world目录中启动:
openresty -p `pwd` -c nginx.conf
这时候用ps命令可以看到nginx进程(openresty命令是连接到nginx命令的符号连接):
$ ps aux|grep nginx
nginx: worker process
nginx: master process openresty -p /Users/lijiao/study-OpenResty/example/01-hello-world -c nginx.conf
访问应用:
$ curl 127.0.0.1:6699
HelloWorld
OpenResty的退出、重启、重新加载等操作,用-s
指定对应的信号:
-s signal : send signal to a master process: stop, quit, reopen, reload
例如重新加载:
openresty -s reload
Lua是一个小巧精炼编程语言,Lua语言的解释器很多,可以到编程语言Lua(一):介绍、入门学习资料、基本语法与项目管理中了解。
OpenResty是一个高度定制的Nginx,集成了NginxLua模块,支持Lua语言。一段lua代码用OpenResty可以执行,直接用Lua命令可能不能执行:
例如下面的代码:
$ cat hello.lua
#! /usr/bin/env lua
--
-- hello.lua
-- Copyright (C) 2018 lijiaocn <[email protected]>
--
-- Distributed under terms of the GPL license.
--
ngx.say("hello world")
用OpenResty可以执行:
$ resty hello.lua
hello world
用Lua不可以:
$ lua-5.1 ./hello.lua
lua-5.1: ./hello.lua:9: attempt to index global 'ngx' (a nil value)
stack traceback:
./hello.lua:9: in main chunk
[C]: ?
用Lua命令执行的时候,提示找不到ngx,这是因为OpenResty包含的一些Lua Package不在Lua的安装目录中,而是在OpenResty自己的安装目录中。
以Mac为例,用brew install openresty/brew/openresty
安装的openresty,它的Package目录是:
$ ls /usr/local/Cellar/openresty/1.13.6.2/
COPYRIGHT homebrew.mxcl.openresty.plist pod
INSTALL_RECEIPT.json luajit resty.index
README.markdown lualib site
bin nginx
$ ls /usr/local/Cellar/openresty/1.13.6.2/lualib
cjson.so ngx redis resty
会发现openresty的项目代码中引用的require "resty.core"
,在lua的package目录中找不到,它是openresty的模块,位于openresty的安装目录中:
$ ls /usr/local/Cellar/openresty/1.13.6.2/lualib/resty/core
base.lua base64.lua ctx.lua exit.lua ....
在使用IDE开发代码时,为了能够跳转到OpenResty的lua模块中,需要将OpenResty的模块目录加入到SDK的ClassPath/SourcePath中。
Kong是一个在OpenResty上实现的API网关应用,这里通过kong来了解OpenResty应用的源码的组织方式。
下载Kong的代码:
git clone https://github.com/Kong/kong
cd kong
Kong使用luarocks管理依赖,依赖的package记录在kong-0.14.1-0.rockspec
文件中:
$ cat kong-0.14.1-0.rockspec
...
dependencies = {
"inspect == 3.1.1",
"luasec == 0.6",
"luasocket == 3.0-rc1",
"penlight == 1.5.4",
"lua-resty-http == 0.12",
"lua-resty-jit-uuid == 0.0.7",
"multipart == 0.5.5",
...
Kong的发布方式也记录在kong-0.14.1-0.rockspec
文件中,记录了模块与代码文件的对应关系:
kong-0.14.1-0.rockspec
build = {
type = "builtin",
modules = {
["kong"] = "kong/init.lua",
["kong.meta"] = "kong/meta.lua",
["kong.cache"] = "kong/cache.lua",
["kong.global"] = "kong/global.lua",
["kong.router"] = "kong/router.lua",
...
make
的时候,是直接用luarocks命令将kong安装到系统中:
install:
@luarocks make OPENSSL_DIR=$(OPENSSL_DIR) CRYPTO_DIR=$(OPENSSL_DIR)
安装之后,在使用OpenResty运行的lua脚本中能够引用kong,例如文件bin/kong
中引用kong的模块kong.cmd.init
:
$ cat bin/kong
#!/usr/bin/env resty
require "luarocks.loader"
package.path = "./?.lua;./?/init.lua;" .. package.path
require("kong.cmd.init")(arg)
使用OpenResty的项目可以使用Lua的IDE,OpenResty虽然和Lua不是一回事,但它可以复用Lua的项目工具,使用的时候别忘了将OpenResty的模块导入到SDK中。
在IntelliJ Idea中的设置方法参考:Lua的项目管理工具-IntelliJ Idea
在前面的例子中,nginx.conf中有一段配置是这样的:
content_by_lua_block {
ngx.say("HelloWorld")
}
这里的content_by_lua_block
指令不是原生的nginx指令,是OpenResty为Nginx增加的指令。
Nginx使用模块化设计,支持接入第三方的模块,第三方模块可以为nginx添加新的配置指令。
OpenResty为标准的Nginx添加了很多模块,大大增强了Nginx的能力。
OpenResty的应用开发过程,主要就是与OpenResty添加的Nginx模块,以及各种Lua的Package打交道的过程。
熟悉OpenResty为Nginx添加的每个模块的用途是必须的,下面是OpenResty的网站上列出的Nginx模块:
array-var-nginx-module,为nginx.conf增加数组类型的变量
ngx_http_auth_request_module,为nginx.conf增加了授权指令
ngx_coolkit,收集了一些比较小巧有用的插件
drizzle-nginx-module,增加了访问mysql的功能
echo-nginx-module,增加了echo系列响应指令
encrypted-session-nginx-module,增加了加解密功能
form-input-nginx-module,读取解析POST和PUT请求的body
headers-more-nginx-module,修改响应头
iconv-nginx-module,编码转换
memc-nginx-module,对接memcache
lua-nginx-module,使nginx能够识别执行lua代码
lua-upstream-nginx-module,将lua-nginx-module模块中的lua api导入到upstreams配置中。
ngx_postgres,增加了访问postgre数据库的功能
rds-csv-nginx-module,将RDS格式数据转换成CSV格式
rds-json-nginx-module,将RDS格式数据转换成JSON格式
HttpRedisModule,增加了访问redis的功能
redis2-nginx-module,支持redis 2.0协议
set-misc-nginx-module,为ngxin的rewrite模块增加的set_XX指令
srcache-nginx-module,增加了缓存功能
ngx_stream_lua_module,为Nginx的stream/tcp增加lua支持
xss-nginx-module,添加跨站支持
每个模块都定义了自己的指令,可以到它们各自的项目中查看,OpenResty Componentes
以LuaNginxModule为例,增加了下面的Nginx指令(directives):
lua_capture_error_log
lua_use_default_type
lua_malloc_trim
lua_code_cache
lua_regex_cache_max_entries
lua_regex_match_limit
...
content_by_lua
content_by_lua_block
content_by_lua_file
rewrite_by_lua
rewrite_by_lua_block
rewrite_by_lua_file
...
其中content_by_lua_block等指令,支持lua代码:
content_by_lua_block {
ngx.say("I need no extra escaping here, for example: \r\nblah")
}
LuaNginxModule还实现了Nginx的lua接口,可以在**_lua_block样式
的指令中直接调用,例如上面的ngx.say
。
Nginx的lua接口比较多,下面只列出了一部分,可以到链接中查看全部: … ngx.ctx ngx.location.capture ngx.location.capture_multi ngx.status ngx.header.HEADER ngx.resp.get_headers ngx.req.is_internal …
除了Nginx模块,OpenResty还收录了一些Lua Package,这些Lua Package有一些是用C语言
开发的,可以用Lua调用,但在IDE中无法跳转到它们的实现。
OpenResty收录的这些Lua Package,被安装到了OpenResty的安装目录中:
$ ls /usr/local/Cellar/openresty/1.13.6.2/lualib/resty/
aes.lua core.lua limit lrucache md5.lua mysql.lua
...
下面是OpenResty网站列出的收录的Package,有的项目中有多个Lua模块,导入一栏中只列出了其中一个,可以它们的源码中查看:
语言 | 导入 | 源码 |
---|---|---|
C | require “cjson” | LuaCjsonLibrary |
C | require “rds.parser” | LuaRdsParserLibrary |
C | require “redis.parser” | LuaRedisParserLibrary |
Lua | require “resty.core” | LuaRestyCoreLibrary |
Lua | require “resty.dns.resolver” | LuaRestyDNSLibrary |
Lua | require “resty.lock” | LuaRestyLockLibrary |
Lua | require “resty.lrucache” | LuaRestyLrucacheLibrary |
Lua | require “resty.memcached” | LuaRestyMemcachedLibrary |
Lua | require “resty.mysql” | LuaRestyMySQLLibrary |
Lua | require “resty.redis” | LuaRestyRedisLibrary |
Lua | require “resty.sha1” | LuaRestyStringLibrary |
Lua | require “resty.upload” | LuaRestyUploadLibrary |
Lua | require “resty.upstream.healthcheck” | LuaRestyUpstreamHealthcheckLibrary |
Lua | require “resty.websocket.server” | LuaRestyWebSocketLibrary |
Lua | require “resty.limit.conn” | LuaRestyLimitTrafficLibrary |
还有处于试验状态的opm命令,用来管理在OpenResty中使用的Lua Package。
OpenResty自身的接口有两部分,一部分是集成的Nginx模块实现的Lua接口,另一部分是收录的Lua Package,它们都位于lualib目录中:
$ ls -F /usr/local/Cellar/openresty/1.13.6.2/lualib
cjson.so* ngx/ redis/ resty/
如果集成的Nginx的模块实现的Lua接口,可以直接在Lua代码中调用。(不是十分确定,下文的ngx.say()是不需要明确引入package就可以执行的 2018-10-27 14:40:34)
例如“第一个OpenResty项目”的例子中,直接调用LuaNginxModule实现的Nginx的Lua接口ngx.say
,不需要引入lua的package。
$ cat hello.lua
ngx.say("hello world")
Lua Package用require引用,例如:
require "resty.core"
需要注意的是lua-nginx-module的部分接口不在lua代码中,见nginx api for lua:
ngx.arg
ngx.var.VARIABLE
Core constants
HTTP method constants
HTTP status constants
Nginx log level constants
print
ngx.ctx
ngx.location.capture
ngx.location.capture_multi
ngx.status
ngx.header.HEADER
ngx.resp.get_headers
ngx.req.is_internal
...