canal针对分库分表场景的高可用架构设计与应用

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: canal针对分库分表场景的高可用架构设计与应用

一 架构设计



网络异常,图片无法展示
|


简单架构设计


说明:

  1. 两个mysql库中均创建有canal/canal的账户;
  2. 这里A、B两个mysql库是用来模拟t_dept进行分库分表;
  3. 另外,在A、B两种表中都创建有表t_canal.
  4. canal原理: 可查看文章【了解canal,看这个就够了】
  5. 安装与搭建流程:可参考文章【canal应用-1个server+2个instance+2个client+2个mysql】


处理分表分库的场景,主要是要使用配置group-instance.xml。group-instance主要针对需要进行多库合并时,可以将多个物理instance合并为一个逻辑instance,提供客户端访问。


比如产品数据拆分了4个库,每个库会有一个instance,如果不用group,业务上要消费数据时,需要启动4个客户端,分别链接4个instance实例。使用group后,可以在canal server上合并为一个逻辑instance,只需要启动1个客户端,链接这个逻辑instance即可。


二 关键实现流程


2.1 canal.properties配置文件


canal.properties是对应一个canal server的全局配置,保存位置:/usr/local/hadoop/app/canal_group/conf/canal.properties。配置修改内容如下:


canal.id = 1 #唯一标识
canal.ip =192.168.175.20 # client访问canal server的ip地址
canal.port = 11111  # client访问canal server的端口
#canal.instance.global.spring.xml = classpath:spring/file-instance.xml   #原来是这个
canal.instance.global.spring.xml = classpath:spring/group-instance.xml  #启动这个
#其他配置保持默认即可.


2.2 instance.properties配置文件


使用如下命令复制出两个代表canal instance的文件夹:


cp -R example t_dept;
cp -R example t_canal;
rm -rf example;


调整配置文件/usr/local/hadoop/app/canal/conf/t_dept/instance.properties如下:


#canal.instance.master.address=192.168.175.21:3306 #原来的
canal.instance.master1.address=192.168.175.21:3306 #新增,与group-instance.xml的对应
canal.instance.master2.address=192.168.175.22:3306 #新增,与group-instance.xml的对应
# username/password
canal.instance.dbUsername=canal
canal.instance.dbPassword=canal
# mq config
canal.mq.topic=t_dept


调整配置文件/usr/local/hadoop/app/canal/conf/t_canal/instance.properties如下:


#canal.instance.master.address=192.168.175.21:3306 #原来的
canal.instance.master1.address=192.168.175.21:3306 #新增,与group-instance.xml的对应
# username/password
canal.instance.dbUsername=canal
canal.instance.dbPassword=canal
# mq config
canal.mq.topic=t_canal


2.3 group-instance.xml配置文件

配置文件/usr/local/hadoop/app/canal/conf/spring/group-instance.xml不需要做调整。


2.4 启动canal server

进入文件夹/usr/local/hadoop/app/canal/bin执行如下启动命令:


./startup.sh


查看日志/usr/local/hadoop/app/canal/logs/canal/canal.log,出现如下内容,即表示启动成功:


2019-06-07 21:15:03.372 [main] INFO  com.alibaba.otter.canal.deployer.CanalLauncher - ## load canal configurations
2019-06-07 21:15:03.427 [main] INFO  c.a.o.c.d.monitor.remote.RemoteConfigLoaderFactory - ## load local canal configurations
2019-06-07 21:15:03.529 [main] INFO  com.alibaba.otter.canal.deployer.CanalStater - ## start the canal server.
2019-06-07 21:15:06.251 [main] INFO  com.alibaba.otter.canal.deployer.CanalController - ## start the canal server[192.168.175.20:11111]
2019-06-07 21:15:22.245 [main] INFO  com.alibaba.otter.canal.deployer.CanalStater - ## the canal server is running now ......


2.5 使用canal client连接canal server


注意运行canal客户端代码时,一定要先启动canal server!!!

(1) 添加pom依赖


<!--canal-->
    <dependency>
      <groupId>com.alibaba.otter</groupId>
      <artifactId>canal.client</artifactId>
      <version>1.1.3</version>
    </dependency>


(2) canal client代码:


