- 目标1:掌握清理Nginx缓存
- 目标2:掌握Lua流程控制语法
- 目标3:能实现Nginx+Lua多级缓存控制
- 目标4:掌握红包雨的缓存架构设计
- 目标5:掌握队列溢出控制设计方案
- 目标6:掌握队列削峰填谷的设计思想
- 目标7:Nginx限流
1 Nginx缓存清理
很多时候我们如果不想等待缓存的过期,想要主动清除缓存,可以采用第三方的缓存清除模块清除缓存nginx_ngx_cache_purge 。安装nginx的时候,需要添加 purge 模块, purge 模块我们已经下载了,在 /usr/local/server 目录下,添加该模块 –add-module=/usr/local/server/ngx_cache_purge-2.3/ ,这一个步骤我们在安装 OpenRestry 的时候已经实现了。
安装好了后,我们配置一个清理缓存的地址:
这里 $1 代表的是 (/.*) 的数据。
每次请求 $host$key 就可以删除指定缓存,我们可以先查看缓存文件的可以:
此时访问 <http://192.168.211.141/purge/user/wangwu>
此时再查看缓存文件,已经删除了。
参数说明:
2 Lua高性能脚本语言
2.1 Lua介绍
Lua是一门以其性能著称的脚本语言,被广泛应用在很多方面。Lua一般用于嵌入式应用,现在越来越多应用于游戏当中,魔兽世界,愤怒的小鸟都有用到。
优势:
Lua脚本的作用:嵌入到应用程序中,给应用程序提供扩展功能。
2.2 Lua安装
依赖安装: yum install libtermcap-devel ncurses-devel libevent-devel readline-devel pcre-devel gccopenssl openssl-devel
下载安装包: curl -R -O http://www.lua.org/ftp/lua-5.3.5.tar.gz
解压安装: tar zxf lua-5.3.5.tar.gz
安装:
此时原来系统自带的lua其实是5.1.4,我们需要替换原来系统自带的lua:
查看版本
2.3 Lua常用知识
2.3.1 Lua案例
创建hello.lua文件,内容为
编辑文件hello.lua
在文件中输入:
保存并退出。
执行命令
输出为:
效果如下:
lua有交互式编程(在控制台编写代码)和脚本式编程(在文件中写代码叫脚本编程)。
交互式编程就是直接输入语法,就能执行。
脚本式编程需要编写脚本,然后再执行命令 执行脚本才可以。
一般采用脚本式编程。(例如:编写一个hello.lua的文件,输入文件内容,并执行lua hell.lua即可)
(1)交互式编程
Lua 提供了交互式编程模式。我们可以在命令行中输入程序并立即查看效果。
Lua 交互式编程模式可以通过命令 lua -i 或 lua 来启用:
如下图:
(2)脚本式编程
我们可以将 Lua 程序代码保持到一个以 lua 结尾的文件,并执行,该模式称为脚本式编程,例如上面入门程序中将lua语法写到hello.lua文件中。
2.3.2 Lua语法
注释
一行注释:两个减号是单行注释:
多行注释
定义变量
全局变量,默认的情况下,定义一个变量都是全局变量,
如果要用局部变量 需要声明为local.例如:
如果变量没有初始化:则 它的值为nil 这和java中的null不同。
如下图案例:
Lua中的数据类型
Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。
Lua 中有 8 个基本类型分别为:nil、boolean、number、string、userdata、function、thread 和 table。
lua红有个type函数,能出对象的类型。
实例:
2.3.3 流程控制
if语句
Lua if 语句 由一个布尔表达式作为条件判断,其后紧跟其他语句组成。
语法:
实例:
if..else语句
Lua if 语句可以与 else 语句搭配使用, 在 if 条件表达式为 false 时执行 else 语句代码块。
语法:
实例:
循环[练习]
while循环[满足条件就循环]
Lua 编程语言中 while 循环语句在判断条件为 true 时会重复执行循环体语句。 语法:
实例:
效果如下:
for循环[练习]
Lua 编程语言中 for 循环语句可以重复执行指定语句,重复次数可在 for 语句中控制。
语法: 1->10 1:exp1 10:exp2 2:exp3:递增的数量
var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 “执行体”。exp3 是可选的,如果不指定,默认为1。
例子:
for i=1,9,2 :i=1从1开始循环,9循环数据到9结束,2每次递增2
repeat…until语句[满足条件结束][练习]
Lua 编程语言中 repeat…until 循环语句不同于 for 和 while循环,for 和 while 循环的条件语句在当前循环执行开始时判断,而 repeat…until 循环的条件语句在当前循环结束后判断。
语法:
案例:
函数
lua中也可以定义函数,类似于java中的方法。例如:
执行之后的结果:
..:表示拼接
表
table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数组、字典等。
Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。
案例:
模块
(1)模块定义
模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
创建一个文件叫module.lua,在module.lua中创建一个独立的模块,代码如下:
由上可知,模块的结构就是一个 table 的结构,因此可以像操作调用 table 里的元素那样来操作调用模块里的常量或函数。
上面的 func2 声明为程序块的局部变量,即表示一个私有函数,因此是不能从外部访问模块里的这个私有函数,必须通过模块里的公有函数来调用.
(2)require 函数
require 用于 引入其他的模块,类似于java中的类要引用别的类的效果。
用法:
两种都可以。
我们可以将上面定义的module模块引入使用,创建一个test_module.lua文件,代码如下:
2 多级缓存架构
任何项目中我们都有一些频繁的查询,而那些频繁的查询数据基本都是相同的,比如项目中查看用户个人信息,购物狂欢查询活动信息,这些功能点使用频率非常高,我们一般需要对他们做特殊处理。
我们以狂欢活动信息为例,当双十一的时候,很多人会去查看今天优惠活动有哪些,对这些活动信息我们可以采用多级缓存架构来抗住高并发。
2.1 多级缓存架构对比
基于Java版的缓存架构实现流程如下:
优点:
缺点:
优点:
2.2 Nginx+Lua多级缓存实战
我们以双十一活动信息加载为例,通过多级缓存架构来加载双十一活动信息,双十一活动信息表结构如下:
2.2.1 链接MySQL封装
创建一个lua脚本 mysql.lua ,用于提供根据SQL语句执行查询操作,代码如下:
2.2.2 链接Redis集群封装
我们需要安装 lua-resty-redis-cluster ,用该依赖库实现Redis集群的操作,这里提供github地址,大家下载后按操作配置即可,下载地址: <https://github.com/cuiweixie/lua-resty-redis-cluster> ,下载该文件配置后即可实现Redis集群操作。
接下来我们实现Redis集群操作,创建一个脚本 redis.lua ,脚本中实现Redis中数据的查询和添加操作,代码如下:
2.2.3 多级缓存操作
配置创建Nginx缓存共享空间
创建多级缓存操作脚本 activity.lua ,代码如下:
Nginx配置:
3 红包雨案例剖析
每次在双十一或者618的时候,各大电商平台为了吸引用户,都会给与用户很大的优惠,比如送优惠券、抢红包等,抢优惠券或者红包的用户群体非常大,这时候会给服务器带来的并发也是非常大,解决这块问题,架构是关键。
3.1 抢红包案例分析
双十一抢红包或者过年微信、QQ红包雨活动的并发量非常庞大,红包雨存在的规则也是非常多的,我们先来分析一下红包雨的特点。
3.2 红包雨多级缓存架构设计
上面我们已经分析过红包雨的特点,要想实现一套高效的红包雨系统,缓存架构是关键。我们根据红包雨的特点设计了如上图所示的红包雨缓存架构体系。
4 缓存队列-并发溢出高效控制设计
并发量非常大的系统,例如秒杀、抢红包、抢票等操作,都是存在溢出现象,比如秒杀超卖、抢红包超额、一票多单等溢出现象,如果采用数据库锁来控制溢出问题,效率非常低,在高并发场景下,很有可能直接导致数据库崩溃,因此针对高并发场景下数据溢出解决方案我们可以采用Redis缓存提升效率。
4.1 设计分析
用户抢红包的时候,我们会分批次发放红包,每次发放红包会先根据发放红包的金额和红包的个数把每个红包计算好,然后存入到Redis队列中,存入到Redis队列后,用户每次抢红包都直接从Redis队列中获取一个红包即可,由于Redis是单线程,在这里可以有效的避免多个人同时抢到了一个红包,类似一种超卖现象。
表结构设计:
4.2 红包定时导入缓存队列
初始化读取
创建容器监听类 com.itheima.quartz.MoneyPushTask ,让该类实现接口 ApplicationListener ,当容器初始化完成后,会调用 onApplicationEvent 方法, 我们可以在该方法中实现初始化读取数据,将数据填充到变量中。
MoneyPushTask 代码如下:
定时加载
定时加载我们需要开
抢红包缓存队列溢出控制
上面已经实现将红包存入到Redis缓存队列中,用户每次抢红包的时候,只需要从Redis缓存队列中获取即可。com.itheima.controller.RedPacketController 代码:
com/itheima/service/RedPacketService.java 代码:
com.itheima.service.impl.RedPacketServiceImpl 代码:
5 队列术限流
5.1 高并发场景分析
用户抢红包的高并发场景下,如果让后端服务器直接处理所有抢红包操作,服务器很有可能会崩溃,就像高铁站如果很多人蜂拥挤进站而不排队,很有可能导致整个高铁站秩序混乱,高铁站服务崩溃,程序也是如此。
解决大量并发用户蜂拥而上的方法可以采用队列术将用户的请求用队列缓存起来,后端服务从队列缓存中有序消费,可以防止后端服务同时面临处理大量请求。缓存用户请求可以用RabbitMQ、Kafka、RocketMQ、ActiveMQ,我们这里采用RabbitMQ即可。
5.2 队列削峰实战
用户抢红包的时候,我们用Lua脚本实现将用户抢红包的信息以生产者角色将消息发给RabbitMQ,后端应用服务以消费者身份从RabbitMQ获取消息并抢红包,再将抢红包信息以WebSocket方式通知给用户。
如果想使用Lua识别用户令牌,我们需要引入 lua-resty-jwt 模块,是用于 ngx_lua 和 LuaJIT 的 Lua 实现库,在该模块能实现Jwt令牌生成、Jwt令牌校验,依赖库的地址: https://github.com/SkyLothar/lua-resty-jwt
lua-resty-jwt安装
在 资料\lua 中已经下载好了该依赖库 lua-resty-jwt-master.zip ,我们将该库文件上传到服务器上,并解压,当然,我们也可以使用opm直接安装 lua-resty-jwt ,配置 lua-resty-jwt 之前,我们需要先安装resty和opm。
安装仓库管理工具包:
添加仓库地址:
安装resty:
安装opm:
安装Jwt组件:
此时 lua-resty-jwt 安装好了,可以直接使用了。
令牌校验库token.lua
抢红包队列脚本:mq.lua,参考地址 <https://github.com/wingify/lua-resty-rabbitmqstomp>
抢红包队列脚本:mq.lua,参考地址 <https://github.com/wingify/lua-resty-rabbitmqstomp>
Java监听
5.3 抢红包测试
打开资料中的 html/websocket.html ,执行抢红包测试,效果如下:
6 Nginx限流(作业)
一般情况下,双十一很多查询的并发量是比较大的,即使 有了多级缓存,当用户不停的刷新页面的时候,也是没有必要的,另外如果有恶意的请求 大量达到,也会对系统造成影响。而限流就是保护措施之一。
6.1 限流理解
限流和生活中很多现实场景类似,如下场景:
- 水坝泄洪,通过闸口限制洪水流量(控制流量速度)。
- 办理银行业务:所有人先领号,各窗口叫号处理。每个窗口处理速度根据客户具体业务而定,所有人排队等待叫号即可。若快下班时,告知客户明日再来(拒绝流量)
- 火车站排队买票安检,通过排队 的方式依次放入。(缓存带处理任务)
而程序限流,主要是控制应对高并发和恶意操作的一种技术手段。
6.2 Nginx限流实战
nginx提供两种限流的方式:
- 一是控制速率
- 二是控制并发连接数
6.2.1 速率限流
控制速率的方式之一就是采用漏桶算法。
(1)漏桶算法实现控制速率限流
漏桶(Leaky Bucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率.示意图如下:
(2)nginx的配置
配置示如下:
创建限流缓存空间:
配置限流:
参数说明:
如果恶意一直刷新会出现如下现象:
(3)处理突发流量
上面例子限制 2r/s,如果有时正常流量突然增大,超出的请求将被拒绝,无法处理突发流量,可以结合 burst 参数使用来解决该问题。
例如,如下配置表示:
burst 译为突发、爆发,表示在超过设定的处理速率后能额外处理的请求数,当 rate=10r/s 时,将1s拆成10份,即每100ms可处理1个请求。
此处,burst=4 ,若同时有4个请求到达,Nginx 会处理第一个请求,剩余3个请求将放入队列,然后每隔500ms从队列中获取一个请求进行处理。若请求数大于4,将拒绝处理多余的请求,直接返回503.
不过,单独使用 burst 参数并不实用。假设 burst=50 ,rate依然为10r/s,排队中的50个请求虽然每100ms会处理一个,但第50个请求却需要等待 50 * 100ms即 5s,这么长的处理时间自然难以接受。
因此,burst 往往结合 nodelay 一起使用。
例如:如下配置:
如上表示:
平均每秒允许不超过2个请求,突发不超过4个请求,并且处理突发4个请求的时候,没有延迟,等到完成之后,按照正常的速率处理。
完整配置如下:
6.2.2 控制并发量(连接数)
ngx_http_limit_conn_module 提供了限制连接数的能力。主要是利用limit_conn_zone和limit_conn两个指令。利用连接数限制 某一个用户的ip连接的数量来控制流量。
注意:并非所有连接都被计算在内 只有当服务器正在处理请求并且已经读取了整个请求头时,才会计算有效连接。此处忽略测试。
配置语法:
(1)配置限制固定连接数
如下,配置如下:
配置限流缓存空间:
location配置:
参数说明:
(2)限制每个客户端IP与服务器的连接数,同时限制与虚拟服务器的连接总数。
限流缓存空间配置:
location配置
每个IP限流 3个
总量5个
版权声明:本文图片和内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送联系客服 举报,一经查实,本站将立刻删除,请注明出处:https://www.4kpp.com/18719.html