如何通过 CloudCanal 实现从 Kafka 到 AutoMQ 的数据迁移

news2024/9/17 7:29:02

01 引言

随着大数据技术的飞速发展,Apache Kafka 作为一种高吞吐量、低延迟的分布式消息系统,已经成为企业实时数据处理的核心组件。然而,随着业务的扩展和技术的发展,企业面临着不断增加的存储成本和运维复杂性问题。为了更好地优化系统性能和降低运营成本,企业开始寻找更具优势的消息系统解决方案。其中,AutoMQ [1] 作为一种基于云重新设计的消息系统,凭借其显著的成本优势和弹性能力,成为了企业的理想选择。

1.1 AutoMQ 介绍

AutoMQ 基于云重新设计了 Kafka,将存储分离至对象存储,在保持与 Apache Kafka 100% 兼容的前提下,为用户提供高达10倍的成本优势和百倍的弹性优势。AutoMQ 通过构建在S3上的流存储库 S3Stream,将存储卸载至云厂商提供的共享云存储 EBS 和 S3,提供低成本、低延时、高可用、高可靠和无限容量的流存储能力。与传统的Shared Nothing 架构相比,AutoMQ 采用了 Shared Storage 架构,显著降低了存储和运维的复杂性,同时提升了系统的弹性和可靠性。AutoMQ 的设计理念和技术优势使其成为替换企业现有 Kafka 集群的理想选择。通过采用 AutoMQ,企业可以显著降低存储成本,简化运维,并实现集群的自动扩缩容和流量自平衡,从而更高效地应对业务需求的变化。

1.2 CloudCanal 概述

CloudCanal [2] 是一款数据同步、迁移工具,帮助企业构建高质量数据管道,具备实时高效、精确互联、稳定可拓展、一站式、混合部署、复杂数据转换等优点。CloudCanal 支持数据迁移、数据同步、结构迁移和同步、数据校验和订正等功能,能够满足企业在数据管理过程中对于数据质量和稳定性的高要求。通过消费源端数据源的增量操作日志,CloudCanal 可以准实时地在对端数据源重放操作,以达到数据同步的目的。

1.3 数据迁移的必要性

在企业的日常运营中,数据系统的升级和迁移是不可避免的。例如,当企业的电商平台面临流量激增和数据量爆炸式增长时,现有的 Kafka 集群可能无法满足需求,导致性能瓶颈和存储成本的显著增加。为了应对这些挑战,企业可能决定迁移到更具成本效益和弹性的 AutoMQ 系统。在这种迁移过程中,全量同步和增量同步都是关键步骤。全量同步可以将 Kafka 中的所有现有数据迁移到  AutoMQ,确保基础数据的完整性。增量同步则在全量同步完成后,实时捕捉和同步 Kafka 中的新增和变更数据,确保在迁移过程中,两个系统之间的数据保持一致。接下来,我将以增量同步为例,详细介绍如何使用 CloudCanal 实现从 Kafka 到 AutoMQ 的数据迁移,确保数据在迁移过程中保持一致和完整。

02 前置条件

在进行数据迁移之前,需要确保以下前提条件已经满足。本文将以一个 Kafka 节点和一个 AutoMQ 节点为例,演示增量同步的过程。

  1. Kafka 节点:一个已部署并运行的 Kafka 节点,确保 Kafka 节点能够正常接收和处理消息,Kafka节点的网络配置允许与 CloudCanal 服务通信。

  2. AutoMQ 节点:一个已部署并运行的 AutoMQ 节点,确保 AutoMQ 节点能够正常接收和处理消息,AutoMQ 节点的网络配置允许与 CloudCanal 服务通信。

  3. CloudCanal 服务: 已部署和配置好的 CloudCanal 服务。

03 部署 AutoMQ、kafka 以及 CloudCanal

3.1 部署 AutoMQ

参考 AutoMQ 官网文档: QuickStart | AutoMQ [3]

3.2 部署 Kafka

参考 Apache Kafka 官方文档:QuickStart | Kafka [4]

3.3 部署 CloudCanal

安装与启动

1.  安装基础工具

