OpenResty简介与缓存测试

OpenResty是一个基于Nginx的可伸缩的web应用服务器,由国人章亦春发起,提供了很多高质量的第三方模块。web开发人员可以使用Lua脚本调用Nginx支持的各种C和Lua模块。其最突出的特点是能够快速构造出承受10K以上的并发连接响应的超高性能web应用系统,目前,360、阿里云、新浪、腾讯都是OpenResty的深度用户。

OpenResty应用场景

  • 高访问下的应用及官网的主页
    如商城,咨询类的应用首页,会存在大量的请求,由于涉及到的内容比较多;所以可是私用预载入的形式,将主页的数据放置在redis中;使用OpenResty+redis实现首页,官网主页的高并发加载
  • 商城类的秒杀功能
    秒杀功能会存在短时间的请求洪峰,如果处理不当可能会造成down机的风险,可以结合OpenResty+redis实现秒杀的功能
  • ip限流
    互联网系统可能存在非法用户恶意暴力请求,导致正常的用户无法使用,可以通过OpenResty+redis实现ip的白名单机制,去拦截非法的用户ip
  • APP灰度升级发布
    可以根据系统的数据及条件实现APP的灰度升级测试

OpenResty可以简单地理解成封装了Nginx,并且集成Lua脚本,开发人员只需要简单地使用已经提供好的Lua脚本,而不需要自己编写复杂的Lua脚本调用Nginx。本文所使用的Linux环境为Centos7。

1. 安装OpenResty

# 添加仓库地址
yum install yum-utils
yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo
# 执行安装
yum install openresty
# 默认已经安装好了Nginx,进入默认目录如下,可以看到Nginx的文件夹
cd /usr/local/openresty

在这里插入图片描述

2. 安装Lua

进入Lua官网地址可以看到具体的安装指南。
在这里插入图片描述

curl -R -O http://www.lua.org/ftp/lua-5.4.0.tar.gz
tar zxf lua-5.4.0.tar.gz
cd lua-5.4.0
make all test

# 出错时运行下面的指令安装Lua需要的依赖
yum install libtermcap-devel ncurses-devel libevent-devel readline-devel

3. 配置Nginx

# 默认已经安装好了Nginx,进入默认目录如下,可以看到Nginx的文件夹
cd /usr/local/openresty/nginx/conf
vi nginx.conf

可以看到Nginx虚拟机的端口为80,我们可以更改配置文件,修改Nginx虚拟机的访问端口。接着,使用/bin/systemctl start openresty.service命令启动OpenResty服务。
在这里插入图片描述

4. 测试访问

在浏览器中访问:http://192.168.137.118:80看到下图内容,说明配置成功。注意,这里是Linux虚拟机的IP地址和Nginx服务器的端口号。

在这里插入图片描述

5. 访问Redis

lua-resty-redis 模块:文档

首先,打开OpenResty下的Nginx配置文件:

# 默认已经安装好了Nginx,进入默认目录如下,可以看到Nginx的文件夹
cd /usr/local/openresty/nginx/conf
vi nginx.conf

在nginx.conf中加入:

        location /redis_test{
            default_type text/html;
            content_by_lua_file /usr/local/openresty/lualib/redis_test.lua;
        }

在这里插入图片描述
这里,需要注意的是,笔者试过新建/root/lua目录,并将脚本放在该目录下,发现访问之后是404,所以这里建议放在/usr/local/openresty/lualib/这个目录下。在该目录下创建redis_test.lua脚本的内容如下:

local redis = require "resty.redis"
local red = redis:new()

red:set_timeout(1000)

local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
        ngx.say("failed to connect: ", err)
        return
end

ngx.say("set result: ", ok)

local res, err = red:get("dog")
if not res then
        ngx.say("failed to get doy: ", err)
        return
end

if res == ngx.null then
        ngx.say("dog not found.")
        return
end

ngx.say("dog: ", res)

使用/bin/systemctl restart openresty.service指令重启OpenResty服务,并访问http://192.168.137.118/redis_test:

在这里插入图片描述
接着,我们进入redis容器中,并设置dog的值。

docker exec -it redis-test /bin/sh
redis-cli -p 6379
set dog my_friend

在这里插入图片描述
执行成功后,再次访问,可以看到已经可以成功访问到redis数据库中的数据。
在这里插入图片描述

6. 访问mysql

openresty的mysql模块:文档

在nginx.conf加入如下配置:

        location /mysql_test {
            default_type text/html;
            content_by_lua_file /usr/local/openresty/lualib/mysql_test.lua;
        }

在这里插入图片描述

/usr/local/openresty/lualib/mysql_test.lua脚本内容如下,注意修改您mysql数据库宿主机的IP地址、数据库名、用户名和密码等信息:

