【并发优化】一、OpenResty 结合 Lua、Redis 实现请求高并发策略

目录

一、整体架构描述

1.1 方案v1.0

1.2 方案v1.0优化

1.3 方案对比

二、方案2.0部署

2.1 OpenResty 安装

2.2 Lua脚本编写

2.3 配置 OpenResty

三、测试


一、整体架构描述

1.1 方案v1.0

在优化旧的设计框架前,先看一下常见的查询请求处理

  

步骤如下:

  • ① 请求到 Nginx 或 restApi 服务后, 向 Redis 请求数据
  • ②、③ Redis 查看缓存中是否有数据,若有数据则直接进入流程⑥,没有则进入流程④
  • ④ 、⑤ 查询数据库数据,并更新数据到 Redis ,以免下次还需要访问数据库
  • ⑥ 返回数据给用户

 

1.2 方案v1.0优化

在方案v1.0,存在了大量简单的请求,如在IM软件中查询用户/群组信息、在商城项目中查询商品信息等,这些简单的查询却频繁的查询无疑会对服务造成一定压力。那么是否有办法进行优化呢。

对方案v1.0进行优化后,方案v2.0结合了 OpenResty、Lua、Redis 实现了二级缓存。利用 Nginx 高并发的特性,使得上述这些请求无需再通过 restAPI 服务,而是通过Lua脚本直接查询和操作 redis 和 mysql,降低服务压力。

  

步骤如下:

  • ①、② 请求到 Nginx 后,Nginx 先查询 Nginx-Cache,若缓存中没有数据,则调用对应的 Lua 脚本
  • ③、④ Lua 脚本查询 Redis,若Redis 缓存中有数据则直接返回并更新Nginx缓存, 没有数据则调用 Lua脚本,查询数据库
  • ⑤ 查询数据库数据并更新Redis缓存

注意:

方案v2.0 采用的是逐级缓存的方式:

  • 第一次访问:nginx 和 redis 中均没有缓存,数据从数据库中查出,并存入 redis 缓存
  • 第二次访问:nginx 中没有缓存,数据从 redis 中查出,并存入 nginx 缓存
  • 第三次访问:数据从 nginx 中查出

这样做有以下原因:

  • nginx 缓存过期时间小于 redis 缓存过期时间,有利于降低 redis 雪崩的危险
  • 若同时对 nginx 和 redis 设置缓存,则数据访问到的都是 nginx。当数据库变化时,无法在不访问数据库的情况下实现快速响应。解决方案为使用 Canal 实现数据库和 redis 的数据同步,将 nginx 的有效时间设置得更短,做到防止大量并发请求到数据库的同时,又能拿到最新数据

 

1.3 方案对比

根据不同的项目需求和实际方案,选择使用哪一种方案。

 方案v1.0方案v2.0
灵活性访问 redis 和数据库由 restApi 控制,可处理复杂请求。但如果有修改必须重启服务

利用 Nginx 高并发特性处理大量简单请求,且修改脚本无需重启后端服务,只要重新加载 Nginx 即可。

但只能处理简单的请求,且需要有特殊的识别方式,如加特定的 API

并发性高,相对来说肯定比方案v1.0高,但要根据实际情况使用,而不是为了炫技而部署
部署难度简单中等。需要学习 OpenResty框架和Lua语言
适用场景通用场景

更适用于存在大量简单的查询请求的项目,如仓库管理系统、商城系统等。

同时这个方案还可以处理权限控制,如直接在 Nginx 拒绝不携带 token 的请求,或进行 token 验证等。

 

二、方案2.0部署

2.1 OpenResty 安装

安装流程:https://blog.csdn.net/qq_34416331/article/details/106421783

 

2.2 Lua脚本编写

Lua 的基本用法:https://blog.csdn.net/qq_34416331/article/details/106419100

Lua 脚本编写:

# 创建存放 lua 脚本的文件夹,名字自定义
mkdir /usr/local/lua_conf

# 进入文件夹
cd /usr/local/lua_conf

# 创建 lua 脚本
vim read_conf.lua
ngx.header.content_type="application/json;charset=utf8"
local uri_args = ngx.req.get_uri_args();
local id = uri_args["id"];
-- 加载nginx缓存模块
local cache_ngx = ngx.shared.dis_cache;
-- 根据ID获取本地缓存数据
local contentCache = cache_ngx:get('content_cache_'..id);

-- 获取IP信息,可删除
-- [[
local headers=ngx.header;
local ip=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr or "0.0.0.0"
ngx.say(ip)
ngx.say(contentCache)
]]--

