基于Redis和openresty实现高并发缓存架构

目录

  • 概述
  • 缓存架构设计
  • 实践
    • 代码
      • 路由
      • 业务
      • 封装redis
    • 效果

概述

   本文是对项目中 QPS 高并发相关问题的一种解决方案,利用 NginxRedis 的高并发、超低延迟响应,结合 Canal 进行实现。

openrestry官网

   当程序需要提供较高的并发访问时,往往需要在程序中引入缓存技术,通常都是使用Redis 作为缓存,如若再更进一步提升性能,不仅要使用 redis 还要提高 并发,使用能支持超高并发的组件,并将请求响应大部分落在这些组件中。

原本访问缓存逻辑

User—> Nginx -> Tomcat -> Redis

User—> Nginx -> Redis

相关组件可自由下载
懒人直通: openresty/openresty:1.25.3.1-4-alpine-fat redis-7.0.15 docker离线镜像安装包

缓存架构设计

基本数据库 mysql ,整合 canal , 异步的同步数据至 redis 中,主要是利用 redis 与 nginx 的低延迟与高并发。
在这里插入图片描述

  • HTML页面做缓存,浏览器端可以缓存HTML页面和其他静态资源,防止用户频繁刷新对后端造成巨大压力
  • Lvs实现记录不同协议以及不同用户请求链路缓存
  • Nginx这里会做HTML页面缓存配置以及Nginx自身缓存配置(本次没有做Nginx缓存)
  • 数据查找这里用Lua取代了其他语言查找,提高了处理的性能效率,并发处理能力将大大提升
  • 集成Canal实现数据库数据增量实时同步Redis

实践

代码

路由

nginx 路由配置

server {listen 8000;set $target_server '';location /test{access_by_lua_file /usr/local/openresty/nginx/lua/router.lua;proxy_pass $target_server;}location /cache{default_type text/html;content_by_lua_file /usr/local/openresty/nginx/lua/cache_redis.lua;}
}

业务

创建文件 cache_redis.lua

local redis = require "redis_iresty"
local cjson = require("cjson")
local ngx_ERR = ngx.ERR
local ngx_exit = ngx.exit
local ngx_print = ngx.print
-- local ngx_re_match = ngx.re.match
local ngx_var = ngx.var-- 响应输出内容
-- body   http输出body内容
-- status http状态码
-- header http响应头,table格式
local function response(body,status,header)ngx.status = statusif header thenfor key, val in pairs(header) dongx.header[key] = valendend--ngx_print(body)ngx.say(cjson.encode(body))ngx_exit(ngx.status)
endlocal opts = {ip = "10.32.36.142",port = "6379",password = "123456",db_index = 0
}local red = redis:new(opts)local status = 200
local header = {}
local content = {}-- 返回的是一个table类型
local args = ngx.req.get_uri_args()
-- 获取名为"key"的参数
local key = args["key"]  header['content_type'] = 'application/json; charset=utf-8'
local value = red:get(key)
content['data'] = value
content['msg'] = '数据获取成功'
content['key'] = key
response(content,status,header)

封装redis

网上寻找的 redis 二次封装 redis_iresty.lua