## ubuntu
sudo apt update
sudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
sudo apt-get install -y lsof
sudo apt-get install -y bc
sudo apt-get install -y p7zip-full

2.  下载安装包登录 CloudCanal 官方网站 [5],点击下载私有部署版按钮,获取软件包下载链接。下载并解压到文件夹/opt/

cd /opt
# 下载
wget -cO cloudcanal.7z "${软件包下载链接}"
# 解压
7z x cloudcanal.7z -o./cloudcanal_home
cd cloudcanal_home/install_on_docker

install_on_docker目录内容包括

  • 镜像: images 目录下四个 tar 结尾的压缩文件
  • docker 容器编排文件: docker-compose.yml 文件
  • 脚本:一些管理 CloudCanal 容器以及维护的脚本

 3.  准备 Docker 环境请确保以下端口未被占用

如果你没有 docker 和 docker compose 环境,可参考 Docker 官方文档 [6] (版本 17.x.x 及以上)。也可直接使用目录中提供的脚本进行安装:

## ubuntu,进入 install_on_docker 目录
bash ./support/install_ubuntu_docker.sh
  1.  启动 CloudCanal,执行安装脚本以启动:
## ubuntu
bash install.sh

出现如下标识即安装成功

激活 CloudCanal

安装成功后,你可以通过 http://{ip}:8111 在浏览器中访问 CloudCanal 的控制台。

注意:如果无法正常访问页面,可以尝试通过脚本更新当前 CloudCanal 的版本,可使用如下命令:

# 进入安装目录
cd /opt/cloudcanal_home/install_on_docker
# 停止当前 CloudCanal
sudo bash stop.sh
# 更新并启动新的 CloudCanal
sudo bash upgrade.sh

 1.  进入登录界面后,通过试用账号登录

  • 账号: test@clougence.com

  • 密码: clougence2021

  • 默认验证码: 777777

 2.  登录成功,需要激活 CloudCanal 账号即可正常使用。申请免费许可证并激活: 许可证获取 | CloudCanal [7],激活成功后,主界面状态为:

04 数据迁移过程

4.1 准备源端 Kafka 数据

可以选择如下方式:

  • CloudCanal 提供的 Mysql->Kafka 数据同步过程,参考:MySQL 到 Kafka 同步 | CloudCanal [8]
  • 通过 Kafka SDK 准备数据
  • 通过 Kafka 提供的脚本手动生产消息

这里我将通过 Kafka SDK 的方式进行数据准备,下面是参考代码:

import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.AdminClientConfig;
import org.apache.kafka.clients.admin.NewTopic;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
import org.apache.kafka.common.serialization.StringSerializer;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutionException;

public class KafkaTest {

    private static final String BOOTSTRAP_SERVERS = "${kafka_broker_ip:port}"; //修改为你自己的 Kafka 节点地址
    private static final int NUM_TOPICS = 50;
    private static final int NUM_MESSAGES = 500;

    public static void main(String[] args) throws Exception {
        KafkaTest test = new KafkaTest();
        test.createTopics();
        test.sendMessages();
    }
    
    // 创建50个 Topic,格式为 Topic-n
    public void createTopics() {
        Properties props = new Properties();
        props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVERS);

        try (AdminClient adminClient = AdminClient.create(props)) {
            List<NewTopic> topics = new ArrayList<>();
            for (int i = 1; i <= NUM_TOPICS; i++) {
                topics.add(new NewTopic("Topic-" + i, 1, (short) 1));
            }
            adminClient.createTopics(topics).all().get();
            System.out.println("Topics created successfully");
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
    // 为50个 Topic-n 分别发送序号从1到1000共一千条消息,消息格式为 Json格式
    public void sendMessages() {
        Properties props = new Properties();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, BOOTSTRAP_SERVERS);
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

        try (KafkaProducer<String, String> producer = new KafkaProducer<>(props)) {
            for (int i = 1; i <= NUM_TOPICS; i++) {
                String topic = "Topic-" + i;
                for (int j = 1; j <= NUM_MESSAGES; j++) {
                    String key = "key-" + j;
                    String value = "{\"userId\": " + j + ", \"action\": \"visit\", \"timestamp\": " + System.currentTimeMillis() + "}";
                    ProducerRecord<String, String> record = new ProducerRecord<>(topic, key, value);
                    producer.send(record, (RecordMetadata metadata, Exception exception) -> {
                        if (exception == null) {
                            System.out.printf("Sent message to topic %s partition %d with offset %d%n", metadata.topic(), metadata.partition(), metadata.offset());
                        } else {
                            exception.printStackTrace();
                        }
                    });
                }
            }
            System.out.println("Messages sent successfully");
        }
    }
}

