OpenResty+lua+redis实现广告缓存
一、需求
ngx_openresty是一个基于 NGINX的lua可编程模块,在性能方面有着出色的性能,配合redis做二级缓存效果,nginx开启一级本地缓存。
实验数据库sql
/*
Navicat Premium Data Transfer
Source Server : 本机mysql
Source Server Type : MySQL
Source Server Version : 50728
Source Host : localhost:3306
Source Schema : demo
Target Server Type : MySQL
Target Server Version : 50728
File Encoding : 65001
Date: 25/05/2020 10:58:31
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for tb_ad
-- ----------------------------
DROP TABLE IF EXISTS `tb_ad`;
CREATE TABLE `tb_ad` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '广告主键',
`url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT 'URL',
`status` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '0:无效 1:有效',
`position` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '广告位置',
`image` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '图片路径',
`start_time` datetime(0) NULL DEFAULT NULL COMMENT '开始时间',
`end_time` datetime(0) NULL DEFAULT NULL COMMENT '到期时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of tb_ad
-- ----------------------------
INSERT INTO `tb_ad` VALUES (1, 'https://www.baidu.com/', '1', 'web_index_lb', 'https://kins.oss-cn-shenzhen.aliyuncs.com/yhzb/2020-03-11/ca21b3b17d6f4757b991dd86b8cef3fa-VIP-680.jpeg', '2020-05-22 10:58:08', '2021-06-01 10:58:14');
SET FOREIGN_KEY_CHECKS = 1;
二、一二级缓存实现
查询mysql中上架的所有广告转换为json字符串,放入redis中作为二级缓存,等真正的第一次查询会把缓存加入一级本地缓存。当广告修改时候需要请求,需要请求预热接口。
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;
#包含redis初始化模块
lua_shared_dict dis_cache 5m; #共享内存开启
server {
listen 80;
server_name localhost;
charset utf-8;
#access_log logs/host.access.log main;
# 添加预热和读取接口
location /ad_loading{
content_by_lua_file /root/lua/ad_loading.lua;
}
location /ad_read {
content_by_lua_file /root/lua/ad_read.lua;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
ad_loading.lua预热脚本
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 = "127.0.0.1",
port = 3306,
database = "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 ="127.0.0.1"
local port = 6379
red:connect(ip,port)
--Redis Authentication
local result, err = red:auth("redis")
if not result then
ngx.say("failed to authenticate: ", err)
return
end
red:set("ad_"..position,cjson.encode(res))
red:close()
ngx.say("{flag:true}")
ad_read.lua读取脚本
--设置响应头类型
ngx.header.content_type="application/json;charset=utf8"
--获取请求中的参数ID
local uri_args = ngx.req.get_uri_args();
local position = uri_args["position"];
--获取本地缓存
local cache_ngx = ngx.shared.dis_cache;
--根据ID 获取本地缓存数据
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("127.0.0.1", 6379)
--Redis Authentication
local result, err = red:auth("redis")
if not result then
ngx.say("failed to authenticate: ", err)
return
end
local rescontent=red:get("ad_"..position)
ngx.say(rescontent)
red:close()
--将redis中获取到广告数据存入nginx本地缓存
cache_ngx:set('ad_cache_'..position, rescontent, 10*60);
else
--nginx本地缓存中获取到数据直接输出
ngx.say(adCache)
end
三、拓展方向
lua + nginx可以作为网关进行限流、流控和多级缓存使用,内存小可用性也非常高,配合一些lua模块可以做一些深入拓展。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。