local redis_c = require "resty.redis"local ok, new_tab = pcall(require, "table.new")
if not ok or type(new_tab) ~= "function" thennew_tab = function (narr, nrec) return {} end
endlocal _M = new_tab(0, 155)
_M._VERSION = '0.01'local commands = {"append",            "auth",              "bgrewriteaof","bgsave",            "bitcount",          "bitop","blpop",             "brpop","brpoplpush",        "client",            "config","dbsize","debug",             "decr",              "decrby","del",               "discard",           "dump","echo","eval",              "exec",              "exists","expire",            "expireat",          "flushall","flushdb",           "get",               "getbit","getrange",          "getset",            "hdel","hexists",           "hget",              "hgetall","hincrby",           "hincrbyfloat",      "hkeys","hlen","hmget",              "hmset",      "hscan","hset","hsetnx",            "hvals",             "incr","incrby",            "incrbyfloat",       "info","keys","lastsave",          "lindex",            "linsert","llen",              "lpop",              "lpush","lpushx",            "lrange",            "lrem","lset",              "ltrim",             "mget","migrate","monitor",           "move",              "mset","msetnx",            "multi",             "object","persist",           "pexpire",           "pexpireat","ping",              "psetex",            "psubscribe","pttl","publish",      --[[ "punsubscribe", ]]   "pubsub","quit","randomkey",         "rename",            "renamenx","restore","rpop",              "rpoplpush",         "rpush","rpushx",            "sadd",              "save","scan",              "scard",             "script","sdiff",             "sdiffstore","select",            "set",               "setbit","setex",             "setnx",             "setrange","shutdown",          "sinter",            "sinterstore","sismember",         "slaveof",           "slowlog","smembers",          "smove",             "sort","spop",              "srandmember",       "srem","sscan","strlen",       --[[ "subscribe",  ]]     "sunion","sunionstore",       "sync",              "time","ttl","type",         --[[ "unsubscribe", ]]    "unwatch","watch",             "zadd",              "zcard","zcount",            "zincrby",           "zinterstore","zrange",            "zrangebyscore",     "zrank","zrem",              "zremrangebyrank",   "zremrangebyscore","zrevrange",         "zrevrangebyscore",  "zrevrank","zscan","zscore",            "zunionstore",       "evalsha"
}local mt = { __index = _M }local function is_redis_null( res )if type(res) == "table" thenfor k,v in pairs(res) doif v ~= ngx.null thenreturn falseendendreturn trueelseif res == ngx.null thenreturn trueelseif res == nil thenreturn trueendreturn false
endfunction _M.close_redis(self, redis)  if not redis then  return  end  --释放连接(连接池实现)local pool_max_idle_time = self.pool_max_idle_time --最大空闲时间 毫秒  local pool_size = self.pool_size --连接池大小  local ok, err = redis:set_keepalive(pool_max_idle_time, pool_size)  if not ok then  ngx.say("set keepalive error : ", err)  end  
end  -- change connect address as you need
function _M.connect_mod( self, redis )redis:set_timeout(self.timeout)local ok, err = redis:connect(self.ip, self.port)if not ok then  ngx.say("connect to redis error : ", err)  return self:close_redis(redis)  endif self.password then ----密码认证local count, err = redis:get_reused_times()if 0 == count then ----新建连接,需要认证密码ok, err = redis:auth(self.password)if not ok thenngx.say("failed to auth: ", err)returnendelseif err then  ----从连接池中获取连接,无需再次认证密码ngx.say("failed to get reused times: ", err)returnendendreturn ok,err;
endfunction _M.init_pipeline( self )self._reqs = {}
endfunction _M.commit_pipeline( self )local reqs = self._reqsif nil == reqs or 0 == #reqs thenreturn {}, "no pipeline"elseself._reqs = nilendlocal redis, err = redis_c:new()if not redis thenreturn nil, errendlocal ok, err = self:connect_mod(redis)if not ok thenreturn {}, errendredis:init_pipeline()for _, vals in ipairs(reqs) dolocal fun = redis[vals[1]]table.remove(vals , 1)fun(redis, unpack(vals))endlocal results, err = redis:commit_pipeline()if not results or err thenreturn {}, errendif is_redis_null(results) thenresults = {}ngx.log(ngx.WARN, "is null")end-- table.remove (results , 1)--self.set_keepalive_mod(redis)self:close_redis(redis)  for i,value in ipairs(results) doif is_redis_null(value) thenresults[i] = nilendendreturn results, err
endlocal function do_command(self, cmd, ... )if self._reqs thentable.insert(self._reqs, {cmd, ...})returnendlocal redis, err = redis_c:new()if not redis thenreturn nil, errendlocal ok, err = self:connect_mod(redis)if not ok or err thenreturn nil, errendredis:select(self.db_index)local fun = redis[cmd]local result, err = fun(redis, ...)if not result or err then-- ngx.log(ngx.ERR, "pipeline result:", result, " err:", err)return nil, errendif is_redis_null(result) thenresult = nilend--self.set_keepalive_mod(redis)self:close_redis(redis)  return result, err
endfor i = 1, #commands dolocal cmd = commands[i]_M[cmd] =function (self, ...)return do_command(self, cmd, ...)end
endfunction _M.new(self, opts)opts = opts or {}local timeout = (opts.timeout and opts.timeout * 1000) or 1000local db_index= opts.db_index or 0local ip = opts.ip or '127.0.0.1'local port = opts.port or 6379local password = opts.passwordlocal pool_max_idle_time = opts.pool_max_idle_time or 60000local pool_size = opts.pool_size or 100return setmetatable({timeout = timeout,db_index = db_index,ip = ip,port = port,password = password,pool_max_idle_time = pool_max_idle_time,pool_size = pool_size,_reqs = nil }, mt)
endreturn _M