local mysql = require "resty.mysql"
local db, err = mysql:new()

if not db then
        ngx.say("failed to instantiate mysql: ", err)
        return
end

db:set_timeout(1000)

local ok, err, errno, sqlstate = db:connect{
        host = "127.0.0.1",
        port = 3306,
        database = "changgou_poster",
        user = "root",
        password="Jack@13163",
        max_packet_size = 1024 * 1024
}

if not ok then
        ngx.say("failed to connect: ", err, ": ", errno, " ", sqlstate)
        return
end

ngx.say("connected to mysql.")

local res, err, errno, sqlstate = db:query("drop table if exists cats")
if not res then
        ngx.say("bad result: ", err, ": ", errno, ": ", sqlstate, ".")
        return
end

res, err, errno, sqlstate = db:query("create table cats " .. "(id int not null primary key auto_increment, "
                                        .. "name varchar(30))")
if not res then
        ngx.say("bad result: ", err, ": ", errno, ": ", sqlstate, ".")
        return
end

ngx.say("table cats created.")

res, err, errno, sqlstate = db:query("insert into cats(name) " .. "values (\'Bob\'),(\'\'),(null)")
if not res then
        ngx.say("bad request: ", err, ": ", errno, ": ", sqlstate, ".")
        return
end

ngx.say(res.affected_rows, " rows inserted into table cats ", "(last insert id: ", res.insert_id, ")")

res, err, errno, sqlstate = db:query("select * from cats order by id asc", 10)
if not res then
        ngx.say("bad result ", err, ": ", errno, ": ", sqlstate, ".")
        return
end

local cjson = require "cjson"
ngx.say("result: ", cjson.encode(res))

local ok, err = db:set_keepalive(1000, 100)
if not ok then
        ngx.say("failed to set keepalive: ", err)
        return
end

本文中mysql数据库是直接安装在虚拟机里面的,读者也可以在虚拟机的docker容器中安装mysql容器。需要注意的是,由于mysql的密码安全校验机制的存在,MySQL的用户名和密码必须是即有大小写字母,又有数字和符号,因此,笔者为了不那么麻烦,就直接遵循它的规则,设置密码为Jack@13163,大家可以自己定义一个好记的密码。关于mysql在centos7下的安装,请移步至CentOS7安装mysql8.0.12

# 拉取mysql最新镜像
docker pull mysql
# 后台运行容器,并设置root账户密码为123456
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql
# 设置mysql容器随虚拟机自动启动
docker update --restart=always mysql
# 进入容器,并创建changgou_poster数据库
docker exec -it mysql /bin/bash
# 登录mysql数据库
mysql -u root -p
# 创建数据库
create database changgou_poster;

在这里插入图片描述

使用/bin/systemctl restart openresty.service指令重启OpenResty服务,并访问:http://192.168.137.118/mysql_test
在这里插入图片描述

lua 连接MySQL mysql.cant_connect: ***consider upgrading MySQL client, errno:1251, sql_state:08004,解决方案:地址

在这里插入图片描述
接着,使用systemctl restart mysqld命令重启mysql服务,可以看到此时已经可以正常创建数据表,并向其中插入了三条记录。

在这里插入图片描述

7. 广告信息缓存测试

假设现在有一个场景:

电商项目中的网站首页需要展示大量的广告,这些广告通常是变化频率较低的,我们可以通过OpenResty服务器的两级缓存来提高系统的并发能力,同时真的做到像外挂一样,不影响系统的原有的架构。

具体的流程如下:

在这里插入图片描述
这里我们首先在数据库中创建两个表,建表语句如下:

drop database if exists changgou_poster;
create database changgou_poster;
use changgou_poster;

-- 广告分类表
drop table if exists tb_content_category;
create table tb_content_category
(
    id bigint(20) auto_increment primary key comment '类目ID',
    name varchar(50) not null comment '分类名称'
);

insert into tb_content_category(name) values('轮播广告');
insert into tb_content_category(name) values('活动广告');

-- 广告表
drop table if exists tb_content;
create table tb_content
(
    id int auto_increment primary key comment '广告编号',
    category_id varchar(50) not null comment '内容类别编号',
    title varchar(500) comment '内容标题',
    url varchar(1000) comment '链接',
    pic varchar(500) comment '图片路径',
    status varchar(1) comment '状态:0无效,1有效',
    sort_order int(11) comment '排序'
);

