黑马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笔记高级篇 | 多级缓存相关推荐

  1. Redis高级篇-多级缓存

    Redis高级篇资料下载 1.什么是多级缓存 传统的缓存策略一般是请求到达Tomcat后,先查询Redis,如果未命中则查询数据库,如图: 存在下面的问题: •请求要经过Tomcat处理,Tomcat ...

  2. 谷粒商城-个人笔记(高级篇二)

    目录 二.商城业务-首页 1.整合thymeleaf渲染首页 1).在"gulimall-product"项目中导入前端代码: 2).渲染一级分类数据&&整合dev ...

  3. 数据库MySQL学习笔记高级篇(周阳)

    数据库MySQL学习笔记高级篇 1. mysql的架构介绍 mysql简介 高级Mysql mysqlLinux版的安装 mysql配置文件 mysql逻辑架构介绍 mysql存储引擎 2. 索引优化 ...

  4. R语言学习笔记——高级篇:第十四章-主成分分析和因子分析

    R语言 R语言学习笔记--高级篇:第十四章-主成分分析和因子分析 文章目录 R语言 前言 一.R中的主成分和因子分析 二.主成分分析 2.1.判断主成分的个数 2.2.提取主成分 2.3.主成分旋转 ...

  5. Redis笔记-基础篇(黑马视频教程)

    写在开头 这是我在观看黑马Redis视频教程中根据PPT和上课内容,个人写的笔记,中间有部分来源于百度,如有侵权,联系我删除. 文章目录 写在开头 NoSQL数据库简介 技术发展 NoSQL数据库 R ...

  6. 电商项目随手笔记(高级篇)

    项目笔记 Elastic Search 安装与使用 整合到Spring Boot 商城业务 商品的上架 Thymeleaf 配置Thymeleaf 一级分类的渲染 二级三级分类的渲染 缓存 Redis ...

  7. 谷粒商城-个人笔记(高级篇五)

    目录 十一.支付 1.内网穿透 1).简介 2).使用场景 3).内网穿透常用软件和安装 2.支付整合 1).支付宝加密原理 2).配置支付宝沙箱环境 3).订单支付与同步通知 4).订单列表页渲染完 ...

  8. 黑马Redis视频教程实战篇(五)

    目录 一.达人探店 1.1.发布探店笔记 1.2.查看探店笔记 1.3.点赞功能 1.4.点赞排行榜 二.好友关注 2.1.关注和取消关注 2.2.共同关注 2.3.Feed流实现方案 2.4.推送到 ...

  9. 黑马Redis视频教程实战篇(六)

    目录 一.附近商户 1.1.GEO数据结构的基本用法 1.2.导入店铺数据到GEO 1.3.实现附近商户功能 二.用户签到 2.1.BitMap功能演示 2.2.实现签到功能 2.3.签到统计 2.4 ...

最新文章

  1. Linux之用户相关操作
  2. python-9-IO编程
  3. rehat linux设置ip,RedhatHat配置IPv6地址
  4. 【虚拟机】关于VMware 提示“无法获得VMCI驱动程序的版本:句柄无效”的解决方案...
  5. RH124 第六单元   管理物理存储
  6. udp java_Java实现Udp网络编程
  7. 地平线:黎明时分中的云渲染技术
  8. 2021考研计算机网络,2021考研:计算机网络复习重点
  9. pydebugger
  10. 在Windows Server 2012 R2 Standard 部署 ASP.NET Core程序
  11. 使用闭包的方式实现一个累加函数 addNum
  12. word2vec的理解
  13. 【汇编语言】通用数据处理指令——数据传送类指令
  14. ZeroMQ 的模式
  15. 编曲时如何在FL Studio卷帘窗口修改单个音符音量
  16. 当Java枚举遇到位掩码,还能这么玩?
  17. GitLab强制关闭双因素认证
  18. DDR的ZQ校准信号-翻译
  19. 修改Docker默认的数据目录
  20. 我的2021年终回顾:一如少年,眼眸有星辰,心中有山海

热门文章

  1. timesten java_java怎么联接服务器上的timesten数据库
  2. rtklib三之relpos rtkpo庖丁解牛
  3. 基于TI-335XD核心板的高精度腹膜透析仪
  4. 告别编码5分钟,命名2小时!史上最全的Java命名规范参考!
  5. 5月22日比特币披萨日,今天你吃披萨了吗?
  6. 云工具支持手机号md5解密、http服务端、json格式化
  7. 【转】电容的作用?电容器的作用?
  8. Echarts地图局部放大及返回实例
  9. 视频剪辑用什么软件?新手入门看这一篇就够!
  10. deap.algorithms模块库官方文档库翻译