canal是阿里巴巴的开源组件,用于监听MySQL的binlog日志而实现消息的同步机制,提供增量数据订阅和消费。
canal必须基于MySQL的主从架构才可使用,canal会伪装成MySQL的一个slave节点,然后向MySQL的master发起dump命令,master会将数据库的增量操作写入binlog,然后将binlog通过网络IO发送给canal-server,canal-server解析后将相关信息发送给订阅了该canal-server的客户端。
本次项目中整合canal用于实现缓存的异步更新。
首先拉去canal的镜像(我采用用最新的1.1.5):
docker pull canal/canal-server:v1.1.5
查看docker镜像是否拉取成功:
[root@iZ2vc97qcawcm6uqblda2fZ ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 8.0 33037edcac9b 47 hours ago 444MB
hello-world latest feb5d9fea6a5 9 months ago 13.3kB
canal/canal-server v1.1.5 0c7f1d62a7d8 15 months ago 874MB
[root@iZ2vc97qcawcm6uqblda2fZ ~]#
启动canal镜像(启动时添加相关的参数):
docker run -p 11111:11111 --name canal \
-e canal.auto.scan=false \
-e canal.destinations=recl \
-e canal.instance.master.address=47.108.105.42:3306 \
-e canal.instance.dbUsername=canal \
-e canal.instance.dbPassword=canal \
-e canal.instance.connectionCharset=UTF-8 \
-e canal.instance.tsdb.enable=true \
-e canal.instance.gtidon=false \
-e canal.instance.filter.regex=recl\\..* \
--network recl \
-d canal/canal-server:v1.1.5
这里解释一下重要参数:
canal.destinations代表客户端需要订阅的主题,只有订阅该主题,才可以接受canal的消息
canal.instance.master.address代表MySQL的master的地址
canal.instance.dbUserName代表MySQL中的对应用户名(在MySQL主库中需要创建一个canal用户并分配相关权限)
canal.instance.dbPassword代表canal用户的密码
canal.instance.filer.regex代表通过正则表达式过滤不需要监听的数据库
查看canal-server是否成功启动(已成功启动):
[root@iZ2vc97qcawcm6uqblda2fZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
893d281152af canal/canal-server:v1.1.5 "/alidata/bin/main.s…" 3 hours ago Up 3 hours 9100/tcp, 11110/tcp, 11112/tcp, 0.0.0.0:11111->11111/tcp, :::11111->11111/tcp canal
05ec74877d7d mysql:8.0 "docker-entrypoint.s…" 16 hours ago Up 4 hours 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp mysql01
[root@iZ2vc97qcawcm6uqblda2fZ ~]#
接下来开始配置MySQL:
首先更改MySQL的my.cnf文件:
打开my.conf添加配置信息:
log-bin=mysql-bin
# 需要生成binlog日志的数据库名称(我的是recl)
binlog-do-db=recl
# binlog的语句类型
binlog-format=ROW
# 配置 MySQL replaction 需要定义,不要和 canal 的 slaveId 重复
server_id=1
重启MySQL使配置生效,然后检查是否生效(通过show master status;指令查看,下图显示已生效):
创建canal用户并授权:
CREATE USER canal IDENTIFIED BY 'canal';
GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;
FLUSH PRIVILEGES;
show grants for 'canal'@'%%';
接下来是springboot整合canal:
首先引入canal的starter依赖:
<!-- canal -->
<dependency>
<groupId>top.javatool</groupId>
<artifactId>canal-spring-boot-starter</artifactId>
<version>1.2.1-RELEASE</version>
</dependency>
application.properties里添加配置(destination为主题): ?
# Canal配置
canal.destination=recl
canal.server=47.108.105.42:11111
创建实体类实现canal的消息监听:
package com.ren.fm.canal;
import com.alibaba.fastjson.JSON;
import com.ren.fm.entity.ReclFm;
import org.springframework.stereotype.Component;
import top.javatool.canal.client.annotation.CanalTable;
import top.javatool.canal.client.handler.EntryHandler;
/**
* @ClassName: CanalHandler
* @Description: TODO
* @Author: RZY
* @DATE: 2022/7/14 23:14
* @Version: v1.0
*/
@Component
@CanalTable("recl_fm")
public class ReclFmCanalHandler implements EntryHandler<ReclFm> {
@Override
public void insert(ReclFm reclFm) {
System.out.println("insert:" + reclFm);
//以下为缓存的更新操作
}
@Override
public void update(ReclFm before, ReclFm after) {
System.out.println("before:" + JSON.toJSONString(before));
System.out.println("after:" + JSON.toJSONString(after));
//以下为缓存的更新操作
}
@Override
public void delete(ReclFm reclFm) {
System.out.println("delete:" + reclFm);
//以下为缓存的更新操作
}
}
ReclFm为实体类(@Column声音字段别名,因为可以存在驼峰转换问题;此为如果存在数据库以下字段可以加上@Transient):
package com.ren.fm.entity;
import com.alibaba.fastjson.annotation.JSONField;
import com.baomidou.mybatisplus.annotation.*;
import java.util.Date;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.springframework.data.annotation.Id;
import javax.persistence.Column;
/**
* <p>
*
* </p>
*
* @author RZY
* @since 2022-07-13
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="ReclFm对象", description="")
@TableName("recl_fm")
public class ReclFm implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "fm唯一id")
@TableId(value = "id", type = IdType.ID_WORKER)
@Id
private Long id;
@ApiModelProperty(value = "上传用户id")
@Column(name = "upload_user_id")
private Long uploadUserId;
@ApiModelProperty(value = "fm音频对应oss地址")
@Column(name = "fm_url")
private String fmUrl;
@ApiModelProperty(value = "fm所属心情")
@Column(name = "fm_mood")
private String fmMood;
@ApiModelProperty(value = "fm作者签名")
@Column(name = "fm_sign")
private String fmSign;
@ApiModelProperty(value = "fm标题")
@Column(name = "fm_title")
private String fmTitle;
@ApiModelProperty(value = "fm封面图片对应oss地址")
@Column(name = "fm_cover")
private String fmCover;
@ApiModelProperty(value = "fm收藏量")
@Column(name = "fm_collection_count")
private Long fmCollectionCount;
@ApiModelProperty(value = "fm浏览量")
@Column(name = "fm_view_count")
private Long fmViewCount;
@ApiModelProperty(value = "fm评论量")
@Column(name = "fm_comment_count")
private Long fmCommentCount;
@ApiModelProperty(value = "fm点赞量")
@Column(name = "fm_praise_count")
private Long fmPraiseCount;
@ApiModelProperty(value = "当前状态(‘Draft’代表审核中, ’remove‘代表下架, ‘Modify’代表内容不符需修改, ‘Verify’代表已上架)")
@Column(name = "status")
private String status;
@ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除")
@Column(name = "isDeleted")
@TableLogic
private Boolean isDeleted;
@ApiModelProperty(value = "创建时间")
@Column(name = "gmt_create")
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
@ApiModelProperty(value = "更新时间")
@Column(name = "gmt_modified")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
}
最后进行测试阶段:
2022-07-15 13:54:21.533 INFO 9580 --- [l-client-thread] t.j.c.client.client.AbstractCanalClient : 获取消息 Message[id=31,entries=[header {
version: 1
logfileName: "mysql-bin.000003"
logfileOffset: 18599
serverId: 1
serverenCode: "UTF-8"
executeTime: 1657864460000
sourceType: MYSQL
schemaName: ""
tableName: ""
eventLength: 84
}
entryType: TRANSACTIONBEGIN
storeValue: " \341\003"
, header {
version: 1
logfileName: "mysql-bin.000003"
logfileOffset: 18768
serverId: 1
serverenCode: "UTF-8"
executeTime: 1657864460000
sourceType: MYSQL
schemaName: "recl"
tableName: "recl_fm"
eventLength: 772
eventType: UPDATE
props {
key: "rowsCount"
value: "1"
}
}
entryType: ROWDATA
storeValue: "\bV\020\002P\000b\312\017\n4\b\000\020\373\377\377\377\377\377\377\377\377\001\032\002id \001(\0000\000B\0231444208758042406913R\006bigint\n@\b\001\020\373\377\377\377\377\377\377\377\377\001\032\016upload_user_id \000(\0000\000B\0231502211201646481410R\006bigint\n\240\001\b\002\020\f\032\006fm_url \000(\0000\000B~https://recl-edu.oss-cn-beijing.aliyuncs.com/recl-music/d99df82714894f08bb9a18c258731b35Alec Benjamin - Let Me Down Slowly.mp3R\fvarchar(255)\n*\b\003\020\f\032\afm_mood \000(\0000\000B\b\346\202\262\344\274\24411R\vvarchar(20)\nU\b\004\020\f\032\afm_sign \000(\0000\000B3\345\206\263\345\256\232\344\270\244\344\270\252\344\272\272\350\203\275\345\220\246\345\234\250\344\270\200\350\265\267\347\232\204\345\233\240\347\264\240\346\234\211\345\276\210\345\244\232\343\200\202R\vvarchar(50)\n.\b\005\020\f\032\bfm_title \000(\0000\000B\vlet me downR\vvarchar(20)\n\204\001\b\006\020\f\032\bfm_cover \000(\0000\000B`https://recl-edu.oss-cn-beijing.aliyuncs.com/2021/10/02/316de4ea0907483aa6de4ea0446b4c5afile.pngR\fvarchar(255)\n3\b\a\020\373\377\377\377\377\377\377\377\377\001\032\023fm_collection_count \000(\0000\000B\0010R\006bigint\n-\b\b\020\373\377\377\377\377\377\377\377\377\001\032\rfm_view_count \000(\0000\000B\0010R\006bigint\n0\b\t\020\373\377\377\377\377\377\377\377\377\001\032\020fm_comment_count \000(\0000\000B\0010R\006bigint\n/\b\n\020\373\377\377\377\377\377\377\377\377\001\032\017fm_praise_count \000(\0000\000B\0010R\006bigint\n\'\b\v\020\f\032\006status \000(\0000\000B\006VerifyR\vvarchar(10)\n.\b\f\020\372\377\377\377\377\377\377\377\377\001\032\nis_deleted \000(\0000\000B\0010R\ntinyint(1)\n5\b\r\020]\032\ngmt_create \000(\0000\000B\0232022-07-13 12:46:24R\bdatetime\n7\b\016\020]\032\fgmt_modified \000(\0000\000B\0232022-07-13 12:46:26R\bdatetime\0224\b\000\020\373\377\377\377\377\377\377\377\377\001\032\002id \001(\0000\000B\0231444208758042406913R\006bigint\022@\b\001\020\373\377\377\377\377\377\377\377\377\001\032\016upload_user_id \000(\0000\000B\0231502211201646481410R\006bigint\022\240\001\b\002\020\f\032\006fm_url \000(\0000\000B~https://recl-edu.oss-cn-beijing.aliyuncs.com/recl-music/d99df82714894f08bb9a18c258731b35Alec Benjamin - Let Me Down Slowly.mp3R\fvarchar(255)\022*\b\003\020\f\032\afm_mood \000(\0010\000B\b\346\202\262\344\274\24499R\vvarchar(20)\022U\b\004\020\f\032\afm_sign \000(\0000\000B3\345\206\263\345\256\232\344\270\244\344\270\252\344\272\272\350\203\275\345\220\246\345\234\250\344\270\200\350\265\267\347\232\204\345\233\240\347\264\240\346\234\211\345\276\210\345\244\232\343\200\202R\vvarchar(50)\022.\b\005\020\f\032\bfm_title \000(\0000\000B\vlet me downR\vvarchar(20)\022\204\001\b\006\020\f\032\bfm_cover \000(\0000\000B`https://recl-edu.oss-cn-beijing.aliyuncs.com/2021/10/02/316de4ea0907483aa6de4ea0446b4c5afile.pngR\fvarchar(255)\0223\b\a\020\373\377\377\377\377\377\377\377\377\001\032\023fm_collection_count \000(\0000\000B\0010R\006bigint\022-\b\b\020\373\377\377\377\377\377\377\377\377\001\032\rfm_view_count \000(\0000\000B\0010R\006bigint\0220\b\t\020\373\377\377\377\377\377\377\377\377\001\032\020fm_comment_count \000(\0000\000B\0010R\006bigint\022/\b\n\020\373\377\377\377\377\377\377\377\377\001\032\017fm_praise_count \000(\0000\000B\0010R\006bigint\022\'\b\v\020\f\032\006status \000(\0000\000B\006VerifyR\vvarchar(10)\022.\b\f\020\372\377\377\377\377\377\377\377\377\001\032\nis_deleted \000(\0000\000B\0010R\ntinyint(1)\0225\b\r\020]\032\ngmt_create \000(\0000\000B\0232022-07-13 12:46:24R\bdatetime\0227\b\016\020]\032\fgmt_modified \000(\0000\000B\0232022-07-13 12:46:26R\bdatetime"
],raw=true,rawEntries=[]]
before:{"fmMood":"悲伤11"}
after:{"fmCollectionCount":0,"fmCommentCount":0,"fmCover":"https://recl-edu.oss-cn-beijing.aliyuncs.com/2021/10/02/316de4ea0907483aa6de4ea0446b4c5afile.png","fmMood":"悲伤99","fmPraiseCount":0,"fmSign":"决定两个人能否在一起的因素有很多。","fmTitle":"let me down","fmUrl":"https://recl-edu.oss-cn-beijing.aliyuncs.com/recl
成功的拿到了消息,说明配置成功了
补充:由于MySQL版本差异,5.6及以上版本的密码验证改变为mysql_native_password,因此canal可以会出现密码验证问题,在MySQL中执行以下命令即可:
ALTER USER 'canal'@'%' IDENTIFIED WITH mysql_native_password BY 'canal';
|