
公司产品最近出了一个需求,说需要进行ABtest进行自动切换页面投放,因为页面本身挺复杂,动态性比较大很多数据都在后台返回的。所以本来想着挺简单的需求,直接通过网关动态进行转发就ok~
很完美的事情,产品说,不行,得投放,并发量预估挺大的。那怎么办呢?于是乎我们就只能在nginx层通过lua脚本调用redis缓存进行动态的处理。完全避免了流量打到应用层。
那首先我们说一下什么是lua脚本呢?
lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
这是lua语言官方的介绍。简而言之就是:lua是脚本程序,很简单很小,就是为了扩展一些额外的功能点而存在的。
我们来学习一下lua
一般情况下,linux服务器本身就会安装lua 。所以这里就不介绍安装了,咱们直接来第一个hello world程序。
1、在linux任意目录文件下,新建一个hello.lua

2、在hello.lua中添加以下的代码
print("hello world")3、运行。对的就是这么简单。

lua的数据类型有以下几种。
如果想知道一个变量是什么数据类型的话。使用 type函数就可以。
print(type("hello"))
//这个命令就会输出一个 String
print(type(1))
//这个命令就会输出一个 number
而在lua中申明一个变量只需要使用local关键字就可以:
local str = "hello"
local num = 1
local arr = {"a","b","c"}
local obj = {"a":"1","b":"2"}
对于访问的话与其他一些开发语言类似:
数组:arr[0] 对象:obj.a下面我们快速的说一下其他的逻辑判断和函数等。因为我们的重点并不在lua语言上。
循环的使用:
local arr = {"a","b","c"}
for index,val in ipairs(arr) do
print(index,val)
end
local obj = {"a"="1","b"="2"}
for index,val in pairs(obj) do
print(index,val)
end函数定义
funtion eat(food){
print("我爱吃",food)
}条件控制
local f = true;
if(f) then
print("对")
else
print("错")
end以上就是基本的用法了。接下来进入主题了。在nginx中运行lua脚本读取redis缓存。
在nginx中运行lua脚本需要 ngx_http_lua_module 模块来实现
一般情况下,我们不自行安装,直接使用OpenResty框架。
什么是OpenResty呢?
官方说OpenResty是一个基于 Nginx的高性能 Web 平台,用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用
Web 服务和动态网关。
总结一下就是:包含nginx、额外支持lua脚本,集成大量lua库的一个框架。
我们安装一下OpenResty
yum install -y pcre-devel openssl-devel gcc --skip-broken我们 CentOS 系统中添加 openresty 仓库,运行下面的命令就可以添加我们的仓库:
yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo如果提示说命令不存在,则运行:
yum install -y yum-utils然后再重复上面的命令
完成上面操作后,执行安装命令
yum install -y openresty默认情况下,OpenResty安装的目录是:/usr/local/openresty
下面目录中的luajit和lualib就是lua脚本的一些扩展库及我们自定义脚本的存放目录。
nginx目录则是完整的nginx服务。

vi /etc/profile在这个文件中追加上环境配置
export NGINX_HOME=/usr/local/openresty/nginx
export PATH=${NGINX_HOME}/sbin:$PATH执行命令,使配置生效
source /etc/profilenginx目录下是完整的nginx服务。我们开始启动

# 启动nginx (启动只需要这个就行) 注意nginx目录 在sbin目录下执行
nginx
# 重新加载配置(重启就用这个命令)
nginx -s reload
# 停止
nginx -s stop修改/usr/local/openresty/nginx/conf/nginx.conf文件,内容如下:
#user nobody;
worker_processes 1;
error_log logs/error.log;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8081;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}#lua 模块
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
#c模块
lua_package_cpath "/usr/local/openresty/lualib/?.so;;";最后记得重启nginx
做好前面的准备工作后,我们写代码
下面定义了2个方法,一个读取,一个关闭 redis.
local redis = require("resty.redis")
-- 初始化对象
local red = redis:new()
-- 设置超时时间
red.set_timeouts(1000)
-- 查询redis的方法 ip和port是redis地址,key是查询的key
local function read_redis(ip, port, key)
-- 获取一个连接
local ok, err = red:connect(ip, port)
if not ok then
ngx.log(ngx.ERR, "连接redis失败 : ", err)
return nil
end
-- 查询redis
local resp, err = red:get(key)
-- 查询失败处理
if not resp then
ngx.log(ngx.ERR, "查询Redis失败: ", err, ", key = " , key)
end
--得到的数据为空处理
if resp == ngx.null then
resp = nil
ngx.log(ngx.ERR, "查询Redis数据为空, key = ", key)
end
close_redis(red)
return resp
end
-- 关闭redis连接的工具方法,其实是放入连接池
local function close_redis(red)
local pool_max_idle_time = 10000 -- 连接的空闲时间,单位是毫秒
local pool_size = 100 --连接池大小
local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)
if not ok then
ngx.log(ngx.ERR, "放入redis连接池失败: ", err)
end
end我们只需要调用read_redis方法就可以访问redis。
一般情况下我们获取到的是一个json的字串。如何解析json呢?
OpenResty提供了一个cjson的模块用来处理JSON的序列化和反序列化。
local cjson = require "cjson"
local obj ={name='zhangsan',age=18}
local json = cjson.encode(obj)
local json1 ='{"name":"lisi","age": 18}'
local obj1=cjson.decode(json1);
print(obj1.name)最后我们在说一下在lua中如何获取前端传递的参数呢?
在openResty中提供了一些方法来获取不同地方的参数。例如:
local headers = ngx.req.get_headers()
local getParams = ngx.req.get_uri_args()
ngx.req.read_body()
local postParams = ngx.req.get_post_args()
local jsonBody = ngx.req.get_body_data()
以上就是lua脚本在nginx层读取reis缓存的全部操作了。其实挺简单的。读取缓存并解析成功数据后,就可以进行abtest数据返回或者内部直接通过进行转发了。完全防止了流量打到应用层。直接做好缓存预热后,就可以解决大量的并发问题了
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。