效果

在这里插入图片描述
在这里插入图片描述
  环境说明:搭建基本上使用了 docker,使用无线网, 单次请求一大半落在了个位数 毫秒级内,最慢情况下,基本不会超过 60ms

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于大数据架构的内容安全风控与分析

1 项目介绍 1.1 研究目的和意义 在数字化时代,内容安全成为了互联网企业面临的一个重要挑战。海量数据的产生与传播,伴随着不良信息和网络安全威胁的日益增加。因此,本课题旨在通过构建一个基于大数据架构的内容安全风控与分析系统&#xf…

概率论论文(关于“到课率”的贝叶斯推理应用)

概率论论文(关于“到课率”的贝叶斯推理应用) 全条件概率公式和贝叶斯公式趣味识 思考1: (引自贝叶斯公式及朴素贝叶斯分类算法应用初探) “狼来了”的故事想必大家都知道,小孩子第三次对村民说狼来了的时候,村民们没有相信他的话&#xff0…

基于weixin小程序农场驿站系统的设计

管理员账户功能包括:系统首页,个人中心,农场资讯管理,用户管理,卖家管理,用户分享管理,分享类型管理,商品信息管理,商品类型管理 开发系统:Windows 架构模式&…

【新手友好】计算机SCI期刊,IF=3+,编辑负责,修改稿件认真

一、期刊名称 Journal of Communications and Networks 二、期刊简介概况 期刊类型:SCI 学科领域:计算机科学 影响因子:3.6 中科院分区:3区 三、期刊征稿范围 《通信与网络杂志》每年出版六期,致力于发表高质量的…

Spring-Boot整合Redis【随记】

基于spring boot整合. 1 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency> 2 编写redis的配置文件 application.yml进行编写. 属性: url por…

《昇思25天学习打卡营第3天 | 昇思MindSpore数据集 Dataset》

第三天 今天学习了不同的数据集加载方式、数据集常见操作和自定义数据集方法。 1.数据集加载。 以Mnist数据集为例。mindspore.dataset提供的接口仅支持解压后的数据文件&#xff0c;因此我们使用download库下载数据集并解压。 2.数据集迭代。 用create_tuple_iterator或create…

摒弃反模式:使用Kotlin委托优化Android BaseActivity

摒弃反模式&#xff1a;使用Kotlin委托优化Android BaseActivity 在Android开发中&#xff0c;许多开发者习惯于创建名为“BaseActivity”或“BaseFragment”的基类&#xff0c;以便在所有Activity或Fragment中共享一些通用行为。这种方法乍一看似乎是个好主意&#xff0c;但实…

Kotlin 中的内联函数

1 inline 内联函数&#xff1a;消除 Lambda 带来的运行时开销。 举例来说&#xff1a; fun main() {val num1 100val num2 80val result num1AndNum2(num1, num2) { n1, n2 ->n1 n2} }fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int): Int …

Docker 安装最新版本 Jenkins

目录 1、下载、启动容器、更新到最新版本 2、查看初始密码两种方式&#xff1a; 3、默认安装的部分未汉化&#xff0c;删除默认的汉化插件。重启容器&#xff0c;重新安装汉化插件 4、安装 Publish over SSH、docker-build-step 、Docker Commons 插件 5、配置服务器连接信…

【红帽战报】6月RHCE考试喜报!