创建完成后,可以通过各种可视化工具查看 Kafka 节点状态,比如 Redpanda Console [9]、Kafdrop [10] 等。这里我选择 Redpanda Console,可以看到当前已经有了 50 个Topic,并且每个 Topic 下有500条初始消息。

其中消息的格式为 Json:

{
    "action": "INSERT/UPDATE/DELETE",
    "bid": 1,
    "before": [],
    "data": [{
        "id":"string data",
        "username":"string data",
        "user_id":"string data",
        "ip":"string data",
        "request_time":"1608782968300","request_type":"string data"}],
    "db": "access_log_db",
    "schema": "",
    "table":"access_log",
    "dbValType": {
        "id":"INT",
        "username":"VARCHAR",
        "user_id":"INT",
        "ip":"VARCHAR",
        "request_time":"TIMESTAMP",
        "request_type":"VARCHAR",},
    "jdbcType": {
        "id":"0",
        "username":"0",
        "user_id":"0",
        "ip":"0",
        "request_time":"0",
        "request_type":"0",},
    "entryType": "ROWDATA",
    "isDdl": false,
    "pks": ["id"],
    "execTs": 0,
    "sendTs": 0,
    "sql": ""}

并且,AutoMQ 节点当前并无任何数据:

添加 CloudCanal 数据源

CloudCanal 界面上方 数据源管理 -> 新增数据源

同理增加 Kafka 数据源,并对两个节点都进行连接测试,可以得到如下结果:

4.3 创建数据迁移任务

1.  CloudCanal 界面上方 同步任务->创建任务

 2.  选择任务规格,这取决于你需要迁移的数据量大小:

 3.  选择需要进行数据迁移的 Topics:

 4.  任务确定:

 5.  任务创建完成后默认自动启动,会跳转到任务列表,你还需要更改源数据源配置以开启心跳配置,能及时更新任务状态,步骤为 任务详情->源数据源配置->修改配置->生效配置:

 6.  随后等待任务重启完成,即可看到如下情况:

注意:如果遇到关于连接问题以及任务延迟过高等问题可以参考 CloudCanal 官方文档:FAQ 索引 | CloudCanal [11]

7.  验证 AutoMQ 中是否已经正确创建了 Topic 结构

4.4 准备增量数据

任务已经正常运行,接下来我们需要准备增量数据,使得迁移任务能够将增量数据同步到 AutoMQ。这里我们仍然通过 Kafka SDK 新增数据。新增数据之后,我们可以通过 任务详情->增量同步->查看日志->任务运行日志 中查看任务执行情况:

2024-07-11 17:16:45.995 [incre-fetch-from-buffer-14-thd-0] INFO  c.c.c.mq.worker.reader.kafka.KafkaIncreEventBroker - getWithoutAck successfully, batch:64, real:64
2024-07-11 17:16:45.995 [incre-fetch-from-buffer-14-thd-0] INFO  c.c.c.mq.worker.reader.kafka.KafkaIncreEventBroker - getWithoutAck successfully, batch:64, real:64
2024-07-11 17:16:45.996 [incre-fetch-from-buffer-14-thd-0] INFO  c.c.c.mq.worker.reader.kafka.KafkaIncreEventBroker - getWithoutAck successfully, batch:64, real:64
2024-07-11 17:16:45.996 [incre-fetch-from-buffer-14-thd-0] INFO  c.c.c.mq.worker.reader.kafka.KafkaIncreEventBroker - getWithoutAck successfully, batch:64, real:64
2024-07-11 17:16:45.996 [incre-fetch-from-buffer-14-thd-0] INFO  c.c.c.mq.worker.reader.kafka.KafkaIncreEventBroker - getWithoutAck successfully, batch:64, real:64
2024-07-11 17:16:45.997 [incre-fetch-from-buffer-14-thd-0] INFO  c.c.c.mq.worker.reader.kafka.KafkaIncreEventBroker - getWithoutAck successfully, batch:64, real:64
2024-07-11 17:16:45.997 [incre-fetch-from-buffer-14-thd-0] INFO  c.c.c.mq.worker.reader.kafka.KafkaIncreEventBroker - getWithoutAck successfully, batch:64, real:64
2024-07-11 17:16:45.997 [incre-fetch-from-buffer-14-thd-0] INFO  c.c.c.mq.worker.reader.kafka.KafkaIncreEventBroker - getWithoutAck successfully, batch:64, real:64
2024-07-11 17:16:45.998 [incre-fetch-from-buffer-14-thd-0] INFO  c.c.c.mq.worker.reader.kafka.KafkaIncreEventBroker - getWithoutAck successfully, batch:64, real:64
2024-07-11 17:16:45.998 [incre-fetch-from-buffer-14-thd-0] INFO  c.c.c.mq.worker.reader.kafka.KafkaIncreEventBroker - getWithoutAck successfully, batch:64, real:64
2024-07-11 17:16:45.998 [incre-fetch-from-buffer-14-thd-0] INFO  c.c.c.mq.worker.reader.kafka.KafkaIncreEventBroker - getWithoutAck successfully, batch:64, real:64
2024-07-11 17:16:45.999 [incre-fetch-from-buffer-14-thd-0] INFO  c.c.c.mq.worker.reader.kafka.KafkaIncreEventBroker - getWithoutAck successfully, batch:64, real:64

05 验证迁移结果

验证AutoMQ是否正确同步到消息:

多次新增数据后依旧正常完成迁移:

可以看到在增量同步任务执行期间对 Kafka 新增的数据都已经同步到了 AutoMQ 中。至此,我们的迁移过程已经全部完成。

06 总结

随着企业数据规模的不断扩大和业务需求的多样化,数据迁移和同步变得尤为重要。通过本文的介绍,我们详细探讨了如何利用 CloudCanal 实现从 Kafka 到 AutoMQ 的增量同步数据迁移,以应对存储成本和运维复杂性的问题。在迁移过程中,增量同步技术确保了数据的一致性和业务的连续性,为企业提供了一个高效、可靠的解决方案。希望本文能够为你在数据迁移和同步方面提供有价值的参考和指导,帮助实现系统的平滑过渡和性能优化!

引用

[1] AutoMQ: https://docs.automq.com/zh/docs/automq-opensource/HSiEwHVfdiO7rWk34vKcVvcvn2Z
[2] CloudCanal: https://www.clougence.com/?src=cc-doc
[3] QuickStart | AutoMQ: https://docs.automq.com/zh/docs/automq-opensource/EvqhwAkpriAomHklOUzcUtybn7g
[4] QuickStart | Kafka: https://kafka.apache.org/quickstart
[5] CloudCanal 官方网站: https://www.clougence.com/?src=cc-doc-install-linux
[6] Docker 官方文档: https://docs.docker.com/engine/install/
[7] 许可证获取 | CloudCanal: https://www.clougence.com/cc-doc/license/license_use
[8] MySQL 到 Kafka 同步 | CloudCanal: https://www.clougence.com/cc-doc/bestPractice/mysql_kafka_sync
[9] Redpanda Console: https://redpanda.com/redpanda-console-kafka-ui
[10] Kafdrop: https://github.com/obsidiandynamics/kafdrop
[11] FAQ 索引 | CloudCanal: https://www.clougence.com/cc-doc/faq/cloudcanal_faq_list

关于我们
我们是来自 Apache RocketMQ 和 Linux LVS 项目的核心团队,曾经见证并应对过消息队列基础设施在大型互联网公司和云计算公司的挑战。现在我们基于对象存储优先、存算分离、多云原生等技术理念,重新设计并实现了 Apache Kafka 和 Apache RocketMQ,带来高达 10 倍的成本优势和百倍的弹性效率提升。