package com.xgh.canal;
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 java.net.InetSocketAddress;
import java.util.List;
public class CanalClientGroupTest1 {
    public static void main(String args[]) {
        //String zkHost="192.168.175.20:2181,192.168.175.21:2181,192.168.175.22:2181";
        // 创建链接
        //CanalConnector connector = CanalConnectors.newClusterConnector(zkHost,"example","","");
        CanalConnector connector = CanalConnectors.newSingleConnector(new InetSocketAddress("192.168.175.21", 11111),
                "t_dept", "", "");
        int batchSize = 1000;
        int emptyCount = 0;
        long batchId = 0;
        //外层死循环:在canal节点宕机后,抛出异常,等待zk对canal处理切换,切换完后,继续创建连接处理数据
        while(true) {
            try {
                connector.connect();
                connector.subscribe(".*\\..*");//订阅所有库下面的所有表
                //connector.subscribe("canal.t_canal");//订阅库canal库下的表t_canal
                connector.rollback();
                //内层死循环:按频率实时监听数据变化,一旦收到变化数据,立即做消费处理,并ack,考虑消费速度,可以做异步处理并ack.
                while (true) {
                    Message message = connector.getWithoutAck(batchSize); // 获取指定数量的数据
                    batchId = message.getId();
                    int size = message.getEntries().size();
                    //// 偏移量不等于-1 或者 获取的数据条数不为0 时,认为拿到消息,并处理
                    if (batchId == -1 || size == 0) {
                        emptyCount++;
                        System.out.println("empty count : " + emptyCount);//此時代表當前數據庫無遍更數據
                        Thread.sleep(1000); //1000ms拉一次变动数据
                    } else {
                        emptyCount = 0;
                        System.out.printf("message[batchId=%s,size=%s] \n", batchId, size);
                        printEntry(message.getEntries());
                    }
                    connector.ack(batchId); // 提交确认
                    //
                }
            }catch(Exception e){
                e.printStackTrace();
                connector.rollback(batchId); // 处理失败, 回滚数据
            } finally {
                connector.disconnect();
            }
        }
    }
    private static void printEntry(List<Entry> entrys) {
        for (Entry entry : entrys) {
            if (entry.getEntryType() == EntryType.TRANSACTIONBEGIN
                    || entry.getEntryType() == EntryType.TRANSACTIONEND) {
                continue;
            }
            RowChange rowChage = null;
            try {
                rowChage = RowChange.parseFrom(entry.getStoreValue());
            } catch (Exception e) {
                throw new RuntimeException("ERROR ## parser of eromanga-event has an error , data:" + entry.toString(),
                        e);
            }
            System.out.println("rowChare ======>"+rowChage.toString());
            EventType eventType = rowChage.getEventType(); //事件類型,比如insert,update,delete
            System.out.println(String.format("================> binlog[%s:%s] , name[%s,%s] , eventType : %s",
                    entry.getHeader().getLogfileName(),//mysql的my.cnf配置中的log-bin名稱
                    entry.getHeader().getLogfileOffset(), //偏移量
                    entry.getHeader().getSchemaName(),//庫名
                    entry.getHeader().getTableName(), //表名
                    eventType));//事件名
            for (RowData rowData : rowChage.getRowDatasList()) {
                if (eventType == EventType.DELETE) {
                    printColumn(rowData.getBeforeColumnsList());
                } else if (eventType == EventType.INSERT) {
                    printColumn(rowData.getAfterColumnsList());
                } else {
                    System.out.println("-------> before");
                    printColumn(rowData.getBeforeColumnsList());
                    System.out.println("-------> after");
                    printColumn(rowData.getAfterColumnsList());
                }
            }
        }
    }
    private static void printColumn(List<Column> columns) {
        for (Column column : columns) {
            System.out.println(column.getName() + " : " + column.getValue() + "    update=" + column.getUpdated());
        }
    }
}


2.6 其他


将canal client代码CanalConnectors.newSingleConnector(new InetSocketAddress("192.168.175.21", 11111), "t_dept", "", "");中的队列名t_dept换成t_canal再执行,就可以监听t_canal对应数据变化了.


三 运行测试及总结



1. 监听t_dept的canal client可以接收到数据库A和B的数据变化
2. 监听t_canal的canal client只能接收到数据库B的数据变化
3. 数据过滤的设置问题


当在instance.properties和canal client中对设置filter时,canal client的设置会覆盖instance.properties中的配置。所以不如干脆保持instance.properties为默认状态,也即是不过滤,然后过滤全部设置在canal client中,如下:


connector.connect();
connector.subscribe(".*\\..*");//订阅所有库下面的所有表
//connector.subscribe("canal.t_canal");//订阅库canal库下的表t_canal


四 高可用架构设计



网络异常,图片无法展示
|


高可用架构设计


