通过本文章,可以完成多级缓存架构中的缓存同步。
一、Canal服务
1. mysql添加canal用户 连接在上一次multiCache
项目中运行的mysql
容器,创建canal
用户。
language-sql 1 2 3 4 CREATE USER canal IDENTIFIED BY 'canal'; GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%'; -- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ; FLUSH PRIVILEGES;
2. mysql配置文件 在docker/mysql/conf/my.cnf
添加如下配置
language-txt 1 2 3 4 server-id=1000 log-bin=/var/lib/mysql/mysql-bin binlog-do-db=heima binlog_format=row
3. canal配置文件 添加canal
服务块到docker-compose.yml
,如下
language-yml 1 2 3 4 5 6 7 8 9 10 11 12 13 canal: container_name: canal image: canal/canal-server:v1.1.7 volumes: - ./canal/logs:/home/admin/canal-server/logs - ./canal/conf:/home/admin/canal-server/conf ports: - "11111:11111" depends_on: - mysql networks: multi-cache: ipv4_address: 172.30.3.7
language-bash 1 docker pull canal/canal-server:v1.1.7
任意启动一个canal-server
容器,将里面的/home/admin/canal-server/conf
文件夹复制到宿主机,对应docker/canal/conf
文件夹。 删除此临时容器。
修改docker/canal/conf/canal.properties
如下条目
language-dart 1 2 canal.destinations=example canal.instance.tsdb.enable=true
修改docker/canal/conf/example/instance.properties
如下条目
language-dart 1 2 3 4 5 6 7 canal.instance.master.address=172.30.3.2:3306 canal.instance.dbUsername=canal canal.instance.dbPassword=canal canal.instance.connectionCharset = UTF-8 canal.instance.tsdb.enable=true canal.instance.gtidon=false canal.instance.filter.regex=heima\\..*
二、引入依赖 pom.xml
language-xml 1 2 3 4 5 <dependency> <groupId>top.javatool</groupId> <artifactId>canal-spring-boot-starter</artifactId> <version>1.2.1-RELEASE</version> </dependency>
application.yml
language-yml 1 2 3 canal: destination: example server: 172.30.3.7:11111
三、监听Canal消息 这是canal-spring-boot-starter
官方仓库,含使用文档
新建canal.ItemHandler
类,内容如下
language-java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package com.heima.item.canal; import com.github.benmanes.caffeine.cache.Cache; import com.heima.item.config.RedisHandler; import com.heima.item.pojo.Item; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import top.javatool.canal.client.annotation.CanalTable; import top.javatool.canal.client.handler.EntryHandler; @CanalTable(value = "tb_item") @Component public class ItemHandler implements EntryHandler<Item> { @Autowired private RedisHandler redisHandler; @Autowired private Cache<Long, Item> itemCache; @Override public void insert(Item item) { itemCache.put(item.getId(), item); redisHandler.saveItem(item); } @Override public void update(Item before, Item after) { itemCache.put(after.getId(), after); redisHandler.saveItem(after); } @Override public void delete(Item item) { itemCache.invalidate(item.getId()); redisHandler.deleteItemById(item.getId()); } }
修改pojo.Item
类,如下
language-java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.heima.item.pojo; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Transient; import java.util.Date; @Data @TableName("tb_item") public class Item { @TableId(type = IdType.AUTO) @Id private Long id;//商品id private String name;//商品名称 private String title;//商品标题 private Long price;//价格(分) private String image;//商品图片 private String category;//分类名称 private String brand;//品牌名称 private String spec;//规格 private Integer status;//商品状态 1-正常,2-下架 private Date createTime;//创建时间 private Date updateTime;//更新时间 @TableField(exist = false) @Transient private Integer stock; @TableField(exist = false) @Transient private Integer sold; }
四、运行 到此为止,docker-compose.yml
内容应该如下
language-yml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 version: '3.8' networks: multi-cache: driver: bridge ipam: driver: default config: - subnet: 172.30.3.0/24 services: mysql: container_name: mysql image: mysql:8 volumes: - ./mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf - ./mysql/data:/var/lib/mysql - ./mysql/logs:/logs ports: - "3306:3306" environment: - MYSQL_ROOT_PASSWORD=1009 networks: multi-cache: ipv4_address: 172.30.3.2 nginx: container_name: nginx image: nginx:stable volumes: - ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf - ./nginx/conf/conf.d/default.conf:/etc/nginx/conf.d/default.conf - ./nginx/dist:/usr/share/nginx/dist ports: - "8080:8080" networks: multi-cache: ipv4_address: 172.30.3.3 canal: container_name: canal image: canal/canal-server:v1.1.7 volumes: - ./canal/logs:/home/admin/canal-server/logs - ./canal/conf:/home/admin/canal-server/conf ports: - "11111:11111" depends_on: - mysql networks: multi-cache: ipv4_address: 172.30.3.7 openresty1: container_name: openresty1 image: openresty/openresty:1.21.4.3-3-jammy-amd64 volumes: - ./openresty1/conf/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf - ./openresty1/conf/conf.d/default.conf:/etc/nginx/conf.d/default.conf - ./openresty1/lua:/usr/local/openresty/nginx/lua - ./openresty1/lualib/common.lua:/usr/local/openresty/lualib/common.lua networks: multi-cache: ipv4_address: 172.30.3.11 redis: container_name: redis image: redis:7.2 volumes: - ./redis/redis.conf:/usr/local/etc/redis/redis.conf ports: - "6379:6379" command: [ "redis-server", "/usr/local/etc/redis/redis.conf" ] networks: multi-cache: ipv4_address: 172.30.3.21
删除原来的multiCache
,重新启动各项服务。
language-bash 1 docker-compose -p multi-cache up -d
启动springboot
程序。
五、测试 springboot
不断输入类似如下日志,属于正常监听canal
消息中。
language-txt 1 2 3 09:27:17:175 INFO 1 --- [l-client-thread] t.j.c.client.client.AbstractCanalClient : 获取消息 Message[id=-1,entries=[],raw=false,rawEntries=[]] 09:27:18:177 INFO 1 --- [l-client-thread] t.j.c.client.client.AbstractCanalClient : 获取消息 Message[id=-1,entries=[],raw=false,rawEntries=[]] 09:27:19:178 INFO 1 --- [l-client-thread] t.j.c.client.client.AbstractCanalClient : 获取消息 Message[id=-1,entries=[],raw=false,rawEntries=[]]
访问http://localhost:8081/item/10001
,此时信息为tomcat
查询数据库所得数据,而后存入Caffeine
缓存。 访问http://localhost:8080/item.html?id=10001
,此时信息为Redis
缓存数据。
然后, 访问http://localhost:8081/
来到商品管理页面。
修改id=10001的数据的商品分类
确认后 springboot日志出现类似如下日志
language-txt 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 09:31:29:234 INFO 1 --- [l-client-thread] t.j.c.client.client.AbstractCanalClient : 获取消息 Message[id=1,entries=[header { version: 1 logfileName: "binlog.000007" logfileOffset: 236 serverId: 1 serverenCode: "UTF-8" executeTime: 1705051889000 sourceType: MYSQL schemaName: "" tableName: "" eventLength: 93 } entryType: TRANSACTIONBEGIN storeValue: " \r" , header { version: 1 logfileName: "binlog.000007" logfileOffset: 411 serverId: 1 serverenCode: "UTF-8" executeTime: 1705051889000 sourceType: MYSQL schemaName: "heima" tableName: "tb_item" eventLength: 626 eventType: UPDATE props { key: "rowsCount" value: "1" } } entryType: ROWDATA storeValue: "\bV\020\002P\000b\332\n\n&\b\000\020\373\377\377\377\377\377\377\377\377\001\032\002id \001(\0000\000B\00510001R\006bigint\nd\b\001\020\f\032\005title \000(\0000\000BCRIMOWA 21\345\257\270\346\211\230\350\277\220\347\256\261\346\213\211\346\235\206\347\256\261 SALSA AIR\347\263\273\345\210\227\346\236\234\347\273\277\350\211\262 820.70.36.4R\fvarchar(264)\n)\b\002\020\f\032\004name \000(\0000\000B\tSALSA AIRR\fvarchar(128)\n)\b\003\020\373\377\377\377\377\377\377\377\377\001\032\005price \000(\0000\000B\00516900R\006bigint\n\226\001\b\004\020\f\032\005image \000(\0000\000Buhttps://m.360buyimg.com/mobilecms/s720x720_jfs/t6934/364/1195375010/84676/e9f2c55f/597ece38N0ddcbc77.jpg!q70.jpg.webpR\fvarchar(200)\n0\b\005\020\f\032\bcategory \000(\0000\000B\f\346\213\211\346\235\206\347\256\261777R\fvarchar(200)\n\'\b\006\020\f\032\005brand \000(\0000\000B\006RIMOWAR\fvarchar(100)\nG\b\a\020\f\032\004spec \000(\0000\000B\'{\"\351\242\234\350\211\262\": \"\347\272\242\350\211\262\", \"\345\260\272\347\240\201\": \"26\345\257\270\"}R\fvarchar(200)\n\032\b\b\020\004\032\006status \000(\0000\000B\0011R\003int\n6\b\t\020]\032\vcreate_time \000(\0000\000B\0232019-05-01 00:00:00R\bdatetime\n6\b\n\020]\032\vupdate_time \000(\0000\000B\0232019-05-01 00:00:00R\bdatetime\022&\b\000\020\373\377\377\377\377\377\377\377\377\001\032\002id \001(\0000\000B\00510001R\006bigint\022d\b\001\020\f\032\005title \000(\0000\000BCRIMOWA 21\345\257\270\346\211\230\350\277\220\347\256\261\346\213\211\346\235\206\347\256\261 SALSA AIR\347\263\273\345\210\227\346\236\234\347\273\277\350\211\262 820.70.36.4R\fvarchar(264)\022)\b\002\020\f\032\004name \000(\0000\000B\tSALSA AIRR\fvarchar(128)\022)\b\003\020\373\377\377\377\377\377\377\377\377\001\032\005price \000(\0000\000B\00516900R\006bigint\022\226\001\b\004\020\f\032\005image \000(\0000\000Buhttps://m.360buyimg.com/mobilecms/s720x720_jfs/t6934/364/1195375010/84676/e9f2c55f/597ece38N0ddcbc77.jpg!q70.jpg.webpR\fvarchar(200)\0220\b\005\020\f\032\bcategory \000(\0010\000B\f\346\213\211\346\235\206\347\256\261888R\fvarchar(200)\022\'\b\006\020\f\032\005brand \000(\0000\000B\006RIMOWAR\fvarchar(100)\022G\b\a\020\f\032\004spec \000(\0000\000B\'{\"\351\242\234\350\211\262\": \"\347\272\242\350\211\262\", \"\345\260\272\347\240\201\": \"26\345\257\270\"}R\fvarchar(200)\022\032\b\b\020\004\032\006status \000(\0000\000B\0011R\003int\0226\b\t\020]\032\vcreate_time \000(\0000\000B\0232019-05-01 00:00:00R\bdatetime\0226\b\n\020]\032\vupdate_time \000(\0000\000B\0232019-05-01 00:00:00R\bdatetime" ],raw=false,rawEntries=[]] 09:31:30:572 INFO 1 --- [l-client-thread] t.j.c.client.client.AbstractCanalClient : 获取消息 Message[id=2,entries=[header { version: 1 logfileName: "binlog.000007" logfileOffset: 1037 serverId: 1 serverenCode: "UTF-8" executeTime: 1705051889000 sourceType: MYSQL schemaName: "" tableName: "" eventLength: 31 } entryType: TRANSACTIONEND storeValue: "\022\00287" ],raw=false,rawEntries=[]]
这里可以先用redis
连接工具查询数据,发现rediis
已被更新。
再次访问http://localhost:8081/item/10001
直接向springboot
的controller
发送请求,发现caffeine
数据更新,并且springboot
日志没有出现查询记录,说明走的是caffeine
。