使用canal+rabbitmq实现数据实时同步

使用canal+rabbitmq实现数据实时同步

本文使用canal加rabbtmq实现同步交易数据,这里只贴出客户端实现代码,canal服务端部署篇后期会更新

package com.canal;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.scheduling.annotation.EnableScheduling;


/**
 * @description:
 * @author: kevin
 * @time: 2020/12/10 18:23
 */
@SpringBootApplication
@EnableDiscoveryClient
@EnableScheduling
public class CanalApp {

    public static void main(String[] args) {
        SpringApplication.run(com.canal.CanalApp.class, args);
    }
    }


这里采用的canal版本1.1.4,版本要与canal服务端采用一致


     <dependency>
            <groupId>com.alibaba.otter</groupId>
            <artifactId>canal.client</artifactId>
            <version>1.1.4</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.otter</groupId>
            <artifactId>canal.common</artifactId>
            <version>1.1.4</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.otter</groupId>
            <artifactId>canal.protocol</artifactId>
            <version>1.1.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

canal客户端连接服务端代码,这里业务需求是只需要订阅channel库中的两张表,那么同步数据只会对订阅的两张表的binlog日志做同步


package com.canal.config;
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.client.CanalConnectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.net.InetSocketAddress;

/**
 * @description:
 * @author: kevin
 * @time: 2020/12/11 17:51
 */
@Component
@Slf4j
public class CanalConfig implements DisposableBean {

    private CanalConnector canalConnector;


    @Bean
    public CanalConnector getCanalConnector() {
       //这个是单机版本写法
       // canalConnector = CanalConnectors.newSingleConnector(new //InetSocketAddress("8.129.108.000",
//                11111), "aaa", "canal", "canal");
      // 这个是集群连zk版本
        canalConnector = CanalConnectors.newClusterConnector("ip:2181,ip:2181,ip:2181", "aaa", "canal", "canal");

        try {
            canalConnector.connect();
            canalConnector.subscribe("canal\\\\..*,channel.channel_deposit_order,channel.pos_channel_order");
        } catch (Throwable t) {
            log.error("failed to connect to canal server", t);
            canalConnector.disconnect();
            throw t;
        }

        canalConnector.rollback();
        log.info("canal connect success");
        return canalConnector;
    }

    @Override
    public void destroy() {
        if (canalConnector != null) {
            canalConnector.disconnect();
        }
    }


}

canal客户端定时从canal服务端的mq中获取binlog日志信息(包含insert,update),提取表字段数据,并对数据做清洗放入rabbitmq

package com.canal.config;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.otter.canal.client.CanalConnector;
import com.alibaba.otter.canal.protocol.CanalEntry;
import com.alibaba.otter.canal.protocol.Message;
import com.google.protobuf.InvalidProtocolBufferException;
import com.sdmgr.canal.constant.Constant;
import com.sdmgr.canal.mq.Produce;
import com.sdmgr.common.core.exception.GlobalException;
import com.sdmgr.common.core.result.ResultEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @description:
 * @author: kevin
 * @time: 2020/12/11 17:47
 */
@Component
@Slf4j
public class CanalJob {


    @Autowired
    private CanalConnector canalConnector;

    @Autowired
    private Produce produce;