参考文章:
  1. https://www.cnblogs.com/yulu080808/p/8819260.html
  2. https://github.com/alibaba/canal
  3. https://blog.csdn.net/my201110lc/article/details/78836270
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2天前
|
Cloud Native 持续交付 云计算
探索云原生架构:构建现代应用的新范式
在当今数字化浪潮中,云原生架构以其敏捷性、弹性和可扩展性成为企业技术转型的核心驱动力。本文将引领读者深入理解云原生的概念,剖析其关键技术组件——微服务、容器化、DevOps实践及持续交付/持续部署流程,并揭示这些技术如何相互协作,共同构建高效、可靠且易于管理的现代软件系统。通过对云原生架构的全面解读,我们旨在为开发者、架构师乃至企业决策者提供有价值的见解与指导,助力其在快速变化的市场环境中保持竞争力。
|
7天前
|
Kubernetes Cloud Native Serverless
探索云原生技术:从基础架构到应用实践
本文深入探讨了云原生技术的各个方面,包括其定义、核心原则、关键技术组件以及在现代企业中的应用。通过分析云原生如何推动数字化转型和提高业务敏捷性,文章旨在为读者提供对这一领域的全面了解和实际应用的指导。
31 7
|
9天前
|
Kubernetes Cloud Native 持续交付
深入理解云原生技术及其在现代IT架构中的应用
【9月更文挑战第18天】云原生技术,作为推动企业数字化转型的引擎,正以它独特的魅力重塑着信息技术的未来。本文将带你一探究竟,从云原生的基础概念出发,逐步深入到其核心组件、设计理念以及如何在实际应用中发挥巨大作用。你将了解到容器化、微服务架构、持续集成与持续部署(CI/CD)等关键实践,并见证它们如何帮助企业构建更加灵活、高效和可靠的应用。
|
1天前
|
前端开发 测试技术 API
探索微前端架构:构建现代化的前端应用
在软件开发中,传统单体架构已难以满足快速迭代需求,微前端架构应运而生。它将前端应用拆分成多个小型、独立的服务,每个服务均可独立开发、测试和部署。本文介绍微前端架构的概念与优势,并指导如何实施。微前端架构具备自治性、技术多样性和共享核心的特点,能够加速开发、提高可维护性,并支持灵活部署策略。实施步骤包括定义服务边界、选择架构模式、建立共享核心、配置跨服务通信及实现独立部署。尽管面临服务耦合、状态同步等挑战,合理规划仍可有效应对。
|
1天前
|
Cloud Native Devops 持续交付
探秘云原生架构:构建高效、灵活的现代应用
在当今数字化时代,企业面临着日益复杂的技术挑战和快速变化的业务需求。为了适应这种环境,云原生架构应运而生。本文将带您深入了解云原生的核心概念、关键技术和应用案例,揭示其在提升业务效率、降低运维成本方面的独特优势。通过阅读本文,您将获得关于如何利用云原生技术构建现代化应用的宝贵见解。
8 0
|
3天前
|
传感器 监控 物联网
无线传感器网络的基本架构及其广泛应用
无线传感器网络的基本架构及其广泛应用
20 0
|
9天前
|
Kubernetes Cloud Native Devops
云原生架构的崛起与应用##
云原生架构是现代企业数字化转型的关键,通过容器化、微服务、DevOps等技术,实现高效、灵活的应用部署和管理。本文探讨云原生的核心概念、主要技术及其实际应用价值,揭示其在提升企业运营效率和创新能力中的重要性。 ##
18 0
|
3天前
|
安全 应用服务中间件 API
微服务分布式系统架构之zookeeper与dubbo-2
微服务分布式系统架构之zookeeper与dubbo-2
|
3天前
|
负载均衡 Java 应用服务中间件
微服务分布式系统架构之zookeeper与dubbor-1
微服务分布式系统架构之zookeeper与dubbor-1
|
30天前
|
Kubernetes Cloud Native Docker
云原生之旅:从容器到微服务的架构演变
【8月更文挑战第29天】在数字化时代的浪潮下,云原生技术以其灵活性、可扩展性和弹性管理成为企业数字化转型的关键。本文将通过浅显易懂的语言和生动的比喻,带领读者了解云原生的基本概念,探索容器化技术的奥秘,并深入微服务架构的世界。我们将一起见证代码如何转化为现实中的服务,实现快速迭代和高效部署。无论你是初学者还是有经验的开发者,这篇文章都会为你打开一扇通往云原生世界的大门。

热门文章

最新文章