insert into tb_content(category_id, title, url, pic, status, sort_order) values(1, '肉松饼', 'http://www.baidu.com', 'http://192.168.137.118:8080/group1/M00/00/00/wKiJdl8rYp-Ab9ydAAA-Nvu01EY467.png', '1', 1);
insert into tb_content(category_id, title, url, pic, status, sort_order) values(1, '华为P30', 'http://www.baidu.com', 'http://192.168.137.118:8080/group1/M00/00/00/wKiJdl8rYp-Ab9ydAAA-Nvu01EY467.png', '1', 1);
-- 查询某一分类的广告
select url,pic from tb_content where status = '1' and category_id = 1 order by sort_order;

具体的poster_test.lua脚本如下:

ngx.header.content_type="application/json;charset=utf-8"
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 = "127.0.0.1",
  port = 3306,
  database = "changgou_poster",
  user = "root",
  password = "Jack@13163"
}

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 = "127.0.0.1"
local port = 6379
red:connect(ip,port)
red:set("content_"..id,cjson.encode(res))
red:close()

ngx.say("{flag:true}")

配置Nginx拦截,即在/usr/local/openresty/nginx/conf/nginx.conf加入如下配置:

        location /update_content {
            default_type text/html;
            content_by_lua_file /usr/local/openresty/lualib/poster_test.lua;
        }

使用/bin/systemctl restart openresty.service重新启动OpenResty服务器,或者进入到/usr/local/openresty/nginx/sbin下,执行./nginx -s reload。最后,访问http://192.168.137.118/update_content?id=1

在这里插入图片描述
同时,可以看到Mysql数据库中的数据已经保存到redis中。
在这里插入图片描述
上述过程实现了将数据库中的数据读取到redis中,下面我们进一步完善poster_test.lua脚本,完成两级缓存。

ngx.header.content_type="application/json;charset=utf-8"
local uri_args = ngx.req.get_uri_args();
local id = uri_args["id"];
--先查询Nginx本地缓存模块:【dis_cache,需要添加配置信息:lua_shared_dict dis_cache 128m;】
local cache_ngx = ngx.shared.dis_cache;
--根据Id获取缓存数据
local contentCache = cache_ngx:get('content_cache_'..id);

--在Nginx本地缓存查询不到
if contentCache == "" or contentCache == nil then
	local redis = require("resty.redis")
	local red = redis:new()
	red:set_timeout(2000)
  	red:connect("127.0.0.1",6379)
  	local redisContent = red:get("content_"..id)
 	
 	--从Redis中查询是否存在
 	if ngx.null == redisContent then
    	local cjson = require("cjson")
    	local mysql = require("resty.mysql")
    	local db = mysql:new()
    	db:set_timeout(2000)
    	local props = {
      		host = "127.0.0.1",
      		port = 3306,
      		database = "changgou_poster",
      		user = "root",
      		password = "Jack@13163"
    	}
    	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)
    	--数据序列化为json字符串
    	local jsonResult = cjson.encode(res)
    	--插入数据到Redis缓存
    	red:set("content_"..id,jsonResult)
		ngx.say("MySQL: "..jsonResult)
    	db:close()
    else
    	--插入数据到Nginx本地缓存,缓存过期时间为2分钟,可以根据实际情况调整
    	cache_ngx:set('content_cache_'..id,redisContent, 2*60)
    	ngx.say("Redis: "..redisContent)
    end
    red:close()
else
	--从Nginx本地缓存找到数据后直接输出
	ngx.say("Nginx: "..contentCache)
end

添加Nginx缓存模块
在这里插入图片描述
清空Redis中之前的缓存,三次访问http://192.168.137.118/update_content?id=1依次输入如下结果:

  • MySQL: [{“url”:“http://www.baidu.com”,“pic”:“http://192.168.137.118:8080/group1/M00/00/00/wKiJdl8rYp-Ab9ydAAA-Nvu01EY467.png”},{“url”:“http://www.baidu.com”,“pic”:“http://192.168.137.118:8080/group1/M00/00/00/wKiJdl8rYp-Ab9ydAAA-Nvu01EY467.png”}]
  • Redis: [{“url”:“http://www.baidu.com”,“pic”:“http://192.168.137.118:8080/group1/M00/00/00/wKiJdl8rYp-Ab9ydAAA-Nvu01EY467.png”},{“url”:“http://www.baidu.com”,“pic”:“http://192.168.137.118:8080/group1/M00/00/00/wKiJdl8rYp-Ab9ydAAA-Nvu01EY467.png”}]
  • Nginx: [{“url”:“http://www.baidu.com”,“pic”:“http://192.168.137.118:8080/group1/M00/00/00/wKiJdl8rYp-Ab9ydAAA-Nvu01EY467.png”},{“url”:“http://www.baidu.com”,“pic”:“http://192.168.137.118:8080/group1/M00/00/00/wKiJdl8rYp-Ab9ydAAA-Nvu01EY467.png”}]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值