赞
踩
canal是阿里开源的数据同步工具,基于bin log可以将数据库同步到其他各类数据库中,目标数据库支持mysql,postgresql,oracle,redis,MQ,ES等
canal分成服务端deployer和客户端adapter,我们可以部署多个,同时为了方便管理还提供了一个管理端admin
canal的数据同步流程如下图所示
因为目前canal还不能直接通过配置就实现对redis的数据同步,因此我们需要自定义一下canal客户端,通过服务端将数据同步到客户端后,由客户端自定义操作同步到redis
第一步:因为canal是监控MySQL中的binlog日志来完成同步工作,所以我们需要开启binlog日志功能,由于咱们项目用的MySQL8以上的版本,binlog默认开启,所以可以不管。
第二步:通过show variables like ‘%binlog_format%’; 查看当前binlog,值为ROW就可以了
第三步:源数据库创建一个canal账号,并且设置slave
,dump
权限
CREATE USER canal IDENTIFIED BY 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'canal'@'%';
-- GRANT ALL PRIVILEGES ON *.* TO 'canal'@'%' ;
FLUSH PRIVILEGES;
第四步:因为mysql8.0.3后身份检验方式为caching_sha2_password,但canal使用的是mysql_native_password,因此需要设置检验方式(如果该版本之前的可跳过),否则会报错IOException: caching_sha2_password Auth failed
ALTER USER 'canal'@'%' IDENTIFIED WITH mysql_native_password BY 'canal';
select host,user,plugin from mysql.user ;
第五步:在208服务器上拉取canal,输入以下命令并解压到/tmp/canal文件夹下
wget https://github.com/alibaba/canal/releases/download/canal-1.1.6/canal.deployer-1.1.6.tar.gz
进入到canal文件夹,然后进入conf文件夹找到example文件夹,复制example文件夹并重命名为redis
cd conf
# 复制example实例配置
cp -R example redis
然后修改redis文件夹中的instance.properties配置文件
在此之前需要查看一下数据库binlog日志状态信息:
show master status;
然后进行instance.properties配置文件开始配置(说明和需要改动的地方都在图里)
想了解配置中其他配置信息的,可以参考下面的
配置完成后,启动canal客户端并查看日志,没报错就说明成功了
./bin/start.sh
cat logs/redis/redis.log
我在过程中也遇到了错误,如遇到,可以看看下面的解决办法:
第六步:创建canal应用端,配置需要同步到redis的地址(展示本地应用,具体的应用这几天会上传到云效上)
依赖引用:
<dependency>
<groupId>top.javatool</groupId>
<artifactId>canal-spring-boot-starter</artifactId>
<version>1.2.1-RELEASE</version>
</dependency>
配置文件(application.yml):
# 应用名称 spring: application: name: ns-service-support-canal # 数据同步到当前redis中 redis: host: 127.0.0.1 password: 123456 database: 6 # 应用服务 WEB 访问端口 server: port: 8080 # canal服务端地址 canal: # 部署到208上,port默认为11111 server: 192.168.xxx.xxx:11111 # 实例名,与之前复制example之后那个文件夹名称一致 destination: redis # 设置canal消息日志打印级别 logging: level: top.javatool.canal.client: warn
RedisConfig.java(json转换与其他微服务同步)
@Configuration @AllArgsConstructor public class RedisConfig { private RedisConnectionFactory factory; /** * jsonObject转换 * @param connectionFactory * @return */ @Bean @SuppressWarnings(value = { "unchecked", "rawtypes" }) public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class); // 使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(serializer); // Hash的key也采用StringRedisSerializer的序列化方式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(serializer); template.afterPropertiesSet(); return template; } }
关键的来了,需要同步哪张表,就需要创建对应的实体类(与其他微服务基本一致,需要添加的东西是,实体类头部增加@Table注解,标明对应的数据库表名,每个实体类参数添加@Column注解,名称对应数据库表参数),以user表为例
@Data @Table(name = "user") public class User implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty("id") @NsGridField(title = "id") @Column(name="id") private Long userId; @ApiModelProperty("姓名") @NsGridField(title = "姓名") @Column(name="name") private String name; @ApiModelProperty("账号") @NsGridField(title = "账号") @Column(name="username") private String username; @ApiModelProperty("密码") @NsGridField(title = "密码") @Column(name="password") private String password; @ApiModelProperty("昵称") @NsGridField(title = "昵称") @Column(name="nickname") private String nickname; @ApiModelProperty("授权顾问昵称") @NsGridField(title = "授权顾问昵称") @Column(name="g_a_a_c_nickname") private String gAACNickname; @ApiModelProperty("头像地址") @NsGridField(title = "头像地址") @Column(name="avatar_url") private String avatarUrl; @ApiModelProperty("手机") @NsGridField(title = "手机") @Column(name="phone") private String phone; @ApiModelProperty("身份证号") @NsGridField(title = "身份证号") @Column(name="id_card") private String idCard; @ApiModelProperty("邮箱") @NsGridField(title = "邮箱") @Column(name="email") private String email; @ApiModelProperty("性别: 0男;1女;2未知") @NsGridField(title = "性别: 0男;1女;2未知") @Column(name="gender") private Integer gender; @ApiModelProperty("账号状态:0正常;1禁用") @NsGridField(title = "账号状态:0正常;1禁用") @Column(name="account_status") private Integer accountStatus; @ApiModelProperty("账号类型:0普通用户 1平台用户 ") @NsGridField(title = "账号类型:0普通用户 1平台用户 ") @Column(name="account_type") private Integer accountType; @ApiModelProperty("是否实名认证:0否 1是") @NsGridField(title = "是否实名认证:0否 1是") @Column(name="is_approve") private Integer isApprove; @ApiModelProperty("平台组织id") @NsGridField(title = "平台组织id") @Column(name="org_id") private Long orgId; @ApiModelProperty("备注") @NsGridField(title = "备注") @Column(name="remark") private String remark; @ApiModelProperty("乐观锁") @NsGridField(title = "乐观锁") @Column(name="revision") private Integer revision; @ApiModelProperty("排序") @NsGridField(title = "排序") @Column(name="sort_value") private Integer sortValue; @ApiModelProperty("状态") @NsGridField(title = "状态") @Column(name="status") private Boolean status; @ApiModelProperty("组织描述") @NsGridField(title = "组织描述") @Column(name="org_describe") private String orgDescribe; @Override public String toString() { return "User{" + "name='" + name + '\'' + ", username='" + username + '\'' + ", password='" + password + '\'' + ", nickname='" + nickname + '\'' + ", gAACNickname='" + gAACNickname + '\'' + ", avatarUrl='" + avatarUrl + '\'' + ", phone='" + phone + '\'' + ", idCard='" + idCard + '\'' + ", email='" + email + '\'' + ", gender=" + gender + ", accountStatus=" + accountStatus + ", accountType=" + accountType + ", isApprove=" + isApprove + ", orgId=" + orgId + ", remark='" + remark + '\'' + ", revision=" + revision + ", sortValue=" + sortValue + ", status=" + status + ", orgDescribe='" + orgDescribe + '\'' + '}'; } }
canal-spring-boot-starter
包提供了EntryHandler
类用于监控表数据更新,于是我们创建一个EntryHandler
实现类,用于实现redis的增删改
/** * @author csf * @Description * @date 2023/1/30 */ @CanalTable("user") @Component @AllArgsConstructor @Slf4j public class NmsUserHandler implements EntryHandler<User> { private final RedisTemplate<Object,Object> redisTemplate; @Override public void insert(User nmsUser) { log.info("[新增]"+nmsUser.toString()); redisTemplate.opsForValue().set("user:"+nmsUser.getUsername(),nmsUser); Object o = redisTemplate.opsForValue().get("user:ns0"); System.out.println(o); } @Override public void update(User before, User after) { log.info("[更新]"+after.toString()); redisTemplate.opsForValue().set("user:"+after.getUsername(),after); Object o = redisTemplate.opsForValue().get("user:snn7"); System.out.println(o); } @Override public void delete(User nmsUser) { log.info("[删除]"+nmsUser.getUsername()); redisTemplate.delete("user:"+nmsUser.getUsername()); } }
至此,配置完成,启动项目
测试:不管对数据进行增或删改,日志都能实时反馈,并且数据都能实时同步到redis中
日志
redis
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。