🌟 GitHub 地址:https://github.com/AutoMQ/automq
💻 官网:https://www.automq.com?utm_source=openwrite

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1958259.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Discourse 如何通过终端工具访问 PGSQL

PGSQL 在 Discourse 中是通过容器方式运行的&#xff0c;要访问 PGSQL 中的数据那么首先需要进入到容器后才可以。 进入容器的命令为&#xff1a; cd /var/discourse/./launcher enter appsu discoursepsql discourse最后的命令是登录到运行在容器中的 pgsql。 查看 pgsql 上…

CTFshow--Web--代码审计

目录 web301 web302 web303 web304 web305 web306 web307 web308 web309 web310 web301 开始一个登录框, 下意识sql尝试一下 发现 1 的时候会到一个 checklogin.php 的路径下, 但啥也没有 好吧, 这是要审计代码的 ,下载好源码, 开始审计 看了一下源码 , 应该就是sql…

thinkPHP开发的彩漂网站源码,含pc端和手机端

源码简介 后台thinkPHP架构,页面程序双分离,Mysql数据库严谨数据结构、多重数据审核机制、出票机制和监控机制,html5前端技术适用移动端,后台逻辑更多以server接口可快捷实现对接pc和ap,下载会有少量图片素材丢失,附件有下载说明前端demo账户密码和后台管理地址管理员账户密码…

c++修炼之路之二叉搜索树

目录 前言 一&#xff1a;二叉搜索树的介绍 二&#xff1a;二叉搜索树的实现 1.查找 2.insert(插入) 3.erase(删除) 4.析构函数 5.拷贝构造 6.赋值重载 7.插入&#xff0c;删除&#xff0c;查找的递归版本 三&#xff1a;二叉搜索树的应用 四&#xff1a;二叉搜索…

精灵图的使用——网页制作技巧

把许多小图标集中在一张背景透明的图片上&#xff0c;这种图片叫做精灵图&#xff0c;如下图。 在线工具地址&#xff1a;http://www.spritecow.com/ 通过工具可以快速找到图标在精灵图上的坐标位置。 首先&#xff0c;我们打开工具地址&#xff0c;点击第一个按钮。 点击后&am…

LeetCode 算法:搜索二维矩阵 c++

原题链接&#x1f517;&#xff1a;搜索二维矩阵 难度&#xff1a;中等⭐️⭐️ 题目 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。给你一个整数 target &#xff0c;如果…

实战:ZooKeeper 操作命令和集群部署

ZooKeeper 操作命令 ZooKeeper的操作命令主要用于对ZooKeeper服务中的节点进行创建、查看、修改和删除等操作。以下是一些常用的ZooKeeper操作命令及其说明&#xff1a; 一、启动与连接 启动ZooKeeper服务器&#xff1a; ./zkServer.sh start这个命令用于启动ZooKeeper服务器…

手搓排序算法:插入排序、选择排序

文章目录 插入排序直接插入排序希尔排序内层循环时间复杂度计算 选择排序直接选择排序优化 堆排序 插入排序 直接插入排序 时间复杂度最差&#xff1a;大的数据都在左边&#xff0c;小的数据在右边&#xff0c;随着有序区间增大&#xff0c;交换次数增多 时间复杂度最优&…

C#知识|文本文件操作:删除、复制、移动文件的操作

哈喽,你好啊,我是雷工! 接下来学习文件的删除和复制,实际应用场景,当软件具有自动在线更新功能时,需要先检测服务器是否具有更新版本的安装包,如果有的话需要将其复制到本地进行升级安装,如果有勾选自动清理安装包功能的话,还可以将安装包删除。 01 删除文件 实现功能…

使用MultipartFile来上传单个及多个文件代码示例(前端传参数及后端接收)

背景 前端使用vue或vue+vant上传文件 后端java接收MultipartFile和其他参数 一、MultipartFile上传单个文件代码示例 1.1 MultipartFile上传单个文件,不包含其它参数 1.1.1 控制层代码如下: /*** 1、上传单个文件,不包含其它参数* */ @PostMapping( "/upload")…

