canal+mq将数据同步到redis中的一些类型转换问题

在将 Canal 捕获到的数据库变更同步到 RabbitMQ 时,通常需要将变更事件的数据从 Java 对象转换为一种通用的数据格式,如 JSON。这样可以确保数据在不同系统之间传递时的兼容性。以下是将 Canal 数据同步到 RabbitMQ 并进行数据类型转换的示例代码。

1. Canal 客户端从 MySQL 读取变更

首先,编写 Canal 客户端以从 MySQL 读取数据库变更,并将变更数据转换为 JSON 格式,然后发送到 RabbitMQ。

2. 依赖库

在项目中添加了所需的依赖库,如 Canal 客户端、RabbitMQ 客户端和 JSON 处理库(例如 Jackson)。

3. Canal 客户端发送数据到 RabbitMQ

编写 Canal 客户端,读取 MySQL 数据库变更并将其发送到 RabbitMQ:

java 复制代码
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.alibaba.otter.canal.protocol.Message;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;

public class CanalToRabbitMQ {
    private final static String QUEUE_NAME = "db_changes";
    private final static ObjectMapper objectMapper = new ObjectMapper();

    public static void main(String[] args) {
        CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("127.0.0.1", 11111), "example", "", "");

        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");

        try (Connection connection = factory.newConnection(); Channel channel = connection.createChannel()) {
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);

            connector.connect();
            connector.subscribe("mydatabase.mytable");
            connector.rollback();

            while (true) {
                Message message = connector.getWithoutAck(100); // 获取指定数量的数据
                long batchId = message.getId();
                int size = message.getEntries().size();
                if (batchId != -1 && size > 0) {
                    for (CanalEntry.Entry entry : message.getEntries()) {
                        if (entry.getEntryType() == CanalEntry.EntryType.ROWDATA) {
                            CanalEntry.RowChange rowChange = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
                            for (CanalEntry.RowData rowData : rowChange.getRowDatasList()) {
                                Map<String, Object> dataMap = new HashMap<>();
                                if (rowChange.getEventType() == CanalEntry.EventType.INSERT) {
                                    dataMap.put("eventType", "INSERT");
                                    for (CanalEntry.Column column : rowData.getAfterColumnsList()) {
                                        dataMap.put(column.getName(), column.getValue());
                                    }
                                } else if (rowChange.getEventType() == CanalEntry.EventType.UPDATE) {
                                    dataMap.put("eventType", "UPDATE");
                                    for (CanalEntry.Column column : rowData.getAfterColumnsList()) {
                                        dataMap.put(column.getName(), column.getValue());
                                    }
                                } else if (rowChange.getEventType() == CanalEntry.EventType.DELETE) {
                                    dataMap.put("eventType", "DELETE");
                                    for (CanalEntry.Column column : rowData.getBeforeColumnsList()) {
                                        dataMap.put(column.getName(), column.getValue());
                                    }
                                }
                                
                                // 将数据转换为 JSON 字符串
                                String jsonMessage = objectMapper.writeValueAsString(dataMap);
                                
                                // 将 JSON 字符串发送到 RabbitMQ
                                channel.basicPublish("", QUEUE_NAME, null, jsonMessage.getBytes("UTF-8"));
                            }
                        }
                    }
                }
                connector.ack(batchId); // 提交确认
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            connector.disconnect();
        }
    }
}

4. RabbitMQ 消费者同步到 Redis

编写 RabbitMQ 消费者,从 RabbitMQ 队列中消费消息并同步到 Redis:

复制代码
java 复制代码
import com.rabbitmq.client.*;
import redis.clients.jedis.Jedis;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.TimeoutException;

public class RabbitMQToRedis {
    private final static String QUEUE_NAME = "db_changes";
    private final static ObjectMapper objectMapper = new ObjectMapper();

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        Jedis jedis = new Jedis("127.0.0.1");

        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), StandardCharsets.UTF_8);
            try {
                // 将 JSON 字符串转换为 Map 对象
                Map<String, Object> dataMap = objectMapper.readValue(message, Map.class);

                // 处理数据并同步到 Redis
                String eventType = (String) dataMap.get("eventType");
                if ("INSERT".equals(eventType) || "UPDATE".equals(eventType)) {
                    // 假设主键是 id
                    String id = (String) dataMap.get("id");
                    jedis.set(id, message);
                } else if ("DELETE".equals(eventType)) {
                    String id = (String) dataMap.get("id");
                    jedis.del(id);
                }

                System.out.println("Received and processed: " + message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        };

        channel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> {});

        // 保持程序运行
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try {
                channel.close();
                connection.close();
                jedis.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }));
    }
}

代码解析

  1. Canal 客户端

    • 连接到 Canal Server,订阅指定数据库和表的变更。
    • 从 binlog 中解析变更事件。
    • 将变更事件转换为 JSON 格式。
    • 将 JSON 消息发送到 RabbitMQ。
  2. RabbitMQ 消费者

    • 连接到 RabbitMQ 队列,消费消息。
    • 将 JSON 消息解析为 Map 对象。
    • 根据事件类型(INSERT、UPDATE、DELETE)同步数据到 Redis。

通过这种方式,Canal 可以将数据库变更事件捕获并转换为 JSON 格式,推送到 RabbitMQ,然后由 RabbitMQ 消费者同步到 Redis。这种方法确保了数据在不同系统之间传递时的兼容性和灵活性。

相关推荐
XiYang-DING几秒前
【Spring】Lombok
java·后端·spring
凤山老林2 分钟前
AI辅助编程:Copilot在Java开发中的最佳实践
java·人工智能·copilot
ew452182 分钟前
【Java】Apache POI 终极封装:支持多表格循环、图片插入、日期格式化的Word导出工具类(兼容POI3.17+)
java·word·apache
铁打的阿秀2 分钟前
IDEA启动项目报错: 加载主类 com.seeburger.webedi.system.SystemApplication 时出现 LinkageError
java·ide·intellij-idea
Yeats_Liao3 分钟前
物联网接入层技术剖析(一):从select到epoll
java·linux·后端·物联网·struts
上弦月-编程9 分钟前
Java类与对象:编程核心解密
java·开发语言·jvm
Kapaseker12 分钟前
为什么 Java 的数组需要 new 出来
android·java·kotlin
Dicky-_-zhang17 分钟前
线上故障排查与应急响应实战:从零开始建立你的SRE体系
java·jvm
大大杰哥18 分钟前
从 Volatile 到 ThreadLocal:Java 线程安全机制备忘
java·开发语言·jvm
AI人工智能+电脑小能手24 分钟前
【大白话说Java面试题 第67题】【JVM篇】第27题:生产环境服务器变慢,诊断思路和性能评估谈谈?
java·服务器·jvm·面试