OpenResty简介与实践

简介

OpenResty(亦称ngx_openresty)是一个基于Nginx的 Web 应用服务器。它集成了Nginx内核、LuaJIT、Lua语言实现的库以及许多第三方的Nginx模块。开发者可以使用Lua语言对Nginx中的C模块和Lua模块进行脚本编程,从而构建高性能的Web应用。

安装

tar xvf ngx_openresty-1.7.7.2.tar.gz
cd ngx_openresty-1.7.7.2/
./configure
make
make install

OpenResty依赖perl 5.6.1+、libreadline、libpcre、libssl,因此在configure时,可能会由于缺少依赖报如下错误:

./configure: error: the HTTP rewrite module requires the PCRE library.

需要安装libpcre:

yum install pcre-devel.x86_64

./configure: error: SSL modules require the OpenSSL library.

需要安装libssl:

yum install openssl-devel.x86_64

安装成功后,OpenResty默认安装至/usr/local/openresty。

使用

使用OpenResty开发接口,该接口实现以下逻辑:

  1. 接口根据传入的key返回value;
  2. 接口先尝试从Redis中读取key所对应的value,若读取到value,则直接返回;
  3. 若读取不到value,则接口再尝试从MySQL中读取key所对应的value,若读取到value,则将key-value写入Redis,并返回value。

在OpenResty安装目录的nginx/conf中新建Lua脚本文件value.lua,代码如下所示,其实现了上述逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
-- ################## 解析参数key ########################
local cjson = require "cjson"
local key = tostring(ngx.var.arg_key)
if key == 'nil' then
ngx.say(cjson.encode({status = false, msg = 'need parameter key'}))
return
end

-- ################## 初始化Redis和MySQL ########################
local mysql = require 'resty.mysql'
local conn = mysql:new()
local props = {
host = "xxx.xxx.xxx.xxx",
port = 3306,
database = "xxx",
user = "xxx",
password = "xxx"
}
-- 使用connect方法创建连接,该方法会先尝试从连接池中获取已有连接
local res, err, errno, sqlstate = conn:connect(props)
if not res then
print("connect to mysql error : ", err, " , errno : ", errno, " , sqlstate : ", sqlstate)
ngx.say(cjson.encode({status = false, msg = 'internal error'}))
return
end
local redis = require 'resty.redis'
local redis_client = redis:new()
-- 使用connect方法创建连接,该方法会先尝试从连接池中获取已有连接
local ok, err = redis_client:connect('xxx.xxx.xxx.xxx', 6379)
if not ok then
print("connect to redis error:", err)
ngx.say(cjson.encode({status = false, msg = 'internal error'}))
return
end

-- 释放Redis连接
function close_redis()
local pool_max_idle_time = 10000
local pool_size = 1024
if redis_client then
-- 使用set_keepalived方法将连接释放到连接池中,并可以指定连接池中连接的最长空闲时间和连接池中的连接数
redis_client:set_keepalive(pool_max_idle_time, pool_size)
end
end

-- 释放MySQL连接
function close_mysql()
local pool_max_idle_time = 10000
local pool_size = 5
if conn then
-- 使用set_keepalived方法将连接释放到连接池中,并可以指定连接池中连接的最长空闲时间和连接池中的连接数
conn:set_keepalive(pool_max_idle_time, pool_size)
end
end

function get(key)
-- 先尝试从Redis中根据key获取value
local value = redis_client:get(key)
if not value or value == ngx.null then
-- 若Redis中不存在该key,则尝试从MySQL中根据key获取value
local rows = conn:query("select v from key_value where k='" .. key .. "'")
if rows and rows[1] then
value = rows[1].v
if value and value ~= ngx.null then
-- 若MySQL中存在该key,则将该key和value添加到Redis中
redis_client:set(key, value)
end
end
end
return value
end

local value = get(key)
if not value or value == ngx.null then
ngx.say(cjson.encode({status = false, msg = 'no value'}))
else
ngx.say(cjson.encode({status = true, msg = 'success', value = value}))
end
close_redis()
close_mysql()

配置nginx/conf/nginx.conf,对于地址为“/api/value”的请求使用value.lua处理:

1
2
3
4
5
6
7
8
server {
listen 8080;
server_name localhost;
location /api/value {
content_by_lua_file conf/value.lua;
}
...
}

启动Nginx,浏览器中访问“api/value?key=v1”,成功返回value:
1