Redis + OpenResty 多级缓存

news2024/11/19 6:32:20

多级缓存

初识 OpenResty

OpenResty® - 开源官方站

基于 Nginx的高性能 Web 平台,用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

  • 具备Nginx的完整功能
  • 基于Lua语言进行扩展,集成了大量精良的 Lua 库、第三方模块
  • 允许使用Lua自定义业务逻辑、自定义库

OpenResty 的安装

安装

首先你的Linux虚拟机必须联网

1)安装开发库

首先要安装OpenResty的依赖开发库,执行命令:

yum install -y pcre-devel openssl-devel gcc --skip-broken

2)安装OpenResty仓库

你可以在你的 CentOS 系统中添加 openresty 仓库,这样就可以便于未来安装或更新我们的软件包(通过 yum check-update 命令)。运行下面的命令就可以添加我们的仓库:

yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo

如果提示说命令不存在,则运行:

yum install -y yum-utils 

然后再重复上面的命令

3)安装OpenResty

然后就可以像下面这样安装软件包,比如 openresty

yum install -y openresty

4)安装opm工具

opm是OpenResty的一个管理工具,可以帮助我们安装一个第三方的Lua模块。

如果你想安装命令行工具 opm,那么可以像下面这样安装 openresty-opm 包:

yum install -y openresty-opm

5)目录结构

默认情况下,OpenResty安装的目录是:/usr/local/openresty

image-20200310225539214

看到里面的nginx目录了吗,OpenResty就是在Nginx基础上集成了一些Lua模块。

6)配置nginx的环境变量

打开配置文件:

vi /etc/profile

在最下面加入两行:

export NGINX_HOME=/usr/local/openresty/nginx
export PATH=${NGINX_HOME}/sbin:$PATH

NGINX_HOME:后面是OpenResty安装目录下的nginx的目录

然后让配置生效:

source /etc/profile
启动和运行

OpenResty底层是基于Nginx的,查看OpenResty目录的nginx目录,结构与windows中安装的nginx基本一致:

image-20210811100653291

所以运行方式与nginx基本一致:

# 启动nginx
nginx
# 重新加载配置
nginx -s reload
# 停止
nginx -s stop

nginx的默认配置文件注释太多,影响后续我们的编辑,这里将nginx.conf中的注释部分删除,保留有效部分。

修改/usr/local/openresty/nginx/conf/nginx.conf文件,内容如下:

#user  nobody;
worker_processes  1;
error_log  logs/error.log;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    server {
        listen       8081;
        server_name  localhost;
        location / {
            root   html;
            index  index.html index.htm;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

在Linux的控制台输入命令以启动nginx:

nginx

然后访问页面:http://192.168.150.101:8081,注意ip地址替换为你自己的虚拟机IP:

OpenResty快速入门

1、在nginx.conf的http下面,添加对OpenResty的Lua模块的加载

#lua 模块
lua_package_path "/usr/local/openresty/lualib/?.lua;;";
#c模块     
lua_package_cpath "/usr/local/openresty/lualib/?.so;;";  

2、在nginx.conf的server下面,添加对/api/item这个路径的监听

location /api/item {
    # 响应类型,这里返回json
    default_type application/json;
    # 响应数据由 lua/item.lua这个文件来决定
    content_by_lua_file lua/item.lua;
}

3、在nginx目录创建文件夹:lua,在lua文件夹下,新建文件:item.lua

-- 返回假数据,这里的ngx.say()函数,就是写数据到Response中
ngx.say('{"id":10001,"data":"hello"}')

4、重新加载配置

nginx -s reload

请求参数处理

路径占位符 /item/1001
# 1.正则表达式匹配: 
location ~ /item/(\d+) {
    content_by_lua_file lua/item.lua;
}
-- 2. 匹配到的参数会存入ngx.var数组中,
-- 可以用角标获取
local id = ngx.var[1]
请求头 id: 1001
-- 获取请求头,返回值是table类型
local headers = ngx.req.get_headers()
Get请求参数 ?id=1001
-- 获取GET请求参数,返回值是table类型
local getParams = ngx.req.get_uri_args()
Post表单参数 id=1001
-- 读取请求体
ngx.req.read_body()
-- 获取POST表单参数,返回值是table类型
local postParams = ngx.req.get_post_args()
JSON参数 {“id”: 1001}
-- 读取请求体
ngx.req.read_body()
-- 获取body中的json参数,返回值是string类型
local jsonBody = ngx.req.get_body_data()

查询Tomcat

nginx内部发送Http请求

nginx提供了内部API用以发送http请求

local resp = ngx.location.capture("/path",{
        method = ngx.HTTP_GET,   -- 请求方式
        args = {a=1,b=2},  -- get方式传参数
        body = "c=3&d=4" -- post方式传参数
    })

注意:这里的path是路径,并不包含IP和端口。这个请求会被nginx内部的server监听并处理。

但是我们希望这个请求发送到Tomcat服务器,所以还需要编写一个server来对这个路径做反向代理:

封装http查询的函数

把http查询的请求封装为一个函数,放到OpenResty函数库中

在/usr/local/openresty/lualib目录下创建common.lua文件

-- 封装函数,发送http请求,并解析响应
local function read_http(path, params)
    local resp = ngx.location.capture(path,{
            method = ngx.HTTP_GET,
            args = params,
        })
    if not resp then
        -- 记录错误信息,返回404
        ngx.log(ngx.ERR, "http not found, path: ", path , ", args: ", args)
        ngx.exit(404)
    end
    return resp.body
end

-- 将方法导出
local _M = {  
    read_http = read_http
}  
return _M

使用Http函数查询数据

修改item.lua文件

-- 引入自定义工具模块
local common = require("common")
local read_http = common.read_http

-- 获取路径参数
local id = ngx.var[1]

-- 根据id查询商品
local itemJSON = read_http("/item/".. id, nil)
-- 根据id查询商品库存
local itemStockJSON = read_http("/item/stock/".. id, nil)
JSON结果处理

OpenResty提供了一个cjson的模块用来处理JSON的序列化和反序列化。

openresty/lua-cjson: Lua CJSON is a fast JSON encoding/parsing module for Lua (github.com)

-- 引入cjson模块
local cjson = require "cjson"

-- 序列化
local obj = {
    name = 'jack',
    age = 21
}
local json = cjson.encode(obj)

-- 反序列化
local json = '{"name": "jack", "age": 21}'
local obj = cjson.decode(json);
print(obj.name)
Tomcat集群的负载均衡
# 反向代理配置,将/item路径的请求代理到tomcat集群        
location /item {
    proxy_pass 	http://tomcat-cluster;
}

# tomcat集群配置
upstream tomcat-cluster{
    # 对请求的 URI 进行哈希处理
    hash $request_uri;
    server 192.168.150.1:8081;
    server 192.168.150.1:8082;
}

Redis缓存预热

冷启动:服务刚刚启动时,Redis中并没有缓存,如果所有商品数据都在第一次查询时添加缓存,可能会给数据库带来较大压力。

缓存预热:在实际开发中,我们可以利用大数据统计用户访问的热点数据,在项目启动时将这些热点数据提前查询并保存到Redis中。

@Component
public class RedisHandler implements InitializingBean {
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Override
    public void afterPropertiesSet() throws Exception { 
        // 初始化缓存 ... 
    }
}

查询Redis缓存

OpenResty的Redis模块

OpenResty提供了操作Redis的模块

-- 引入redis模块
local redis = require("resty.redis")
-- 初始化Redis对象
local red = redis:new()
-- 设置Redis超时时间
red:set_timeouts(1000, 1000, 1000)

封装函数,用来释放Redis连接,其实是放入连接池

-- 关闭redis连接的工具方法,其实是放入连接池
local function close_redis(red)  
    local pool_max_idle_time = 10000 -- 连接的空闲时间,单位是毫秒  
    local pool_size = 100 --连接池大小  
    local ok, err = red:set_keepalive(pool_max_idle_time, pool_size)  
    if not ok then  
        ngx.log(ngx.ERR, "放入Redis连接池失败: ", err)  
    end  
end  

封装函数,从Redis读数据并返回

-- 查询redis的方法 ip和port是redis地址,key是查询的key
local function read_redis(ip, port, key)
    -- 获取一个连接
    local ok, err = red:connect(ip, port)
    if not ok then
        ngx.log(ngx.ERR, "连接redis失败 : ", err)
        return nil
    end
    -- 查询redis
    local resp, err = red:get(key)
    -- 查询失败处理
    if not resp then
        ngx.log(ngx.ERR, "查询Redis失败: ", err, ", key = " , key)
    end
    --得到的数据为空处理
    if resp == ngx.null then
        resp = nil
        ngx.log(ngx.ERR, "查询Redis数据为空, key = ", key)
    end
    close_redis(red)
    return resp
end

业务实现

-- 封装函数,先查询redis,再查询http
local function read_data(key, path, params)
    -- 查询redis
    local resp = read_redis("127.0.0.1", 6379, key)
    -- 判断redis是否命中
    if not resp then
        -- Redis查询失败,查询http
        resp = read_http(path, params)
    end
    return resp
end

Nginx本地缓存

开启共享词典,在nginx.conf的http下添加配置:

# 共享字典,也就是本地缓存,名称叫做:item_cache,大小150m
lua_shared_dict item_cache 150m; 

操作共享字典

-- 获取本地缓存对象
local item_cache = ngx.shared.item_cache
-- 存储, 指定key、value、过期时间,单位s,默认为0代表永不过期
item_cache:set('key', 'value', 1000)
-- 读取
local val = item_cache:get('key')

修改后的查询逻辑

-- 封装函数,先查询本地缓存,再查询redis,再查询http
local function read_data(key, expire, path, params)
    -- 读取本地缓存
    local val = item_cache:get(key)
    if not val then
        -- 缓存未命中,记录日志
        ngx.log(ngx.ERR, "本地缓存查询失败, key: ", key , ", 尝试redis查询")
        -- 查询redis
        val = read_redis("127.0.0.1", 6379, key)
        -- 判断redis是否命中
        if not val then
            ngx.log(ngx.ERR, "Redis缓存查询失败, key: ", key , ", 尝试http查询")
            -- Redis查询失败,查询http
            val = read_http(path, params)
        end
    end
    -- 写入本地缓存
    item_cache:set(key, val, expire)
    return val
end
-- 根据id查询商品
local itemJSON = read_data('item:id:' .. id, 1800, "/item/".. id, nil)
-- 根据id查询商品库存
local itemStockJSON = read_data('item:stock:id:' .. id, 60, "/item/stock/".. id, nil)
开始
本地缓存命中?
返回缓存数据
记录日志: 本地缓存查询失败
尝试redis查询
Redis命中?
返回Redis数据
记录日志: Redis缓存查询失败
尝试http查询
返回http数据
写入本地缓存
返回数据

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1657530.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AlibabaCloud微服务下的链路追踪系统实战详解

🚀 作者 :“二当家-小D” 🚀 博主简介:⭐前荔枝FM架构师、阿里资深工程师||曾任职于阿里巴巴担任多个项目负责人,8年开发架构经验,精通java,擅长分布式高并发架构,自动化压力测试,微服务容器化k…

【深耕 Python】Quantum Computing 量子计算机(3)重要数学公式一览

写在前面 往期量子计算机博客: 【深耕 Python】Quantum Computing 量子计算机(1)图像绘制基础 【深耕 Python】Quantum Computing 量子计算机(2)绘制电子运动平面波 正文 偏微分: 交换关系&#xff…

MATLAB 三维空间中在两点之间等间隔插入多个点 (67)

MATLAB 三维空间中在两点之间等间隔插入多个点 (67) 一、算法介绍二、算法实现1.代码2.结果一、算法介绍 用于加密直线点云,具体为根据给定的直线端点,沿着该直线方向,插入多个点,从而加密。具体方法和效果如下所示: 二、算法实现 1.代码 代码如下(示例): % 定…

数据的输入和输出

早期的总线系统 为了解决通信的问题、主板上铺设了一条公共线路、各个设备都连接到这条线路上、不管谁要和谁通信、都能使用它来传输、这条线路就是总线。 总线上有CPU、内存、鼠标、键盘、硬盘、网卡、声卡、显卡等… 说是一条总线、实际上是包含了传输数据的数据总线、传输…

【C++】继承 — 继承的引入、赋值切片详细讲解

前言 我们知道C语言是一门面向对象编程的语言,而面向对象编程有三大特性,它们分别是: 封装继承多态 目录 1. 继承的概念及定义1.1继承的概念1.2继承的定义格式1.3 继承的使用 2 基类和派生类对象赋值转换3 继承中的作用域3.1 派生类对象的存…

YOLOv9中模块总结补充|RepNCSPELAN4详图

专栏地址:目前售价售价69.9,改进点70 专栏介绍:YOLOv9改进系列 | 包含深度学习最新创新,助力高效涨点!!! 1. RepNCSPELAN4详图 RepNCSPELAN4是YOLOv9中的特征提取-融合模块,类似前几…

【Java基础】设计模式——单例设计模式

单例设计模式(Singleton Design Pattern)是一种创建型设计模式,它确保⼀个类有且只有⼀个实例,并提供一个全局访问点来访问这个唯一实例。 单例模式主要解决的是,⼀个全局使⽤的类频繁的创建和消费,从⽽提…

信通院智能体标准发布,实在智能牵头编写

4月28日,由人工智能关键技术和应用评测工业和信息化部重点实验室、中国信息通信研究院(以下简称:中国信通院)人工智能研究所共同主办的“人工智能”高质量发展研讨会顺利召开,会上中国信通院正式发布全国首个Agent&…

Java12基础(Package包 作用域 String字符串)

目录 一. Package包 import关键字 命名规范 二. 作用域 三. String字符串(进阶) 创建方式: 内存情况: 1. 字符串的搜索 2. trim()方法 3. 替换字符串 4. 分割字符串 5. 拼接字符串 6. 格式化字符串 7. 类型转换 8. 转换为char[ ]字符数组 9. 字符编码 10. Str…

抖店内卷严重,平台规则繁琐,商家如何应对这种现状?

我是王路飞。 随着抖音的快速发展和扩张,抖音电商版块也在不断演进玩法和提高门槛。 然而也正是走的太快了,导致很多商家已经有些跟不上平台的繁琐规则。 压力更大的是,抖音电商的内卷逐渐严重,很多人被迫成为了“六边形战士”…

Vulstack红队评估(一)

文章目录 一、环境搭建1、网络拓扑2、web服务器(win7)配置3、域控(winserver2008)配置4、域内机器(windows 2003)配置5、调试网络是否通常 二、web渗透1、信息搜集2、端口扫描3、目录扫描4、弱口令5、phpmyadmin getshell日志gets…

OPC :快速上手

本系列为OPC技术的快速上以及持续研究和技术实战专栏,将不定期更新。 本章节提供OPC系列技术博文的快速导航。 《OPC服务器简介和入门介绍》 《物联网平台如何为OPC服务器创造新生命力》 《OPC服务器开发之WtOPCSvr——开发文档(1)》 《OPC服…

Linux 磁盘管理命令fdisk mount umount mkfs mkfs.ext2

文章目录 3.Linux 磁盘管理命令3.4 fdisk:磁盘分区案例练习 3.5 mount:挂载文件系统案例练习 3.6 umount:卸载文件系统案例练习 3.7 mkfs:建立各种文件系统案例练习 3.8 mkfs.ext2:建立一个 Ext2/Ext3 文件系统案例练习…

番外篇 | YOLOv8改进之在C2f中引入MSBlock模块(来源于YOLO-MS) | 轻量化网络结构

前言:Hello大家好,我是小哥谈。YOLO-MS是一种基于YOLO(You Only Look Once)的目标检测算法,它利用多尺度特征图提取和融合的方式来检测不同尺度的物体。YOLO-MS在准确率和速度方面都有很好的表现,特别适用于实时场景下的物体检测。在YOLO-MS提出的一种针对于实时目标检测…

Python | Leetcode Python题解之第70题爬楼梯

题目: 题解: class Solution:def climbStairs(self, n: int) -> int:a, b 1, 1for _ in range(n - 1):a, b b, a breturn b

新火种AI|AI让大家都变“土”了!

作者:一号 编辑:美美 AI不仅要把人变“土”,还要把人变多样。 这个世界,终究是变“土”了。 今年五一假期,一个名为“Remini”的AI修图APP火遍了全网。注意,是Remini,而不是Redmi&#xff0…

用Rust解决鸡兔同笼问题

目录 一、什么是鸡兔同笼问题? 二、用Rust解决鸡兔同笼问题 三、鸡兔同笼问题在实际生活中的应用有哪些? 一、什么是鸡兔同笼问题? 鸡兔同笼问题是一种古代著名的数学问题,用于训练逻辑思维和解决方程的能力。 鸡兔同笼问题起…

发布-订阅(Publish-Subscribe)C++实现

1、原理 发布-订阅(Publish-Subscribe)模式是一种消息传递模式,用于构建分布式系统中的通信机制。在这种模式下,消息的发送者(发布者)和消息的接收者(订阅者)之间通过一个称为“主题…

中国4月进口以美元计同比增长8.4%,出口同比增长1.5%

中国按美元计4月进出口同比增速均转负为正,双双超预期。 5月9日周四,海关总署公布数据显示,以美元计价,中国2024年4月进口同比增长8.4%至2201亿美元,前值同比下降1.9%,出口同比增长1.5%至2924.5亿美元&…

景源畅信:抖音小店有哪些人气品类?

抖音小店作为短视频平台中的一股清流,已经成为了众多商家和消费者关注的焦点。在这个平台上,有各种各样的商品琳琅满目,让人眼花缭乱。那么,抖音小店有哪些人气品类呢?下面就从四个不同的方面来详细阐述这个问题。 一、美妆护肤类…