lua、Canal实现广告缓存
这一部分先不搞 ,太麻烦了,跳过
一、介绍
Lua语法输出、变量定义、数据类型、流程控制、循环操作、函数、表、模块
OpenResty介绍:封装Nginx、并提供了Lua扩展、大大提升了Nginx对并发处理的能力
Nginx漏斗限流:控制速率、控制并发量
Canal实现数据同步操作——>Mysql Canal实现首页缓存同步
二、表结构与缓存结构
首页广告表结构设计
广告分类表:
CREATE TABLE `tb_content_category` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '类目ID',
`name` varchar(50) DEFAULT NULL COMMENT '分类名称',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='内容分类';
广告表:
CREATE TABLE `tb_content` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`category_id` bigint(20) NOT NULL COMMENT '内容类目ID',
`title` varchar(200) DEFAULT NULL COMMENT '内容标题',
`url` varchar(500) DEFAULT NULL COMMENT '链接',
`pic` varchar(300) DEFAULT NULL COMMENT '图片绝对路径',
`status` varchar(1) DEFAULT NULL COMMENT '状态,0无效,1有效',
`sort_order` int(11) DEFAULT NULL COMMENT '排序',
PRIMARY KEY (`id`) USING BTREE,
KEY `category_id` (`category_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
首页广告缓存结构
1.查询Nginx缓存,如果有缓存,则直接将缓存中的广告返回。
2.如果Nginx缓存中没有广告数据,则通过Lua脚本查询Redis,如果Redis中有数据,则会将数据存入到Nginx的缓存,并返回查询到的数据。
3.如果Redis中也没有緩存,则此时会通过ua脚本查询MySQL,如果MySQL 中有数据,则将数据存入到Redis中并返回查询到的数据。
三、Lua
Lua是一个小巧的脚本语言。其设计目的是为了通过灵活嵌入应用程序中从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编写成,几乎在所有操作系统和平台上都可以编译,运行。Lua并没有提供强大的库,这是由它的定位决定的。所以Lua不适合作为开发独立应用程序的语言。Lua 有一个同时进行的IT项目,提供在特定平台上的即时编译功能。
简单来说:
Lua是一-种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从
而为应用程序提供灵活的扩展和定制功能。
特性:
●支持面向过程(procedure-oriented)编程和函数式编程(functional programming);
●自动内存管理;只提供了一种通用类型的表(table) ,用它可以实现数组,哈希表,集合,对象;
●语言内置模式匹配;闭包(closure);函数也可以看做一一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;
●通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虛函数,继承和重载等。
应用场景
●游戏开发
●独立应用脚本
●Web应用脚本
●扩展和数据库插件如: MySQL Proxy和MySQL WorkBench
●I安全系统,如入侵检测系统.
●redis中嵌套调用实现类似事务的功能
●web容器中应用处理一-些过滤 缓存等等的逻辑,例如nginx。
安装Lua
curl -R -O http://www.lua.org/ftp/lua-5.3.5.tar.gz
tar zxf lua-5.3.5.tar.gz
cd lua-5.3.5
make linux test
安装失败:
需要安装lua相关依赖库的支持:
yum install libtermcap-devel ncurses-devel libevent-devel readline-devel
再次 make linux test
安装成功!
简单使用Lua
type: 函数 输出对象类型
流程控制
if语句
语法:
if(Boolean)
then
end
if…else语句
if(Boolean)
then
else
end
循环
while循环
while()
do
end
函数
> function max(num1,num2)
if(num1>=num2)
then
result = num1
else
result = num2
end
return result
end
> print(max(10,100))
100
>
注意 : ..
两个点代表拼接,使用逗号也可以 ,只是间隔有点大。
模块
-- 定义一个模块
module = {
}
module.username = "张三"
--定义一个全局方法
function module.fun1()
print("fun1")
end
local function fun2()
print("fun2")
end
function module.fun3()
fun2()
end
return module
-- 引入nodule
require("module")
print(module.username)
module.fun1()
module.fun3()
四、Openresty
介绍
OpenResty(又称: ngx_ openresty) 是一个基于nginx的可伸缩的Web平台,由中国人章亦春发起,提供了很多高质量的第三方模块。OpenResty是一个强大的Web应用服务器,Web开发人员可以使用Lua脚本语言调动Nginx支持的各种C以及Lua模块,更主要的是在性能方面,OpenResty可以 快速构造出足以胜任10K以上并发连接响应的超高性能Web应用系统。360,UPYUN,阿里云,新浪,腾讯网,去哪儿网,酷狗音乐等都是OpenResty的深度用户。
OpenResty简单理解成就相当于封装了nginx,并且集成了LUA脚本,开发人员只需要简单的其提供了模块就可以实现相关的逻辑,而不再像之前,还需要在nginx中自己编写lua的脚本,再进行调用了。
安装openresty
yum install yum-utils
yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
yum install openresty
安装完成后会在/usr/local/openresty目录下
进入/openresty/nginx/sbin ./nginx 浏览器直接访问
五、广告缓存的载入与读取(重点)
将MySql数据写入Redis缓存中
用于查询数据库中的数据更新到redis中。
(1)连接mysql ,按照广告分类ID读取广告列表,转换为json字符串。
(2)连接redis,将广告列表json字符串存入redis 。
请求:/ad_update
参数:position ‐‐指定广告位置
返回值:json
在/root/lua目录下创建update_content.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 id = uri_args["id"]
local db = mysql:new()
db:set_timeout(1000)
local props = {
host = "116.62.13.104",
port = 3306,
database = "changgou_content",
user = "root",
password = "123456"
}
local res = db:connect(props)
local select_sql = "select url,pic from tb_content where status ='1' and category_id="..id.." order by sort_order"
res = db:query(select_sql)
db:close()
local redis = require("resty.redis")
local red = redis:new()
red:set_timeout(2000)
local ip ="116.62.13.104"
local port = 6379
red:connect(ip,port)
red:set("content_"..id,cjson.encode(res))
red:close()
ngx.say("{flag:true}")
修改/usr/local/openresty/nginx/conf/nginx.conf文件:
# 将用户请求/update_content?id=1 将该请求给 /root/lua/下的update_content.lua脚本
location /update_content {
content_by_lua_file /root/lua/update_content.lua;
}
测试
浏览器中输入:update_content?id=1
路径被nginx拦截到,执行/root/lua下的update_content.lua脚本,查询mysql数据库,将数据写入到redis中
广告缓存读取
流程:
先加载本地Nginx缓存
如果本地Nginx缓存没有就去Redis缓存找,Redis中有缓存的话再将缓存加入到本地Nginx缓存中
如果Redis缓存中没有的话就查询Mysql数据库,将数据加入Redis缓存中、加入到Nginx缓存。
定义Nginx缓存模块
通过lua脚本直接从redis中获取数据即可。
在/root/lua目录下创建ad_read.lua
请求:/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 id = uri_args["id"];
--获取本地缓存
local cache_ngx = ngx.shared.dis_cache;
--根据ID 获取本地缓存数据
local contentCache = cache_ngx:get('content_cache_'..id);
ngx.say(contentCache)
if contentCache == "" or contentCache == nil then
local redis = require("resty.redis");
local red = redis:new()
red:set_timeout(2000)
red:connect("116.62.13.104", 6379)
local rescontent=red:get("content_"..id)
if ngx.null == rescontent then
local cjson = require("cjson");
local mysql = require("resty.mysql");
local db = mysql:new();
db:set_timeout(2000)
local props = {
host = "116.62.13.104",
port = 3306,
database = "changgou_content",
user = "root",
password = "123456"
}
local res = db:connect(props);
local select_sql = "select url,pic from tb_content where status = '1' and category_id="..id.." order by sort_order";
res = db:query(select_sql);
local responsejson = cjson.encode(res);
red:set("content_"..id,responsejson);
ngx.say(responsejson);
db:close();
else
cache_ngx:set('content_cache_'..id,rescontent,10*60);
ngx.say(rescontent);
end
red:close();
else
ngx.say(contentCache)
end
修改nginx配置文件:
# 将用户请求/update_content?id=1 将该请求给 /root/lua/下的update_content.lua脚本
location /read_content {
content_by_lua_file /root/lua/read_content.lua;
}
测试
清空Redis中的缓存
修改一下便于测试。
在浏览器中输入http://116.62.13.104/read_content?id=1
回车
再次回车:
这个时候加载的就是Nginx中的缓存,当Nginx中的缓存10分钟过期是,在此请求就会查询Redis中的缓存。
前端页面实现(了解)
(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;
}
六、nginx限流
一般情况下,首页的并发量是比较大的,即使有了多级缓存,如果有大量恶意的请求,也会对系统造成影响。而限流就是保护措施之一。 nginx提供两种限流的方式:
一是控制速率
控制速率的方式之一就是采用漏桶算法。
漏桶(Leaky Bucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接 口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请 求,可以看出漏桶算法能强行限制数据的传输速率.
配置限流的缓存空间
修改/usr/local/openresty/nginx/conf/nginx.conf:
#限流设置 $binary_remote_addr根据IP进行限流 contentRateLimit 缓存空间名称 rate速率 两个请求 每秒
limit_req_zone $binary_remote_addr zone=contentRateLimit:10m rate=2r/s;
location /read_content {
# 使用限流配置
limit_req zone=contentRateLimit;
content_by_lua_file /root/lua/read_content.lua;
}
测试:
当我们频繁刷新的时候就会出现以下错误页面:
burst=4 表示 当速率是每秒只能处理两个请求时,来了4个请求,另外两个会放入队列中,并不会返回错误页面 ,等到前两个请求执行完后再执行后两个请求。
nodelay 表示无延迟,可以并行处理请求
完整代码:
location /read_content {
# 使用限流配置
limit_req zone=contentRateLimit burst=4 nodelay;
content_by_lua_file /root/lua/read_content.lua;
}
二是控制并发连接数
# 根据IP地址来进行限制
limit_conn_zone $binary_remote_addr zone=addr:1m;
location /brand {
# limit_conn addr 2;
proxy_pass http://172.30.0.68:18081;
}
七、canal同步广告
cana可以用来监控数据库数据的变化,从而获得新增数据,或者修改的数据。
canal是应阿里巴巴存在杭州和美国的双机房部署,存在跨机房同步的业务需求而提出的。
阿里系公司开始逐步的尝试基于数据库的日志解析,获取增量变更进行同步,由此衍生出了增量订阅&消费的业
务。
mysql开启binlog模式:
# 下载镜像
docker pull docker.io/canal/canal-server
# 容器安装
docker run -p 11111:11111 --name canal -d docker.io/canal/canal-server
# 进入容器 修改核心配置文件canal.properties 和 instance.properties
docker exec -it canal /bin/bash
cd canal-server/conf/
vi canal.properties
cd example/
vi instance.properties
这一部分先不搞 ,太麻烦了,跳过