-- 若nginx中没有相应的缓存信息
if contentCache == "" or contentCache == nil then

    -- 获取redis模块
    local redis = require("resty.redis");
    local red = redis:new()
    red:set_timeout(2000)
    red:connect("192.168.47.142", 6379)
    local rescontent=red:get("content_"..id);

    -- 若redis模块也没有这个信息
    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 = "192.168.47.142",
            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);

        -- 存储到redis中
        red:set("content_"..id,responsejson);

        -- 返回数据
        ngx.say(responsejson);
        -- 关闭数据库连接
        db:close()
    else
        -- 若redis中有缓存,则设置的到nginx缓存中,并返回
        -- 2*60 表示设置 nginx 缓存时间为2分钟,应根据实际情况修改
        cache_ngx:set('content_cache_'..id, rescontent, 2*60);
        ngx.say(rescontent)
    end
    -- 关闭redis连接
    red:close()
else
-- 若nginx中有对应的信息,则返回
    ngx.say(contentCache)
end

 

2.3 配置 OpenResty

要让 OpenResty 来使用 Lua 脚本,只需要配置 Nginx 的配置文件即可。

### 修改 Nginx 配置文件 ###
# 进入 OpenResty 自带的 Nginx 目录
cd /usr/local/openresty/nginx/conf

# 编辑配置文件
vim nginx.conf

  

 lua_shared_dict 的作用是声明一个共享内存区域 name,以充当基于 Lua 字典的共享存储。简单来说就是当nginx运行时的 lua 脚本缓存空间大小

  

在http中配置要访问的接口:

  

重启 nginx

# 重新加载 nginx
/usr/local/openresty/nginx/sbin/nginx -s reload

 

三、测试

修改 read_content.lua

ngx.header.content_type="application/json;charset=utf8"
local uri_args = ngx.req.get_uri_args();
local id = uri_args["id"];
-- 加载nginx缓存模块
local cache_ngx = ngx.shared.dis_cache;
-- 根据ID获取本地缓存数据
local contentCache = cache_ngx:get('content_cache_'..id);

--[[
local headers=ngx.header;
local ip=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr or "0.0.0.0"
ngx.say(ip)
ngx.say(contentCache)
]]--

if contentCache==nil then
  ngx.say("nginx缓存为空")
else 
  ngx.say("从nginx缓存中查到了数据")
end

-- 若nginx中没有相应的缓存信息
if contentCache == "" or contentCache == nil then

    -- 获取redis模块
    local redis = require("resty.redis");
    local red = redis:new()
    red:set_timeout(2000)
    red:connect("192.168.47.142", 6379)
    local rescontent=red:get("content_"..id);

    if ngx.null == rescontent then
       ngx.say("redis为空")
    else
       ngx.say("nginx为空,从redis中查到了数据")
    end
    -- 若redis模块也没有这个信息
    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 = "192.168.47.142",
            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);

        -- 存储到redis中
        red:set("content_"..id,responsejson);

        -- 返回数据
        ngx.say(responsejson);
        -- 关闭数据库连接
        db:close()
    else
        -- 若redis中有缓存,则设置的到nginx缓存中,并返回
        cache_ngx:set('content_cache_'..id, rescontent, 30);
    end
    -- 关闭redis连接
    red:close()
else
-- 若nginx中有对应的信息,则返回
    ngx.say(contentCache)
end

清理redis

访问:

第一次访问

第二次访问:

第三次访问:

 

 

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
OpenResty中使用Redis的过程是通过Lua脚本来实现的。首先,需要进行准备工作,确保OpenRestyRedis环境的配置正确。OpenResty主要用于解决高并发问题,而为了避免数据库成为高并发的瓶颈,操作Redis变得不可避免。 如果对OpenResty不太了解,可以参考相关文章进行学习。在Windows系统下,可以使用ZeroBrane Studio进行开发和调试OpenResty代码。 在使用OpenResty操作Redis之前,需要将相关的代码添加到配置文件中。具体的配置数据可以根据自己的Redis数据库情况进行修改。配置文件中包含了连接信息、超时时间以及Redis的库等信息。 在使用OpenResty时,可以根据具体的需求和场景,编写Lua脚本来操作Redis实现数据的读取、写入和删除等操作。通过调用相关的Redis命令,可以实现Redis的交互。 总结来说,OpenResty中使用Redis的过程是通过Lua脚本与Redis进行交互,通过配置文件设置Redis的连接信息和相关参数,然后根据需求编写Lua脚本来操作Redis中的数据。这样可以有效地解决高并发问题并提升系统性能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [OpenResty并发最佳实践--Redis操作](https://blog.csdn.net/lupengfei1009/article/details/86160652)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值