nginx+lua+redis实现广告缓存
需求分析
需要在页面上显示广告的信息。
OpenResty
OpenResty介绍
OpenResty(又称:ngx_openresty) 是一个基于 NGINX 的可伸缩的 Web 平台,由中国人章亦春发起,提供了很多高质量的第三方模块。
OpenResty 是一个强大的 Web 应用服务器,Web 开发人员可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,更主要的是在性能方面,OpenResty可以 快速构造出足以胜任 10K 乃至1000K以上并发连接响应的超高性能 Web 应用系统。
360,UPYUN,阿里云,新浪,腾讯网,去哪儿网,酷狗音乐等都是 OpenResty 的深度用户。
OpenResty 简单理解,就相当于封装了nginx,并且集成了LUA脚本,开发人员只需要简单的其提供了模块就可以实现相关的逻辑,而不再像之前,还需要在nginx中自己编写lua的脚本,再进行调用了。
OpenResty安装
linux安装openresty:
1.添加仓库执行命令
yum install yum-utils yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
2.执行安装
yum install openresty
3.安装成功后 会在默认的目录如下:
/usr/local/openresty
安装nginx
默认已经安装好了nginx,在目录:/usr/local/openresty/nginx 下。
修改/usr/local/openresty/nginx/conf/nginx.conf ,将配置文件使用的根设置为root,目的就是将来要使用lua脚本的时候 ,直接可以加载在root下的lua脚本。
#user nobody; 配置文件第一行原来为这样, 现改为下面的配置
user root root;
实现思路
表结构分析
tb_ad (广告表)
字段名称 | 字段含义 | 字段类型 | 字段长度 | 备注 |
---|---|---|---|---|
id | ID | INT | ||
name | 广告名称 | VARCHAR | ||
position | 广告位置 | VARCHAR | 系统定义 | |
start_time | 开始时间 | DATETIME | ||
end_time | 到期时间 | DATETIME | ||
status | 状态 | CHAR | 0:无效 1:有效 | |
image | 图片地址 | VARCHAR | ||
url | URL | VARCHAR | ||
remarks | 备注 | VARCHAR | ||
web_index_lb | 首页轮播图 | |||
web_index_amusing | 有趣区 | |||
web_index_ea_lb | 家用电器楼层轮播图 | |||
web_index_ea | 家用电器楼层广告 | |||
web_index_mobile_lb | 手机通讯楼层轮播图 | |||
web_index_mobile | 手机通讯楼层广告 |
缓存预热与二级缓存查询
步骤一:编写lua脚本实现缓存预热(将mysql里的数据查询出来存入redis)
步骤二:编写lua脚本实现二级缓存读取
代码实现
缓存预热
实现思路:
定义请求:用于查询数据库中的数据更新到redis中。
(1)连接mysql ,按照广告分类ID读取广告列表,转换为json字符串。
(2)连接redis,将广告列表json字符串存入redis 。
定义请求:
请求: /ad_update 参数: position --指定广告位置 返回值: json
在/root/lua目录下创建ad_load.lua ,实现连接mysql 查询数据 并存储到redis中。
ngx.header.content_type="application/json;charset=utf8" local cjson = require("cjson") local mysql = require("resty.mysql") local uri_args = ngx.req.get_uri_args() local position = uri_args["position"] local db = mysql:new() db:set_timeout(1000) local props = { host = "192.168.200.128", port = 3306, database = "changgou_business", user = "root", password = "root" } local res = db:connect(props) local select_sql = "select url,image from tb_ad where status ='1' and position='"..position.."' and start_time<= NOW() AND end_time>= NOW()" res = db:query(select_sql) db:close() local redis = require("resty.redis") local red = redis:new() red:set_timeout(2000) local ip ="192.168.200.128" local port = 6379 red:connect(ip,port) red:set("ad_"..position,cjson.encode(res)) red:close() ngx.say("{flag:true}")
修改/usr/local/openresty/nginx/conf/nginx.conf文件:
代码如下:
#user nobody; user root root; worker_processes 1; #error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; #tcp_nopush on; #keepalive_timeout 0; keepalive_timeout 65; #gzip on; server { listen 80; server_name localhost; charset utf-8; #access_log logs/host.access.log main; # 添加 location /ad_update { content_by_lua_file /root/lua/ad_update.lua; } # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
重新启动nginx
测试:http://192.168.200.128/ad_update?position=web_index_lb
广告缓存读取
实现思路:
通过lua脚本直接从redis中获取数据即可。
定义请求:
请求:/ad_read
参数:position
返回值:json
在/root/lua目录下创建ad_read.lua
ngx.header.content_type="application/json;charset=utf8" local uri_args = ngx.req.get_uri_args(); local position = uri_args["position"]; local redis = require("resty.redis"); local red = redis:new() red:set_timeout(2000) local ok, err = red:connect("192.168.200.128", 6379) local rescontent=red:get("ad_"..position) ngx.say(rescontent) red:close()
在/usr/local/openresty/nginx/conf/nginx.conf中server下添加配置
location /ad_read { content_by_lua_file /root/lua/ad_read.lua; }
测试 http://192.168.200.128/ad_read?position=web_index_lb 输出
[{"url":"img\/banner1.jpg","image":"img\/banner1.jpg"},{"url":"img\/banner2.jpg","image":"img\/banner2.jpg"}]
二级缓存-加入openresty本地缓存
如上的方式没有问题,但是如果请求都到redis,redis压力也很大,所以我们一般采用多级缓存的方式来减少下游系统的服务压力。
先查询openresty本地缓存 如果没有再查询redis中的数据
- 修改/root/lua目录下ad_read文件, 内容如下:
ngx.header.content_type="application/json;charset=utf8" local uri_args = ngx.req.get_uri_args(); local position = uri_args["position"]; local cache_ngx = ngx.shared.dis_cache; local adCache = cache_ngx:get('ad_cache_'..position); if adCache == "" or adCache == nil then local redis = require("resty.redis"); local red = redis:new() red:set_timeout(2000) local ok, err = red:connect("192.168.200.128", 6379) local rescontent=red:get("ad_"..position) ngx.say(rescontent) red:close() cache_ngx:set('ad_cache_'..position, rescontent, 10*60); else ngx.say(adCache) end
- 修改nginx配置文件vi /usr/local/openresty/nginx/conf/nginx.conf ,http节点下添加配置:
#包含redis初始化模块
lua_shared_dict dis_cache 5m; #共享内存开启
前端页面实现(了解)
(1)修改index.html,编写脚本
<script> new Vue({ el: '#app', data: { ad: { web_index_lb:[] } }, methods: { adRead: function(position) { axios.get('ad_read?position='+position).then(response =>{ this.ad[position]=response.data }) } }, created(){ this.adRead('web_index_lb') } }) </script>
在页面上添加<div id='app'> ... </div>
(2)修改index.html,渲染广告轮播图
<div id="myCarousel" data-ride="carousel" data-interval="4000" class="sui-carousel slide"> <ol class="carousel-indicators"> <li data-target="#myCarousel" data-slide-to="0" class="active" v-for="item in ad.web_index_lb"></li> </ol> <div class="carousel-inner" id="lbt"> <div class="item" v-for="item in contentList"> <a :href="item.url"> <img :src="item.pic" /> </a> </div> </div> <a href="#myCarousel" data-slide="prev" class="carousel-control left">‹</a> <a href="#myCarousel" data-slide="next" class="carousel-control right">›</a> </div>
(3)上传至服务器并测试
更改
# 加载首页 location / { root html; index index.html index.htm; }