docker 建木 发版 (详细教程)

先创建git仓库 Git勤勉 两种方式上传-CSDN博客 把项目送上去 进入建木 可以接着这个来 dockerfile部署镜像 -&#xff1e;push仓库 -&#xff1e;虚拟机安装建木 -&#xff1e;自动部署化 (详细步骤)-CSDN博客 创建分组项目 开始操作 git 上钩子 前面链接里有这个教…

MobaXterm tmux 配置妥当

一、事出有因 缘由&#xff1a;接上篇文章&#xff0c;用Docker搭建pwn环境后&#xff0c;用之前学过的多窗口tmux进行调试程序&#xff0c;但是鼠标滚动的效果不按预期上下翻屏。全网搜索很难找到有效解决办法&#xff0c;最后还是找到了一篇英文文章&#xff0c;解决了&…

upload-labs靶场练习

文件上传函数的常见函数&#xff1a; 在PHP中&#xff0c;‌文件上传涉及的主要函数包括move_uploaded_file(), is_uploaded_file(), get_file_extension(), 和 mkdir()。‌这些函数共同协作&#xff0c;‌使得用户可以通过HTTP POST方法上传文件&#xff0c;‌并在服务器上保存…

浅谈C语言整型类数据在内存中的存储

1、整型类数据 C语言中的整型类数据都归类在整型家族中&#xff0c;其中包括&#xff1a;char、short、int、long、long long这5个大类&#xff0c;而每个大类中又分为两类signed和unsigned,这些都是C语言中的内置类型。以下重点基于char和int这两种类型的数据进行阐述&#x…

妈吖,看过这个大厂的oracle主键自增,我的信心暴增!信创,国产数据库也能行。

创作不易 只因热爱!! 热衷分享&#xff0c;一起成长! “你的鼓励就是我努力付出的动力” 1.数据库oracle自增主键字段思维导图 在Oracle数据库中&#xff0c;可以通过创建序列&#xff08;SEQUENCE&#xff09;来实现自增功能。但也可以不在数据库中实现&#xff0c;而是通过程…

Sequential的使用

卷积前后尺寸不变的 Padding值计算&#xff1a; padding &#xff08;卷积核尺寸-1&#xff09;/2 Sequential 可以简化代码&#xff1a; def __init__(self):super(Tudui, self).__init__()self.model1 Sequential(Conv2d(3, 32, 5, padding2),MaxPool2d(2),Conv2d(32, 32…

ctfshow web入门 CMS web477--web479

web477 CMSEazy5.7 不让扫&#xff0c;那就尝试一下admin路由&#xff0c;成功了 admin登录进入后台 也看到了其实 首页可以看到提示 然后去自定义标签打 1111111111";}<?php phpinfo()?>刷新一下预览即可 11";}<?php assert($_POST[g]);?>也可…

Git和TortoiseGit的安装与使用

文章目录 前言一、Git安装步骤查看版本信息 二、TortoiseGit安装中文语言包TortoiseGit 配置不同语言 Git基本原理介绍及常用指令 GitLab添加TortoiseGIT生成SSH Key 前言 Git 提供了一种有效的方式来管理项目的版本&#xff0c;协作开发&#xff0c;以及跟踪和应用文件的变化…

【FPGA设计】千兆以太网

一. OSI 七层模型 OSI&#xff08;Open Systems Interconnection&#xff09;七层模型是由国际标准化组织&#xff08;ISO&#xff09;制定的一个概念模型&#xff0c;用于描述网络中各部分如何通信。这个模型将网络通信分解为七个不同的层次&#xff0c;每一层都有特定的功能…

嵌入式学习Day14---C语言进阶

目录 一、构造类型 1.1.结构体 1.存储 2.输入输出&#xff08;传参&#xff09; 3.结构体数组 1.2.共同体&#xff08;联合体&#xff09; 1.格式 2.存储 3.测试一个平台是打端还是小端 1.3.枚举 1.格式 2.特点 二、位运算&#xff08;操作二进制&#xff09; 2.1.&a…