网站运营服务商,搞软件开发的一般学什么专业,网站设计的难点,潍坊市作风建设年官方网站目录
一、传统缓存的问题、多级缓存方案。
二、JVM进程缓存。
1#xff09;进程缓存和缓存。
2#xff09;导入商品案例。
1.安装MySQL
2.导入SQL
3.导入Demo工程
4.导入商品查询页面
3#xff09;初识Caffeine#xff08;就是在springboot学过的注解方式的cache进程缓存和缓存。
2导入商品案例。
1.安装MySQL
2.导入SQL
3.导入Demo工程
4.导入商品查询页面
3初识Caffeine就是在springboot学过的注解方式的cache。
4实现进程缓存。
三、Lua语法入门。
1初识Lua。
2数据类型、变量和循环。
3函数、条件控制。
四、多级缓存。
1安装OpenResty。
1.安装
2.启动和运行
3.备注
2OpenResty快速入门。
3请求参数处理。
4查询Tomcat。
5Tomcat集群的负载均衡。
6Redis缓存预热。
7查询Redis缓存。
8Nginx本地缓存。
五、缓存同步策略。
1数据同步策略。
2安装Canal。
2.1初识Canal。
2.2安装和配置Canal。
1.开启MySQL主从
2.安装Canal
3监听Canal。
六、多级缓存总结。 一、传统缓存的问题、多级缓存方案。 二、JVM进程缓存。
1进程缓存和缓存。
在Java中进程缓存和缓存也是两个不同的概念。 进程缓存在Java中进程缓存通常指JVM的堆内存它是Java虚拟机为每个Java进程分配的内存空间。Java进程可以使用堆内存来存储对象、数组等数据结构以及执行方法时所需的局部变量、方法参数等。Java程序可以通过调整JVM的参数来控制堆内存的大小从而影响程序的性能和内存占用。 缓存在Java中缓存通常指应用程序中的缓存机制用于临时存储经常访问的数据以提高数据访问速度。Java应用程序可以使用各种缓存框架来实现缓存机制例如Ehcache、Guava Cache、Redis等。这些框架通常提供了一些API来支持数据的读取、写入、删除等操作并可以通过配置文件或代码来指定缓存的容量、过期时间、失效策略等参数。
总的来说Java中的进程缓存和缓存都是为了提高程序的性能和响应速度而存在的但它们的作用和实现方式有所不同。进程缓存是JVM为每个Java进程分配的内存空间用于存储Java对象和方法执行时所需的数据而缓存是应用程序中的一种机制用于缓存经常访问的数据以减少对数据库或其他数据源的访问次数提高程序的性能。
需要注意的是Caffeine是一个进程级别的缓存它只在单个Java进程内生效。
2导入商品案例。 为了演示多级缓存我们先导入一个商品管理的案例其中包含商品的CRUD功能。我们将来会给查询商品添加多级缓存。 1.安装MySQL 后期做数据同步需要用到MySQL的主从功能所以需要大家在虚拟机中利用Docker来运行一个MySQL容器。 1.1.准备目录 为了方便后期配置MySQL我们先准备两个目录用于挂载容器的数据和配置文件目录 # 进入/tmp目录
cd /tmp
# 创建文件夹
mkdir mysql
# 进入mysql目录
cd mysql1.2.运行命令 进入mysql目录后执行下面的Docker命令 docker run \-p 3306:3306 \--name mysql \-v $PWD/conf:/etc/mysql/conf.d \-v $PWD/logs:/logs \-v $PWD/data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORD123 \--privileged \-d \mysql:5.7.251.3.修改配置 在/tmp/mysql/conf目录添加一个my.cnf文件作为mysql的配置文件 # 创建文件
touch /tmp/mysql/conf/my.cnf文件的内容如下 [mysqld]
skip-name-resolve
character_set_serverutf8
datadir/var/lib/mysql
server-id10001.4.重启 配置修改后必须重启容器 docker restart mysql2.导入SQL 接下来利用Navicat客户端连接MySQL然后导入课前资料提供的sql文件 其中包含两张表 tb_item商品表包含商品的基本信息 tb_item_stock商品库存表包含商品的库存信息 之所以将库存分离出来是因为库存是更新比较频繁的信息写操作较多。而其他信息修改的频率非常低。 3.导入Demo工程 下面导入课前资料提供的工程 项目结构如图所示 其中的业务包括 分页查询商品 新增商品 修改商品 修改库存 删除商品 根据id查询商品 根据id查询库存 业务全部使用mybatis-plus来实现如有需要请自行修改业务逻辑。 3.1.分页查询商品 在com.heima.item.web包的ItemController中可以看到接口定义 3.2.新增商品 在com.heima.item.web包的ItemController中可以看到接口定义 3.3.修改商品 在com.heima.item.web包的ItemController中可以看到接口定义 3.4.修改库存 在com.heima.item.web包的ItemController中可以看到接口定义 3.5.删除商品 在com.heima.item.web包的ItemController中可以看到接口定义 这里是采用了逻辑删除将商品状态修改为3 3.6.根据id查询商品 在com.heima.item.web包的ItemController中可以看到接口定义 这里只返回了商品信息不包含库存 3.7.根据id查询库存 在com.heima.item.web包的ItemController中可以看到接口定义 3.8.启动 注意修改application.yml文件中配置的mysql地址信息 需要修改为自己的虚拟机地址信息、还有账号和密码。 修改后启动服务访问http://localhost:8081/item/10001即可查询数据 4.导入商品查询页面 商品查询是购物页面与商品管理的页面是分离的。 部署方式如图 我们需要准备一个反向代理的nginx服务器如上图红框所示将静态的商品页面放到nginx目录中。 页面需要的数据通过ajax向服务端nginx业务集群查询。 4.1.运行nginx服务 这里我已经给大家准备好了nginx反向代理服务器和静态资源。 我们找到课前资料的nginx目录 将其拷贝到一个非中文目录下运行这个nginx服务。 运行命令 start nginx.exe 然后访问 http://localhost/item.html?id10001即可 4.2.反向代理 现在页面是假数据展示的。我们需要向服务器发送ajax请求查询商品数据。 打开控制台可以看到页面有发起ajax查询数据 而这个请求地址同样是80端口所以被当前的nginx反向代理了。 查看nginx的conf目录下的nginx.conf文件 其中的关键配置如下 其中的192.168.150.101是我的虚拟机IP也就是我的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;upstream nginx-cluster{server 192.168.150.101: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;}}
} 3初识Caffeine就是在springboot学过的注解方式的cache。
这里是使用代码方式写的。 使用案例 public class CaffeineTest {/*基本用法测试*/Testvoid testBasicOps() throws UnsupportedEncodingException {// 创建缓存对象CacheString, String cache Caffeine.newBuilder().build();// 存数据
// cache.put(gf, aaa);// 取数据不存在则返回nullString gf cache.getIfPresent(gf);System.out.println(gf gf);// 取数据不存在则去数据库查询String defaultGF cache.get(defaultGF, key - {// 这里可以去数据库根据 key查询valuereturn lll;});System.out.println(defaultGF defaultGF);/*** 输出结果为* gf null* defaultGF lll*/}/*基于大小设置驱逐策略*/Testvoid testEvictByNum() throws InterruptedException {// 创建缓存对象CacheString, String cache Caffeine.newBuilder()// 设置缓存大小上限为 1.maximumSize(1).build();// 存数据cache.put(gf1, 柳岩);cache.put(gf2, 范冰冰);cache.put(gf3, 迪丽热巴);// 延迟10ms给清理线程一点时间
// Thread.sleep(10L);//打印三个都有数据因为还没来得及清理偶尔也是清理掉的即前两个为null。打开这个后前两个为null最后一个有数据// 获取数据System.out.println(gf1: cache.getIfPresent(gf1));//gf1: nullSystem.out.println(gf2: cache.getIfPresent(gf2));//gf2: nullSystem.out.println(gf3: cache.getIfPresent(gf3));//gf3: 迪丽热巴}/*基于时间设置驱逐策略*/Testvoid testEvictByTime() throws InterruptedException {// 创建缓存对象CacheString, String cache Caffeine.newBuilder().expireAfterWrite(Duration.ofSeconds(1)) // 设置缓存有效期为 10 秒.build();// 存数据cache.put(gf, 柳岩);// 获取数据System.out.println(gf: cache.getIfPresent(gf));//gf: 柳岩// 休眠一会儿Thread.sleep(1200L);System.out.println(gf: cache.getIfPresent(gf));//gf: null}
} 4实现进程缓存。 加载Cache成为Bean Configuration
public class CaffeineConfig {Beanpublic CacheLong, Item itemCache(){return Caffeine.newBuilder().initialCapacity(100)//初始化100个key容量.maximumSize(10_000)//上限是10000个key容量.build();}Beanpublic CacheLong, ItemStock itemStockCache(){return Caffeine.newBuilder().initialCapacity(100)//初始化100个key容量.maximumSize(10_000)//上限是10000个key容量.build();}
} 使用Caffeine缓存 RestController
RequestMapping(item)
public class ItemController {Autowiredprivate IItemService itemService;Autowiredprivate IItemStockService stockService;Autowiredprivate CacheLong,Item itemCache;Autowiredprivate CacheLong,ItemStock stockCache;......省略GetMapping(/{id})public Item findById(PathVariable(id) Long id){//itemCache.get()方法的第二个参数是一个lambda表达式它接受一个类型为Long的键即id然后返回一个类型为Item的值。return itemCache.get(id,key - itemService.query().ne(status, 3).eq(id, key).one());}GetMapping(/stock/{id})public ItemStock findStockById(PathVariable(id) Long id){return stockCache.get(id,key - stockService.getById(id));}
}我们这里实现的就是Tomcatjava里面的进程缓存 三、Lua语法入门。
1初识Lua。 CentOS中自带Lua环境。 Lua 是一种轻量小巧的脚本语言用标准C语言编写并以源代码形式开放 其设计目的是为了嵌入应用程序中从而为应用程序提供灵活的扩展和定制功能。官网https://www.lua.org/ 简单写一个lua脚本 2数据类型、变量和循环。 lua中字符串拼接是使用..连接的如local str hello .. world #打印出来是helloworld 3函数、条件控制。 四、多级缓存。
1安装OpenResty。 官方网站 https://openresty.org/cn/ 1.安装 首先你的Linux虚拟机必须联网 1安装开发库 首先要安装OpenResty的依赖开发库执行命令 yum install -y pcre-devel openssl-devel gcc --skip-broken2安装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 openresty4安装opm工具 opm是OpenResty的一个管理工具可以帮助我们安装一个第三方的Lua模块。 如果你想安装命令行工具 opm那么可以像下面这样安装 openresty-opm 包 yum install -y openresty-opm5目录结构 默认情况下OpenResty安装的目录是/usr/local/openresty 看到里面的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/profile2.启动和运行 OpenResty底层是基于Nginx的查看OpenResty目录的nginx目录结构与windows中安装的nginx基本一致 所以运行方式与nginx基本一致 # 启动nginx
nginx
# 重新加载配置
nginx -s reload
# 停止
nginx -s stopnginx的默认配置文件注释太多影响后续我们的编辑这里将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地址替换为你自己的虚拟机IP3.备注 下面的这些是OpenResty快速入门时要使用的东西。 加载OpenResty的lua模块 #lua 模块
lua_package_path /usr/local/openresty/lualib/?.lua;;;
#c模块
lua_package_cpath /usr/local/openresty/lualib/?.so;;; 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-- 记录错误信息返回404ngx.log(ngx.ERR, http not found, path: , path , , args: , args)ngx.exit(404)endreturn resp.body
end
-- 将方法导出
local _M { read_http read_http
}
return _M 释放Redis连接API -- 关闭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数据的API -- 查询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 开启共享词典 # 共享字典也就是本地缓存名称叫做item_cache大小150m
lua_shared_dict item_cache 150m; 2OpenResty快速入门。 1.该展示的是windows下的nginx的反向代理服务器的nginx.conf文件。 2.该展示的是linux下的openResty里的nginx的nginx.conf文件。 这个是添加到openResty中的nginx的nginx.conf里面的html标签中。 lua_package_path /usr/local/openresty/lualib/?.lua;;; 表示在lualib目录下以lua后缀名的模块文件都加载进来。 lua_package_cpath /usr/local/openresty/lualib/?.so;;; 表示在lualib目录下以so后缀名的模块文件都加载进来。 3.编写item.lua文件。 注意写好文件后linux的openResty中的nginx要重新加载windows中的nginx也要重新加载否则的话是访问失败还是原来的样子没有变化的。 3请求参数处理。 ~波浪线表示后面跟着正则表达式匹配。 案例 修改openResty中的nginx的nginx.conf文件。 修改openResty中的nginx目录下的lua目录下的item.lua文件。 都改完后执行nginx -s reload重新加载然后访问。 4查询Tomcat。 适用于所有虚拟机连接windows系统的便捷方式虚拟机的IP地址前三个数字不变第四个数字替换为1、就一定能得到wdows地址。前提是windows防火墙关闭 例如 虚拟机IP地址192.168.203.129 连接windows系统使用192.168.203.1 lua文件的语句结束不用“”但我发现使用了“”也没有报错要使用英文分号。 将函数导出意思就是加载这个模块类似java中的导包的文件可以使用该函数。这里的发送请求会被反向代理拦截然后发到指定IP地址。 5Tomcat集群的负载均衡。 在Nginx中使用 hash $request_uri; 可以实现基于请求URI的负载均衡策略。这个策略会根据请求的URI对后端服务器进行哈希计算并将同一URI的请求始终分发到同一台后端服务器上。$ 符号表示引用变量的开始。在这种上下文中$request_uri代表了请求的URI变量。 计算请求路径的哈希值根据哈希值取余tomcat服务器数量保证同一个请求路径只会发给同一个tomcat处理保证进程缓存的可用性。 操作如下 6Redis缓存预热。 初始化redis缓存 Component
public class RedisHandler implements InitializingBean {Autowiredprivate StringRedisTemplate redisTemplate;Autowiredprivate IItemService itemService;Autowiredprivate IItemStockService stockService;Autowiredprivate static final ObjectMapper MAPPER new ObjectMapper();Overridepublic void afterPropertiesSet() throws Exception {//初始化缓存//1.查询商品信息ListItem 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.查询库存信息ListItemStock 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);}}
} 7查询Redis缓存。 common.lua文件 -- 引入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 not found, path: , path , , args: , args)ngx.exit(404)endreturn resp.body
end
-- 将方法导出
local _M { read_http read_http,read_redis read_redis
}
return _Mitem.lua文件 -- 导入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)-- 查询redislocal resp read_redis(127.0.0.1,6379,key)-- 判断查询结果if not resp thenngx.log(ngx.ERR,redis查询失败,尝试查询http,key:,key)-- redis查询失败resp 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)) 改完文件后保存并查询加载nginx。 注意如果没有其效果那就查lua后缀名的文件内容是否有写错。我都是因为写错导致没有效果可以查nginx日志一般会告诉你因何错位 8Nginx本地缓存。 在 Nginx 中worker 是指工作进程worker process。Nginx 的主进程负责管理整个服务器而工作进程则负责处理实际的客户端请求。每个工作进程相互独立它们可以同时处理多个客户端连接和请求。 linux的openResty里的nginx的nginx.conf文件 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
-- 88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
-- 封装查询函数
function read_data(key,expire,path,params)-- 查询本地缓存local val item_cache:get(key) if not val thenngx.log(ngx.ERR,本地缓存查询失败,尝试查询redis,key:,key)-- 查询redisval read_redis(127.0.0.1,6379,key)-- 判断查询结果if not val thenngx.log(ngx.ERR,redis查询失败,尝试查询http,key:,key)-- redis查询失败val read_http(path,params)endend-- 查询成功,把数据写入本地缓存item_cache:set(key,val,expire)-- 返回数据return val
end
-- 888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
--获取路径参数
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)) 五、缓存同步策略。
1数据同步策略。 使用MQ还是有一些代码侵入。 我们使用下面这种下面这种代码侵入更少。 2安装Canal。
2.1初识Canal。 2.2安装和配置Canal。 下面我们就开启mysql的主从同步机制让Canal来模拟salve 1.开启MySQL主从 Canal是基于MySQL的主从同步功能因此必须先开启MySQL的主从功能才可以。 这里以之前用Docker运行的mysql为例 1.1.开启binlog 打开mysql容器挂载的日志文件我的在/tmp/mysql/conf目录: 这里是因为创建mysql容器的时候已经把mysql容器目录挂载到主机了所以可以直接在主机修改对应文件。 修改文件 vi /tmp/mysql/conf/my.cnf 添加内容 log-bin/var/lib/mysql/mysql-bin
binlog-do-dbheima 配置解读 log-bin/var/lib/mysql/mysql-bin设置binary log文件的存放地址和文件名叫做mysql-bin binlog-do-dbheima指定对哪个database记录binary log events这里记录heima这个库 最终效果 [mysqld]
skip-name-resolve
character_set_serverutf8
datadir/var/lib/mysql
server-id1000
log-bin/var/lib/mysql/mysql-bin
binlog-do-dbheima在配置文件中[mysqld]是一个段section的名称表示 MySQL 服务器的配置部分。然后重启mysql容器 1.2.设置用户权限 接下来添加一个仅用于数据同步的账户出于安全考虑这里仅提供对heima这个库的操作权限。这里是在mysql里面执行使用mysql客户端登录执行即可 create user canal% IDENTIFIED by canal;
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT,SUPER ON *.* TO canal% identified by canal;
FLUSH PRIVILEGES;重启mysql容器即可 docker restart mysql测试设置是否成功在mysql控制台或者Navicat中输入命令 show master status; 2.安装Canal 2.1.创建网络 我们需要创建一个网络将MySQL、Canal、MQ放到同一个Docker网络中 docker network create heima 让mysql加入这个网络 docker network connect heima mysql2.3.安装Canal 课前资料中提供了canal的镜像压缩包: 大家可以上传到虚拟机然后通过命令导入 docker load -i canal.tar然后运行命令创建Canal容器 在docker中容器在同一个网络中可以使用容器名连接。 docker run -p 11111:11111 --name canal \
-e canal.destinationsheima \
-e canal.instance.master.addressmymysql:3306 \
-e canal.instance.dbUsernamecanal \
-e canal.instance.dbPasswordcanal \
-e canal.instance.connectionCharsetUTF-8 \
-e canal.instance.tsdb.enabletrue \
-e canal.instance.gtidonfalse \
-e canal.instance.filter.regexheima\\..* \
--network heima \
-d canal/canal-server:v1.1.5说明: -p 11111:11111这是canal的默认监听端口 -e canal.instance.master.addressmysql:3306数据库地址和端口如果不知道mysql容器地址可以通过docker inspect 容器id来查看 -e canal.instance.dbUsernamecanal数据库用户名 -e canal.instance.dbPasswordcanal 数据库密码 -e canal.instance.filter.regex要监听的表名称 表名称监听支持的语法 mysql 数据解析关注的表Perl正则表达式.
多个正则之间以逗号(,)分隔转义符需要双斜杠(\\)
常见例子
1. 所有表.* or .*\\..*
2. canal schema下所有表 canal\\..*
3. canal下的以canal打头的表canal\\.canal.*
4. canal schema下的一张表canal.test1
5. 多个规则组合使用然后以逗号隔开canal\\..*,mysql.test1,mysql.test2 3监听Canal。 Canal框架 概念: canal是用java开发的基于数据库增量日志解析,提供增量数据订阅消费的中间件。目前,canal主要支持了MySQL的binlog解析,解析完成后才利用canal client 用来处理获得的相关数据。 Canal 是阿里巴巴开源的数据库变更数据抓取和同步框架用于监听数据库的变更并将这些变更事件传输到消息中间件或者其他存储介质中。 RedisHandler implements InitializingBean类 Component
public class RedisHandler implements InitializingBean {Autowiredprivate StringRedisTemplate redisTemplate;Autowiredprivate IItemService itemService;Autowiredprivate IItemStockService stockService;Autowiredprivate static final ObjectMapper MAPPER new ObjectMapper();Overridepublic void afterPropertiesSet() throws Exception {//初始化缓存//1.查询商品信息ListItem 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.查询库存信息ListItemStock 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);}}public void saveItem(Item item) {try {//1 item序列化为jsonString json MAPPER.writeValueAsString(item);//2 存入redisredisTemplate.opsForValue().set(item:id:item.getId(),json);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}public void deleteItemById(Long id){redisTemplate.delete(item:id:id);}
} ItemHandler implements EntryHandlerItem类 CanalTable(tb_item)
Component
public class ItemHandler implements EntryHandlerItem {Autowiredprivate RedisHandler redisHandler;Autowiredprivate CacheLong, Item itemCache;Overridepublic void insert(Item item) {//写数据到jvm进程缓存itemCache.put(item.getId(),item);//写数据到redisredisHandler.saveItem(item);}Overridepublic void update(Item before, Item after) {//修改数据到jvm进程缓存itemCache.put(after.getId(),after);//修改数据到redisredisHandler.saveItem(after);}Overridepublic void delete(Item item) {//删除数据到jvm进程缓存itemCache.invalidate(item.getId());//删除数据到redisredisHandler.deleteItemById(item.getId());}
} 六、多级缓存总结。