1 OpenResty整合Reids集群配置
1.1 下载安装lua_resty_redis
lua_resty_redis 它是一个基于rockspec API的为ngx_lua模块提供Lua redis客户端的驱动。
resty-redis-cluster模块地址:https://github.com/steve0511/resty-redis-cluster
将resty-redis-cluster/lib/resty/下面的文件 拷贝到 openresty/lualib/resty
总共两个文件rediscluster.lua,xmodem.lua
1.1.1 连接Redis集群封装
创建
redisUtils.lua
的文件
xxxxxxxxxx --操作Redis集群,封装成一个模块 --引入依赖库 local redis_cluster = require "resty.rediscluster" --配置Redis集群链接信息 local config = { name = "testCluster", --rediscluster name serv_list = { --redis cluster node list(host and port), {ip="192.168.245.164", port = 7001}, {ip="192.168.245.164", port = 7002}, {ip="192.168.245.164", port = 7003}, {ip="192.168.245.164", port = 7004}, {ip="192.168.245.164", port = 7005}, {ip="192.168.245.164", port = 7006}, }, keepalive_timeout = 60000, --redis connection pool idle timeout keepalive_cons = 1000, --redis connection pool size connection_timout = 1000, --timeout while connecting max_redirection = 5 } --定义一个对象 local lredis = {} --创建查询数据get() function lredis.get(key) local red = redis_cluster:new(config) local res, err = red:get(key) if err then ngx.log(ngx.ERR,"执行get错误:",err) return false end return res end -- 执行hgetall方法并封装成table function lredis.hgetall(hash_key) local red = redis_cluster:new(config) local flat_map, err = red:hgetall(hash_key) if err then ngx.log(ngx.ERR,"执行hgetall错误:",err) return false end local result = {} for i = 1, #flat_map, 2 do result[flat_map[i]] = flat_map[i + 1] end return result end -- 判断key中的item是否在布隆过滤器中 function lredis.bfexists(key,item) local red = redis_cluster:new(config) -- 通过eval执行脚本 local res,err = red:eval([[ local key=KEYS[1] local val= ARGV[1] local res,err=redis.call('bf.exists',key,val) return res ]],1,key,item) if err then ngx.log(ngx.ERR,"过滤错误:",err) return false end return res end return lredis
1.1.2 配置lua脚本路径
我们编写了lua脚本需要交给nginx服务器去执行,我们需要将创建一个lua目录,并在全局的nginx‘配置文件中配置lua目录,配置参数使用
lua_package_path
xxxxxxxxxx # 配置redis 本地缓存 lua_shared_dict redis_cluster_slot_locks 100k; # 配置lua脚本路径 lua_package_path "/usr/local/openresty/script/?.lua;;"; #注意后面是两个分号
完整的配置如下
xxxxxxxxxx user root; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; # 注意!! error日志输出info级别日志 error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; #log_format main '$remote_addr - $remote_user [$time_local] "$request" ' # '$status $body_bytes_sent "$http_referer" ' # '"$http_user_agent" "$http_x_forwarded_for"'; #access_log logs/access.log main; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; # 注意!!配置redis 本地缓存 lua_shared_dict redis_cluster_slot_locks 100k; # 注意!!lua脚本路径 lua_package_path "/usr/local/openresty/script/?.lua;;"; #gzip on; include conf.d/*.conf; }
1.1.3 测试脚本
在 nginx配置文件嵌入lua脚本进行测试
xxxxxxxxxx server { listen 9999; charset utf-8; location /test { default_type text/html; content_by_lua ' local lrredis = require("redisUtils") -- 尝试读取redis中key的值 local value = lrredis.get("key") --判断key是否在bf_taxi的布隆过滤器中 local bfexist = lrredis.bfexists("bf_taxi","key") local htest = lrredis.hgetall("h_taxi") ngx.log(ngx.INFO, "key的值是",value) ngx.log(ngx.INFO, "bf_taxi布隆过滤器key的状态",bfexist) ngx.log(ngx.INFO, "h_taxi[url]的值是",htest["url"]) '; } }
设置Redis数据
xxxxxxxxxx # 登录Redis docker exec -ti redis01 /bin/bash redis-cli -h 172.18.0.2 -c # 设置key set key value11 # 设置hash hset h_taxi url http://www.baidu.com # 创建布隆过滤器 BF.RESERVE bf_taxi 0.01 10000 NONSCALING # 布隆过滤器添加key BF.ADD bf_taxi key
访问测试
访问查看nginx日志打印
xxxxxxxxxx tail -f /usr/local/openresty/nginx/logs/error.log
1.2 请求参数封装
nginx为了能够处理请求需要获取请求数据,需要将获取请求参数的lua脚本封装以下,创建
requestUtils.lua
的文件
xxxxxxxxxx --定义一个对象 local lreqparm={} -- 获取请求参数的方法 function lreqparm.getRequestParam() -- 获取请求方法 get或post local request_method = ngx.var.request_method -- 定义参数变量 local args = nil if "GET" == request_method then args = ngx.req.get_uri_args() elseif "POST" == request_method then ngx.req.read_body() args = ngx.req.get_post_args() end return args end return lreqparm
1.2.1 测试脚本
xxxxxxxxxx server { listen 9999; charset utf-8; location /testreq { default_type text/html; content_by_lua ' local lreqparm = require("requestUtils") local params = lreqparm.getRequestParam() local title = params["title"] if title ~= nil then ngx.say("<p>请求参数的Title是:</p>"..title) return end ngx.say("<P>没有输入title请求参数<P>") '; } }
访问http://192.168.64.150:9999/testreq?title=ceshi 测试
1.3 抓取模板内容封装
1.3.1 下载安装lua-resty-http
下载地址 https://github.com/ledgetech/lua-resty-http
将lua-resty-http-master\lib\resty下的所有文件复制到openresty/lualib/resty httpclient
总共两个文件http.lua,http_headers.lua
因为需要从远程服务器抓取远程页面的内容,需要用到http模块,将其封装起来,创建requestHtml.lua
xxxxxxxxxx -- 引入Http库 local http = require "resty.http" --定义一个对象 local lgethtml={} function lgethtml.gethtml(requesturl) --创建客户端 local httpc = http:new() local resp,err = httpc:request_uri(requesturl, { method = "GET", headers = {["User-Agent"]="Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36"} }) --关闭连接 httpc:close() if not resp then ngx.say("request error:",err) return end local result = {} --获取状态码 result.status = resp.status result.body = resp.body return result end return lgethtml
1.3.2 测试脚本
模板文件我们用
x server { listen 9999; charset utf-8; # 配置路径重写 location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ { rewrite ^/(.*) http://www.resources.com/$1 permanent; } location /testgetHtml { default_type text/html; content_by_lua ' local lgethtml = require("requestHtml") local url = "http://127.0.0.1/template.html" local result = lgethtml.gethtml(url); ngx.log(ngx.INFO, "状态是",result.status) ngx.log(ngx.INFO, "body是",result.body) ngx.say(result.body) '; } }
访问http://192.168.64.181:9999/testgetHtml 测试
1.4 模版渲染配置
1.4.1 下载安装lua-resty-template
xxxxxxxxxx wget https://github.com/bungle/lua-resty-template/archive/v1.9.tar.gz tar -xvzf v1.9.tar.gz
解压后可以看到lib/resty下面有一个template.lua,这个就是我们所需要的,在template目录中还有两个lua文件,将这两个文件复制到/usr/openResty/lualib/resty中即可。
1.4.2 使用方式
xxxxxxxxxx local template = require "resty.template" -- Using template.new local html=[[<html> <body> <h1>{{message}}</h1> </body> </html> ]] template.render(html, { message = params["title"] })
1.4.3 测试
在nginx中配置文件中嵌入lua脚本执行测试
x server { listen 9999; charset utf-8; location /testtemplate { default_type text/html; content_by_lua ' local lreqparm = require("requestUtils") local template = require "resty.template" local params = lreqparm.getRequestParam() -- Using template.new local html=[[<html> <body> <h1>{{message}}</h1> </body> </html> ]] template.render(html, { message = params["title"] }) '; } }
访问http://192.168.64.150:9999/testtemplate?title=123456
2 整体业务分析
整个调用流程如下,需要使用lua脚本编写,整体流程分为7 步
上面我们将各个组件都给封装了,下面我们需要将各个组件组合起来
2.1 编写lua脚本
创建一个
requestTemplateRendering.lua
的lua脚本
x --- --- Generated by EmmyLua(https://github.com/EmmyLua) --- Created by baiyp. --- DateTime: 2020/11/24 13:24 --- local template = require("resty.template") local lrredis = require("redisUtils") local lgethtml = require("requestHtml") local lreqparm = require("requestUtils") --获取请求参数 local reqParams = lreqparm.getRequestParam() -- 定义本地缓存 local html_template_cache = ngx.shared.html_template_cache -- 获取请求ID的参数 local reqId = reqParams["id"]; ngx.log(ngx.INFO, "requestID:", reqId); -- 校验参数 if reqId==nil then ngx.say("缺少ID参数"); return end -- 布隆过滤器检查id是否存在 local bfexist = lrredis.bfexists("bf_taxi",reqId) ngx.log(ngx.INFO, "布隆过滤器检验:", bfexist) -- 校验key不存在直接返回 if bfexist==0 then ngx.say("布隆过滤器校验key不存在...") return end -- 拼接hget的key local hgetkey = "hkey_".. reqId -- 通过hget获取map的所有数据 local templateData = lrredis.hgetall(hgetkey); if next(templateData) == nil then ngx.say("redis没有存储数据...") return end --获取模板价格数据 local amount = templateData["amount"] ngx.log(ngx.INFO, "amount:", amount) if amount == nil then ngx.say("价格数据未配置"); return end -- 获取本地缓存对象 ngx.log(ngx.INFO, "开始从缓存中获取模板数据----") local html_template = html_template_cache:get(reqId) -- 判断本地缓存是否存在 if html_template == nil then -- 获取模板url中的数据 ngx.log(ngx.INFO, "缓存中不存在数据开始远程获取模板") local url = templateData["url"] ngx.log(ngx.INFO, "从缓存中获取的url地址:", url) if url == nil then ngx.say("URL路径未配置"); return end -- 抓取远程url的html ngx.log(ngx.INFO, "开始抓取模板数据:", url) local returnResult = lgethtml.gethtml(url); -- 判断抓取模板是否正常 if returnResult==nil then ngx.say("抓取URL失败..."); return end -- 判断html状态 if returnResult.status==200 then html_template = returnResult.body --设置模板缓存为一小时 ngx.log(ngx.INFO, "将模板数据加入到本地缓存") html_template_cache:set(reqId,html_template,60 * 60) end end ngx.log(ngx.INFO, "缓存中获取模板数据结束----") -- 模板渲染 --编译得到一个lua函数 local func = template.compile(html_template) local context = {amount=amount} ngx.log(ngx.INFO, "开始渲染模板数据") --执行函数,得到渲染之后的内容 local content = func(context) --通过ngx API输出 ngx.say(content)
2.2 编写nginx配置
2.2.1 添加本地缓存
在nginx主配置文件中添加模板缓存
x lua_shared_dict html_template_cache 10m;
2.2.2 编写nginx配置文件
我们这里使用
content_by_lua_file
的方式执行脚本
x vi template.conf x server { listen 8888; charset utf-8; # 配置路径重写 location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ { rewrite ^/(.*) http://www.resources.com/$1 permanent; } #删除本地缓存 location /delete { default_type text/html; content_by_lua ' local lreqparm = require("requestUtils") --获取请求参数 local reqParams = lreqparm.getRequestParam() -- 定义本地缓存 local html_template_cache = ngx.shared.html_template_cache -- 获取请求ID的参数 local reqId = reqParams["id"]; ngx.log(ngx.INFO, "requestID:", reqId); -- 校验参数 if reqId==nil then ngx.say("缺少ID参数"); return end -- 获取本地缓存对象 html_template_cache:delete(reqId); ngx.say("清除缓存成功"); '; } location /template { default_type text/html; content_by_lua_file /usr/local/openresty/script/requestTemplateRendering.lua; } }
2.3 初始化数据
x # 进入一个redis集群的节点内部 docker exec -ti redis01 /bin/bash # 以集群方式登录172.18.0.2:3306节点 redis-cli -h 172.18.0.2 -c # 在redis中添加一个布隆过滤器 错误率是0.01 数量是1万个 BF.RESERVE bf_taxi 0.01 10000 NONSCALING # 在bf_test 的布隆过滤器添加一个key BF.ADD bf_taxi 1 #检查数据是否存在 BF.EXISTS bf_taxi 1 # 添加URL以及价格 hset hkey_1 url http://127.0.0.1/template.html amount 100.00