前言:
- 默认已经安装好 lua和openResty和nginx
不会吧!不会吧! 还真的有人不会装吧。 我给兄弟们指一条路。在我下一篇博客将详细介绍(小声比比:装不好别怪我,怪你太菜)手动狗头保命 - 在互联网公司,Nginx可以说是标配组件,但是主要场景还是负载均衡、反向代理、代理缓存、限流等场景;而把Nginx作为一个Web容器使用的还不是那么广泛。Nginx开发主要是以C/C++模块的形式进行,我靠这学习成本也太高了, 如果有一种简单的语言来实现Web应用的开发,那么Nginx绝对是把好的瑞士军刀, Nginx团队也开始意识到这个问题,所以目前市面上用的非常成熟的扩展是由(中国)章亦春将Lua和Nginx粘合的ngx_lua模块,并且将Nginx核心、LuaJIT、ngx_lua模块、许多有用的Lua库和常用的第三方Nginx模块组合在一起成为OpenResty,这样开发人员就可以安装OpenResty,使用Lua编写脚本,然后部署到Nginx Web容器中运行。从而非常轻松就能开发出高性能的Web服务。
- 关于nginx,Lua和OpenResty这里我就不多做介绍了,想了解的同学可以捧一下老弟的场。
- 看到这里我就怕有人说 Tomcat不香吗?我靠>>>>>>
正文:
修改/usr/local/openresty/nginx/conf/nginx.conf,将配置文件使用的根设置为root,目的就是将来要使用lua脚本的时候,直接可以加载在root下的lua脚本。
#user nobody;配置文件第一行原来为这样,现改为下面的配置
user root root;
缓存预热
实现思路:
用于查询数据库中的数据更新到redis中。
(1)连接mysql,按照广告分类ID读取广告列表,转换为json字符串。
(2)连接redis,将广告列表json字符串存入redis。
定义请求:
请求:
/ad_update
参数:
position ‐‐指定广告位置
返回值:
json
看一下图吧!
搞起来!!!
在/root/lua目录下创建ad_load.lua,实现连接mysql查询数据并存储到redis中。不要和我说不会写lua,我负责写,你负责看
相关脚本代码:
-- 指定json数据传递
ngx.header.content_type="application/json;charset=utf8"
-- 通过 require 关键字引入了两个第三方模块 require("cjson")代表支持json数据
local cjson = require("cjson")
-- require("resty.mysql")代表MySQL的致辞
local mysql = require("resty.mysql")
-- ngx.req.get_uri_args(请求路径上的参数) 赋值给一个名为 uri_args 的局部变量
local uri_args = ngx.req.get_uri_args()
-- 拿到请求参数后 拿到一个名为 position 的值 并赋值给一个名为 position 的局部变量
local position = uri_args["position"]
-- 开启MySQL的连接
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)
-- 下面这句话的意思是代表连接到MySQL后进行的查询语句 (查询tb_ad表,并设置相关条件)
local select_sql = "select url,image from tb_ad where status ='1' and position='"..position.."' and start_time<= NOW() AND end_time>= NOW()"
-- 通过db:query 将sql语句进行查询 并得到相关结果
res = db:query(select_sql)
-- 关闭数据库连接
db:close()
-- 通过 require 关键字引入 resty.redis 模块
local redis = require("resty.redis")
-- 创建Redis的连接
local red = redis:new()
-- 超时时间
red:set_timeout(2000)
-- Redis的ip
local ip ="192.168.200.128"
local port = 6379
red:connect(ip,port)
-- 向Redis 存放内容
red:set("ad_"..position,cjson.encode(res))
-- 关闭redis 的连接
red:close()
-- 随便返回一句话
ngx.say("{\"flag\":true,\"position\":\""..position.."\"}")
OK 到这里我们第一个lua脚本已经搭建完毕,
但是还差点火候,想让上面的配置文件生效, 就更改nginx 的配置文件,
修改/usr/local/openresty/nginx/conf/nginx.conf文件:
找到如下配置文件:
server {
listen 80;
server_name localhost;
charset utf-8;
#charset koi8-r;
#access_log logs/host.access.log main;
在底下添加
# 添加广告
location /ad_update {
content_by_lua_file /root/lua/ad_update.lua; # 加载root/lua/ad_update.lua文件
}
最后nginx.conf配置文件长成这样(我是不是有点瞧不起大家)
server {
listen 80;
server_name localhost;
charset utf-8;
#charset koi8-r;
#access_log logs/host.access.log main;
# 添加广告
location /ad_update {
content_by_lua_file /root/lua/ad_update.lua;
}
现在来开始测试,
等一等
不要忘记一件事:但凡我们改了nginx的配置文件就要记得重新启动nginx服务
进入 /usr/local/openresty/nginx/sbin
./nginx -s reload
这个时候应该没问题了,我们来测试一哈
测试:http://自己的ip/ad_update?position=自己数据库表中的数据
能到这一步代表大家才迈出一小步
如果执行报错或者查不到数据:相信大家都是优秀的程序员,找bug的能力还是有的
广告缓存读取
现在Redis中有数据,怎么读出来呢?接下来请看我操作!
定义请求:
请求:
/ad_read
参数:
position
返回值:
json
在/root/lua目录下创建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"];
--引入redis库
local redis = require("resty.redis");
--创建redis对象
local red = redis:new()
--设置超时时间
red:set_timeout(2000)
--连接
local ok, err = red:connect("192.168.200.128", 6379)
--获取key的值
local rescontent=red:get("ad_"..position)
--输出到返回响应中
ngx.say(rescontent)
--关闭连接
red:close()
想让上面的配置生效 ,必须修改nginx的配置文件!
在/usr/local/openresty/nginx/conf/nginx.conf中server下添加配置
server {
listen 80;
server_name localhost;
charset utf-8;
#charset koi8-r;
#access_log logs/host.access.log main;
# 添加广告
location /ad_update {
content_by_lua_file /root/lua/ad_update.lua; # 加载root/lua/ad_update.lua文件
}
# 读取广告
location /ad_read {
content_by_lua_file /root/lua/ad_read.lua;
}
既然已经修改nginx的配置文件,想让配置生效怎么操作就不说了
随随便便测试一下:http://自己的IP地址/ad_read?position=web_index_lb
效果图就是返回一串json数据
二级缓存-加入openresty本地缓存
到这一步代表着以上都没问题,但是思考一个问题,如果所有的请求都到Redis中,Redis怎能受得了,唉 英年早逝!
所以我们一般采用多级缓存的方式来减少下游系统的服务压力。先查询openresty本地缓存如果没有再查询redis中的数据。(openresty是啥不知道>>>>http:www.baidu.com)
所以接着修改lad_read脚本文件
内容如下:
--设置响应头类型
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
--引入redis库
local redis = require("resty.redis");
--创建redis对象
local red = redis:new()
--设置超时时间
red:set_timeout(2000)
--连接
local ok, err = red:connect("192.168.200.128", 6379)
--获取key的值
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
接着修改
nginx配置文件vi/usr/local/openresty/nginx/conf/nginx.conf,http节点下添 加配置:
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
#包含redis初始化模块
lua_shared_dict dis_cache 5m; #共享内存开启
既然已经修改nginx的配置文件,想让配置生效怎么操作就不说了(不知道?>>>>http:www.baidu.com)
加油!
nginx限流
一般情况下,首页的并发量是比较大的,(万一碰到那个手痒痒的频繁访问)即使有了多级缓存,如果有大量恶意的请求,也会对系统造成影响。而限流就是保护措施之一,那我们能不能学习一下像微服务网关一样限流,巧了,nginx就有这么强大!
nginx提供两种限流的方式:
一 是控制速率
二 是控制并发连接数
控制速率
控制速率的方式之一就是采用漏桶算法。漏桶(LeakyBucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率.示意图如下:
漏桶算法实现nginx的配置:
修改/usr/local/openresty/nginx/conf/nginx.conf:
#user nobody;
user root root;
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
# 设置限流配置
limit_req_zone $binary_remote_addr zone=myRateLimit:10m rate=2r/s;
server {
listen 8080;
server_name localhost;
charset utf-8;
location / {
limit_req zone=myRateLimit;
root html;
index index.html index.htm;
}
}
server {
listen 80;
server_name localhost;
charset utf-8;
# 添加广告
location /ad_update {
content_by_lua_file /root/lua/ad_update.lua; # 加载root/lua/ad_update.lua文件
}
# 读取广告
location /ad_read {
content_by_lua_file /root/lua/ad_read.lua;
}
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
#包含redis初始化模块
lua_shared_dict dis_cache 5m; #共享内存开启
}
解释:
binary_remote_addr是一种key,表示基于remote_addr(客户端IP)来做限流,binary_的目的是压缩内存占用量。zone:定义共享内存区来存储访问信息,
myRateLimit:10m表示一个大小为10M,名字为myRateLimit的内存区域。1M能存储16000IP地址的访问信息,10M可以存储16WIP地址访问信息。
rate用于设置最大访问速率,rate=10r/s表示每秒最多处理10个请求。
Nginx实际上以毫秒为粒度来跟踪请求信息,因此10r/s实际上是限制:每100毫秒处理一个请求。这意味着,自上一个请求处理完后,若后续100毫秒内又有请求到达,将拒绝处理该请求.我们这里设置成2方便测试。
处理突发流量
上面例子限制2r/s,如果有时正常流量突然增大,超出的请求将被拒绝,无法处理突发流量,可以结合burst参数使用来解决该问题。
server{
location/{
limit_req zone=myRateLimit burst=5;
root html;
index index.htmlindex.htm;
}
}
burst译为突发、爆发,表示在超过设定的处理速率后能额外处理的请求数,当rate=2r/s时,将1s拆成2份,即每500ms可处理1个请求。
此处,burst=5,若同时有6个请求到达,Nginx会处理第一个请求,剩余5个请求将放入队列,然后每隔500ms从队列中获取一个请求进行处理。若请求数大于6,将拒绝处理多余的请求,直接返回503.
不过,单独使用burst参数并不实用。假设burst=50,rate为10r/s,排队中的50个请求虽然每100ms会处理一个,但第50个请求却需要等待50*100ms即5s,这么长的处理时间自然难以接受。
因此,burst往往结合nodelay一起使用。
server{
location/{
limit_req zone=myRateLimit burst=5 nodelay;
root html;
index index.htmlindex.htm;
}
}
如上表示:处理突发5个请求的时候,没有延迟,等到完成之后,按照正常的速率处理。如上两种配置结合就达到了速率稳定,但突然流量也能正常处理的效果。