黑马Redis笔记高级篇 | 多级缓存
黑马Redis笔记高级篇 | 多级缓存(黑马教程云服务器踩坑记录)
- 1、JVM进程缓存(tomcat服务内部)
- 1.1 导入商品案例
- 1.2 初识Caffeine
- 1.3 实现进程缓存
- 2、Lua语法入门
- 2.1 初识Lua
- 2.2 变量和循环
- 2.3 条件控制、函数
- 3、多级缓存
- 3.1 安装OpenResty
- 3.2 OpenResty快速入门
- 3.3 请求参数处理
- 3.4 查询Tomcat
- 3.5 Redis缓存预热
- 3.6 查询Redis缓存
- 3.7 Nginx本地缓存
- 4、缓存同步策略
- 4.1 数据同步策略
- 4.2 安装Canal
- 4.3 监听Canal
- 多级缓存总结
1、JVM进程缓存(tomcat服务内部)
1.1 导入商品案例
1、安装docker教程:黑马-Centos7安装Docker
2、根据本章资料中【案例导入说明.md】启动mysql镜像,注意要先关闭本地的mysql服务,否则3306端口被占用,mysql镜像会启动失败。
3、接下来完全按照【案例导入说明.md】导入
1.2 初识Caffeine
1.3 实现进程缓存
2、Lua语法入门
2.1 初识Lua
2.2 变量和循环
2.3 条件控制、函数
3、多级缓存
3.1 安装OpenResty
按照【安装OpenResty】教程安装,云服务器记得打开配置文件中对应端口(8081)
3.2 OpenResty快速入门
本地访问商品详情页时需要开启本地nginx,同时云服务器也要reload配置。
3.3 请求参数处理
//服务器OpenResty的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;#lua 模块lua_package_path "/usr/local/openresty/lualib/?.lua;;";#c模块 lua_package_cpath "/usr/local/openresty/lualib/?.so;;"; server {listen 8081;server_name localhost;location ~ /api/item/(\d+) {# 默认的响应类型default_type application/json;# 响应结果由lua/item.lua文件决定content_by_lua_file lua/item.lua;}location / {root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}}
}
--OpenResty的item.lua
-- 获取路径参数
local id = ngx.var[1]
-- 返回结果
ngx.say('{"id":' .. id .. ',"name":"SALSA AIR","title":"RIMOWA 26寸托运箱拉杆箱 SALSA AIR系列果绿色 820.70.36.4","price":19900,"image":"https://m.360buyimg.com/mobilecms/s720x720_jfs/t6934/364/1195375010/84676/e9f2c55f/597ece38N0ddcbc77.jpg!q70.jpg.webp","category":"拉杆箱","brand":"RIMOWA","spec":"","status":1,"createTime":"2019-04-30T16:00:00.000+00:00","updateTime":"2019-04-30T16:00:00.000+00:00","stock":2999,"sold":31290}')
3.4 查询Tomcat
这里由于本人的OpenResty安装在云服务器,而运行的程序(即视频中说到的tomcat服务器)在本地,实操踩了很多坑,详细记录一下。
首先我要先理清请求的过程:
1、浏览器向windows本地80端口发送请求:http://localhost/api/item/10003
2、该请求被本地的nginx拦截处理,转发给:云服务器IP:8081,即云服务器OpenResty
3、云服务器OpenResty(基于nginx)监听到符合/api/item/(\d+)的请求,调用lua脚本查询数据。由于此时云服务器被反向代理到windows本地,lua脚本查询到本地8081端口的tomcat服务器中的数据。
因此,想要正确地查到数据,首先需要先保证本地程序正处于运行状态,为8081端口提供服务。
其次需要保证本地端口能被云服务器(外网)访问,因此需要对本地windows进行内网穿透,这里我使用的是花生壳。
接下来贴出我的对应配置即代码以供参考:(出于隐私保护,我将对应ip和域名替换成【解释】的形式)
1、本地nginx配置文件
#user nobody;
worker_processes 1;events {worker_connections 1024;
}http {include mime.types;default_type application/octet-stream;sendfile on;#tcp_nopush on;keepalive_timeout 65;#nginx的业务集群,nginx本地缓存,redis缓存,tomcat查询upstream nginx-cluster{server 【云服务器ip】:8081;}server {listen 80;server_name localhost;location /api {proxy_pass http://nginx-cluster;}location / {root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}}
}
2、云服务器OpenResty中nginx配置
#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;#lua 模块lua_package_path "/usr/local/openresty/lualib/?.lua;;";#c模块 lua_package_cpath "/usr/local/openresty/lualib/?.so;;"; server {listen 8081;server_name localhost;location /item {# windows电脑的ip和java服务端口proxy_pass http://【本地windows内网穿透后的外网域名】:【网穿透后的端口】;}location ~ /api/item/(\d+) {# 默认的响应类型default_type application/json;# 响应结果由lua/item.lua文件决定content_by_lua_file lua/item.lua;}location / {root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}}
}
3、云服务器OpenResty中执行的lua脚本(这里调用的 read_http 等函数可以自行去黑马资料中找)
--导入common库
local common = require('common')
local read_http = common.read_http
-- 导入cjson库
local cjson = require('cjson')-- 获取路径参数
local id = ngx.var[1]-- 查询商品信息
local itemJSON = read_http("/item/" .. id,nil)
-- 查询库存信息
local stockJSON = read_http("/item/stock/" .. id,nil)-- JSON转化为lua的table
local item = cjson.decode(itemJSON)
local stock = cjson.decode(stockJSON)-- 组合数据
item.stock = stock.stock
item.sold = stock.sold-- 把item序列化为json返回
ngx.say(cjson.encode(item))
多台tomcat负载均衡案例的云服务器OpenResty中nginx配置
#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;#lua 模块lua_package_path "/usr/local/openresty/lualib/?.lua;;";#c模块 lua_package_cpath "/usr/local/openresty/lualib/?.so;;"; upstream tomcat-cluster {hash $request_uri;server 【本地windows内网穿透后的外网域名】:【网穿透后的端口1】;# 注意这里没有http://server 【本地windows内网穿透后的外网域名】:【网穿透后的端口2】;# 注意这里没有http://}server {listen 8081;server_name localhost;location /item {# windows电脑的ip和java服务端口proxy_pass http://tomcat-cluster;}location ~ /api/item/(\d+) {# 默认的响应类型default_type application/json;# 响应结果由lua/item.lua文件决定content_by_lua_file lua/item.lua;}location / {root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}}
}
3.5 Redis缓存预热
1、云服务器利用Docker安装Redis
docker run --name redis -p 6379:6379 -d redis redis-server --appendonly yes
2、在item-service服务中引入Redis依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
3、配置Redis地址
spring:redis:host: 【云服务器的ip(用于访问docker中6379端口的redis)】
4、编写初始化类
@Component
public class RedisHandler implements InitializingBean {@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate IItemService itemService;@Autowiredprivate IItemStockService stockService;private static final ObjectMapper MAPPER = new ObjectMapper();@Overridepublic void afterPropertiesSet() throws Exception {//初始化缓存// 1.查询商品信息List<Item> itemList = itemService.list();// 2.放入缓存for (Item item : itemList) {// 2.1.item序列化为JSONString json = MAPPER.writeValueAsString(item);// 2.2.存入redisredisTemplate.opsForValue().set("item:id:" + item.getId(), json);}// 3.查询商品库存信息List<ItemStock> stockList = stockService.list();// 4.放入缓存for (ItemStock stock : stockList) {// 2.1.item序列化为JSONString json = MAPPER.writeValueAsString(stock);// 2.2.存入redisredisTemplate.opsForValue().set("item:stock:id:" + stock.getId(), json);}}
}
3.6 查询Redis缓存
1、修改common.lua引入Redis模块
-- 引入redis模块
local redis = require('resty.redis')
-- 初始化Redis对象
local red = redis:new()
-- 设置Redis超时时间
red:set_timeouts(1000, 1000, 1000)-- 关闭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 thenngx.log(ngx.ERR, "放入redis连接池失败: ", err)end
end-- 查询redis的方法 ip和port是redis地址,key是查询的key
local function read_redis(ip, port, key)-- 获取一个连接local ok, err = red:connect(ip, port)if not ok thenngx.log(ngx.ERR, "连接redis失败 : ", err)return nilend-- 查询redislocal resp, err = red:get(key)-- 查询失败处理if not resp thenngx.log(ngx.ERR, "查询Redis失败: ", err, ", key = " , key)end--得到的数据为空处理if resp == ngx.null thenresp = nilngx.log(ngx.ERR, "查询Redis数据为空, key = ", key)endclose_redis(red)return resp
end-- 封装函数,发送http请求,并解析响应
local function read_http(path, params)local resp = ngx.location.capture(path,{method = ngx.HTTP_GET,args = params,})if not resp then-- 记录错误信息,返回404ngx.log(ngx.ERR, "http查询失败, path: ", path , ", args: ", args)ngx.exit(404)endreturn resp.body
end-- 将方法导出
local _M = { read_http = read_http,read_redis = read_redis
}
return _M
2、修改item.lua,设置为先查redis后查tomcat,减小tomcat服务器的压力,这里的127.0.0.1要替换成云服务器ip。
这里我也不清楚原因,明明OpenResty和redis都在云服务器上,但使用127.0.0.1查询就只能查到前三个商品。如果有大佬懂的话欢迎在评论区解答!
--导入common库
local common = require('common')
local read_http = common.read_http
local read_redis = common.read_redis
-- 导入cjson库
local cjson = require('cjson')-- 封装查询函数
function read_data(key, path, params)-- 先查redis-- local resp = read_redis("127.0.0.1",6379,key)local resp = read_redis("【云服务器ip】",6379,key)-- 判断查询结果if not resp then-- 这里一定要加(key or nil),否则会报错lua entry thread aborted: runtime errorngx.log("redis查询失败,尝试查询http key:",(key or nil))-- redis查询失败,查询httpresp = read_http(path, params)endreturn resp
end-- 获取路径参数
local id = ngx.var[1]-- 查询商品信息
local itemJSON = read_data("item:id:" .. id,"/item/" .. id,nil)
-- 查询库存信息
local stockJSON = read_data("item:stock:id:" .. id,"/item/stock/" .. id,nil)-- JSON转化为lua的table
local item = cjson.decode(itemJSON)
local stock = cjson.decode(stockJSON)-- 组合数据
item.stock = stock.stock
item.sold = stock.sold-- 把item序列化为json返回
ngx.say(cjson.encode(item))
3.7 Nginx本地缓存
1、OpenResty的配置中开启共享词典(本地缓存)
#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;#lua 模块lua_package_path "/usr/local/openresty/lualib/?.lua;;";#c模块 lua_package_cpath "/usr/local/openresty/lualib/?.so;;"; # 添加共享词典(本地缓存)lua_shared_dict item_cache 150m;upstream tomcat-cluster {hash $request_uri;server 【本地windows内网穿透后的外网域名】:【网穿透后的端口1】;# 注意这里没有http://server 【本地windows内网穿透后的外网域名】:【网穿透后的端口2】;# 注意这里没有http://}server {listen 8081;server_name localhost;location /item {# windows电脑的ip和java服务端口proxy_pass http://tomcat-cluster;}location ~ /api/item/(\d+) {# 默认的响应类型default_type application/json;# 响应结果由lua/item.lua文件决定content_by_lua_file lua/item.lua;}location / {root html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location = /50x.html {root html;}}
}
2、修改item.lua中的业务逻辑
--导入common库
local common = require('common')
local read_http = common.read_http
local read_redis = common.read_redis
-- 导入cjson库
local cjson = require('cjson')
-- 导入共享词典,本地缓存
local item_cache = ngx.shared.item_cache-- 封装查询函数
function read_data(key, expire, path, params)-- 先查本地缓存local val = item_cache:get(key)if not val thenngx.log(ngx.ERR, "本地缓存查询失败,尝试查询redis key:",(key or nil))-- 查redis-- val = read_redis("127.0.0.1",6379,key)val = read_redis("124.222.157.95",6379,key)-- 判断查询结果if not val thenngx.log(ngx.ERR, "redis查询失败,尝试查询http key:",(key or nil))-- redis查询失败,查询httpval = read_http(path, params)endend-- 查询成功,写入本地缓存item_cache:set(key, val, expire)-- 返回数据return val
end-- 获取路径参数
local id = ngx.var[1]-- 查询商品信息
local itemJSON = read_data("item:id:" .. id, 1800, "/item/" .. id,nil)
-- 查询库存信息
local stockJSON = read_data("item:stock:id:" .. id, 60, "/item/stock/" .. id,nil)-- JSON转化为lua的table
local item = cjson.decode(itemJSON)
local stock = cjson.decode(stockJSON)-- 组合数据
item.stock = stock.stock
item.sold = stock.sold-- 把item序列化为json返回
ngx.say(cjson.encode(item))
4、缓存同步策略
4.1 数据同步策略
1、基于MQ的异步通知:发消息对于item-service仍然有侵入
2、基于Canal的异步通知:无侵入
4.2 安装Canal
根据【安装Canal.md】安装即可。
4.3 监听Canal
跟着视频教程操作即可,云服务器11111端口记得开放。
多级缓存总结
黑马Redis笔记高级篇 | 多级缓存相关推荐
- Redis高级篇-多级缓存
Redis高级篇资料下载 1.什么是多级缓存 传统的缓存策略一般是请求到达Tomcat后,先查询Redis,如果未命中则查询数据库,如图: 存在下面的问题: •请求要经过Tomcat处理,Tomcat ...
- 谷粒商城-个人笔记(高级篇二)
目录 二.商城业务-首页 1.整合thymeleaf渲染首页 1).在"gulimall-product"项目中导入前端代码: 2).渲染一级分类数据&&整合dev ...
- 数据库MySQL学习笔记高级篇(周阳)
数据库MySQL学习笔记高级篇 1. mysql的架构介绍 mysql简介 高级Mysql mysqlLinux版的安装 mysql配置文件 mysql逻辑架构介绍 mysql存储引擎 2. 索引优化 ...
- R语言学习笔记——高级篇:第十四章-主成分分析和因子分析
R语言 R语言学习笔记--高级篇:第十四章-主成分分析和因子分析 文章目录 R语言 前言 一.R中的主成分和因子分析 二.主成分分析 2.1.判断主成分的个数 2.2.提取主成分 2.3.主成分旋转 ...
- Redis笔记-基础篇(黑马视频教程)
写在开头 这是我在观看黑马Redis视频教程中根据PPT和上课内容,个人写的笔记,中间有部分来源于百度,如有侵权,联系我删除. 文章目录 写在开头 NoSQL数据库简介 技术发展 NoSQL数据库 R ...
- 电商项目随手笔记(高级篇)
项目笔记 Elastic Search 安装与使用 整合到Spring Boot 商城业务 商品的上架 Thymeleaf 配置Thymeleaf 一级分类的渲染 二级三级分类的渲染 缓存 Redis ...
- 谷粒商城-个人笔记(高级篇五)
目录 十一.支付 1.内网穿透 1).简介 2).使用场景 3).内网穿透常用软件和安装 2.支付整合 1).支付宝加密原理 2).配置支付宝沙箱环境 3).订单支付与同步通知 4).订单列表页渲染完 ...
- 黑马Redis视频教程实战篇(五)
目录 一.达人探店 1.1.发布探店笔记 1.2.查看探店笔记 1.3.点赞功能 1.4.点赞排行榜 二.好友关注 2.1.关注和取消关注 2.2.共同关注 2.3.Feed流实现方案 2.4.推送到 ...
- 黑马Redis视频教程实战篇(六)
目录 一.附近商户 1.1.GEO数据结构的基本用法 1.2.导入店铺数据到GEO 1.3.实现附近商户功能 二.用户签到 2.1.BitMap功能演示 2.2.实现签到功能 2.3.签到统计 2.4 ...
最新文章
- Linux之用户相关操作
- python-9-IO编程
- rehat linux设置ip,RedhatHat配置IPv6地址
- 【虚拟机】关于VMware 提示“无法获得VMCI驱动程序的版本:句柄无效”的解决方案...
- RH124 第六单元 管理物理存储
- udp java_Java实现Udp网络编程
- 地平线:黎明时分中的云渲染技术
- 2021考研计算机网络,2021考研:计算机网络复习重点
- pydebugger
- 在Windows Server 2012 R2 Standard 部署 ASP.NET Core程序
- 使用闭包的方式实现一个累加函数 addNum
- word2vec的理解
- 【汇编语言】通用数据处理指令——数据传送类指令
- ZeroMQ 的模式
- 编曲时如何在FL Studio卷帘窗口修改单个音符音量
- 当Java枚举遇到位掩码,还能这么玩?
- GitLab强制关闭双因素认证
- DDR的ZQ校准信号-翻译
- 修改Docker默认的数据目录
- 我的2021年终回顾:一如少年,眼眸有星辰,心中有山海