往期战报回顾&#xff1a; 点击查看【战报】5月RHCE考试喜报&#xff01;通过率100% 点击查看【战报】4月份红帽考试战报&#xff01; 点击查看【战报】PASS&#xff01;PASS&#xff01;2023年终来一波RHCE考试 微思网络-红帽官方授权合作伙伴&#xff01;面向全国招生&…

Python爬取中国天气网天气数据.

一、主题式网络爬虫设计方案 1.主题式网络爬虫名称 名称&#xff1a;Python爬取中国天气网天气数据 2.主题式网络爬虫爬取的内容与数据特征分析 本次爬虫主要爬取中国天气网天气数据 3.主题式网络爬虫设计方案概述&#xff08;包括实现思路与技术难点&#xff09; reques…

[火灾警报系统]yolov5_7.0-pyside6火焰烟雾识别源码

国内每年都会发生大大小小的火灾&#xff0c;造成生命、财产的损失。但是很多火灾如果能够早期发现&#xff0c;并及时提供灭火措施&#xff0c;将会大大较小损失。本套源码采用yolov5-7.0目标检测算法结合pyside6可视化界面源码&#xff0c;当检测到火灾时&#xff0c;能否发出…

第三阶段Spark

Spark和PySpark的介绍 PySpark的相关设置 安装PySpark库 pip install pyspark pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyspark 构建PySpark执行环境入口对象 # 导包 from pyspark import SparkConf, SparkContext# 创建SparkConf类对象 conf SparkConf()…

多行业预约门店服务小程序源码系统 支持多门店预约 带完整的安装代码包以及搭建教程

系统概述 该系统基于先进的云计算和大数据技术&#xff0c;采用模块化设计&#xff0c;具有高度的可扩展性和可定制性。无论是餐饮、美容美发、健身房还是其他服务行业&#xff0c;都可以通过该系统轻松实现多门店预约功能。同时&#xff0c;我们还提供了丰富的接口和插件&…

STM32-hal库学习(4)--usart/uart通信 (同时显示在oled)

前言&#xff1a; 关于usart详解&#xff1a; stm32-USART通信-CSDN博客 因为在oled上显示&#xff0c;我们直接在上一个工程进行修改&#xff1a; STM32_hal库学习&#xff08;3&#xff09;-OLED显示-CSDN博客 其他配置与oled显示工程保持不变&#xff0c;打开oled文件的…

html渲染的文字样式大小不统一解决方案

React Hooks 封装可粘贴图片的输入框组件&#xff08;wangeditor&#xff09;_react 支持图片拖拽的输入框-CSDN博客 这篇文章中的wangediter可粘贴图片的输入框&#xff0c;输入的文字和粘贴的文字在dangerouslySetInnerHTML渲染后出现了字体不统一的情况 在html中右键检查可…

短视频利器 ffmpeg (2)

ffmpeg 官网这样写到 Converting video and audio has never been so easy. 如何轻松简单的使用&#xff1a; 1、下载 官网&#xff1a;http://www.ffmpeg.org 安装参考文档&#xff1a; https://blog.csdn.net/qq_36765018/article/details/139067654 2、安装 # 启用RPM …

基于Java微信小程序火锅店点餐系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f;感兴趣的可以先收藏起来&#xff0c;还…

Dataease配置Nginx代理

Dataease配置Nginx代理 一.修改前端静态资源地址和后端接口地址 **1.**修改应用程序的上下文路径 配置文件地址&#xff1a;backend/src/main/resources 找到文件application-whole.properties&#xff0c;做如下修改&#xff1a; **2.**修改前端静态资源路径和打包配置 配…

【基于深度学习方法的激光雷达点云配准系列之GeoTransformer】——模型部分浅析(1)

【GeoTransformer系列】——模型部分 1. create_model2. model的本质3. 模型的主要结构3.1 backbone3.2 transformer本篇继续对GeoTransformer/experiments/geotransformer.kitti.stage5.gse.k3.max.oacl.stage2.sinkhorn/下面的trainval.py进行详细的解读,主要是模型部分, 可以…