    @Scheduled(fixedDelay = 1000)
    public void handle() {

        long batchId = -1;
        try {
            int batchSize = 1000;
            Message message = canalConnector.getWithoutAck(batchSize);
            batchId = message.getId();
            List<CanalEntry.Entry> entries = message.getEntries();
            if (batchId != -1 && entries.size() > 0) {
                entries.forEach(entry -> {
                    if (entry.getEntryType() == CanalEntry.EntryType.ROWDATA) {
                        String name = entry.getHeader().getTableName();
                        CanalEntry.RowChange change = null;
                        try {
                            change = CanalEntry.RowChange.parseFrom(entry.getStoreValue());
                        } catch (InvalidProtocolBufferException e) {
                            log.error("parseFrom error ", e);
                            return;
                        }
                        change.getRowDatasList().forEach(rowData -> {
                            List<CanalEntry.Column> columns = rowData.getAfterColumnsList();
                            try {

                                Map<String, Object> jsonMap = new HashMap<>();
                                if (name.equals(Constant.SURFACE_ONE)) {
                                    columns.forEach(column -> {
                                        if (column == null) {
                                            return;
                                        }
                                        if (column.getName().equals("mer_no")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("term_no")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("amount")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("fee")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("tradeStatus")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("create_time")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("finish_time")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("settle_merno")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("settle_term")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("transaction_id")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("trade_type")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("settle_flag")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("d0_flag")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("card_Code")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("sn")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("card_type")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("trans_categary")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("charge_fee")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                        if (column.getName().equals("order_id")) {
                                            jsonMap.put(column.getName(), column.getValue());
                                        }
                                    });
                                    if (CollectionUtil.isEmpty(jsonMap)) {
                                        throw new GlobalException(ResultEnum.DATA_HAVE_EMPTY);
                                    }
                                    produce.sendMessage1(jsonMap);
                                    log.info("发送消息111成功");

                                } else {
                                    columns.forEach(column -> {
                                        if (column == null) {
                                            return;
                                        }
                                        jsonMap.put(column.getName(), column.getValue());
                                    });
                                    if (CollectionUtil.isEmpty(jsonMap)) {
                                        throw new GlobalException(ResultEnum.DATA_HAVE_EMPTY);
                                    }
                                    produce.sendMessage2(jsonMap);
                                    log.info("发送消息222成功");

                                }

                            } catch (Exception e) {
                                log.error("parseFrom error ", e);
                            }

                        });
                    }
                });
            }
            canalConnector.ack(batchId);
        } catch (Exception e) {
            log.error("data parse error ", e);
            canalConnector.rollback(batchId);
        }

    }


}

配置rabbitmq的topic模式配置类

package com.canal.config;
import com.sdmgr.canal.constant.Constant;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

/**
 * @description:
 * @author: kevin
 * @time: 2020/12/10 17:51
 */
@Component
public class TopicConfig {


    @Bean
    public Queue topicOpenQueue() {
        return new Queue(Constant.TOPIC_CANAL_ORDER_QUEUE);
    }

    @Bean
    public Queue topicFlumeQueue() {
        return new Queue(Constant.TOPIC_CANAL_FREZE_QUEUE);
    }


    @Bean
    TopicExchange topicExchange() {
        return new TopicExchange(Constant.EXCHANGE_NAME);
    }


    @Bean
    Binding bindingExchangeOpen(Queue topicOpenQueue, TopicExchange topicExchange) {
        return BindingBuilder.bind(topicOpenQueue).to(topicExchange).with(Constant.CANAL_ROUTING_KEY);

    }

    @Bean
    Binding bindingExchangeFlume(Queue topicFlumeQueue, TopicExchange topicExchange) {
        return BindingBuilder.bind(topicFlumeQueue).to(topicExchange).with(Constant.CANAL_FLUME_ROUTING_KEY);

    }


}

package com.canal.constant;

/**
 * @description:
 * @author: kevin
 * @time: 2020/12/11 15:02
 */
public class Constant {

    public final static String TOPIC_CANAL_ORDER_QUEUE = "sd.order.queue";

    public final static String EXCHANGE_NAME = "sd.order.exchange";

    public final static String CANAL_ROUTING_KEY = "sd.order.trans";

    public final static String TOPIC_CANAL_FREZE_QUEUE = "sd.flume.queue";

    public final static String CANAL_FLUME_ROUTING_KEY = "sd.flume.trans";

    public static String SURFACE_ONE = "pos_channel_order";


}

mq消息生产者的代码,将清洗后的数据放入mq中

package com.canal.mq;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sdmgr.canal.constant.Constant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Map;

/**
 * @description:
 * @author: kevin
 * @time: 2020/12/11 16:30
 */
@Component
@Slf4j
public class Produce {


    @Autowired
    private AmqpTemplate amqpTemplate;



    /**
     * @description: 发送交易订单
     */
    public void sendMessage1(Map<String,Object> data) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        String result = objectMapper.writeValueAsString(data);
        amqpTemplate.convertAndSend(Constant.EXCHANGE_NAME, Constant.CANAL_ROUTING_KEY, result);
        log.info("发送订单成功 {}",result);

    }


    /**
     * @description: 发送附加订单
     */
    public void sendMessage2(Map<String,Object> data) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        String result = objectMapper.writeValueAsString(data);
        amqpTemplate.convertAndSend(Constant.EXCHANGE_NAME, Constant.CANAL_FLUME_ROUTING_KEY, result);
        log.info("发送附加订单成功:{}",result);
    }

}

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值