Debezium发布历史103

news2024/11/15 21:28:00

原文地址: https://debezium.io/blog/2021/03/18/understanding-non-key-joins-with-quarkus-extension-for-kafka-streams/

欢迎关注留言,我是收集整理小能手,工具翻译,仅供参考,笔芯笔芯.

了解 Kafka Streams 的 Quarkus 扩展的非键连接
三月 18, 2021 作者: Anisha Mohanty
kafka 流 quarkus 示例
Kafka Streams是一个用于开发基于 Apache Kafka 的流处理应用程序的库。引用其文档,“Kafka Streams 应用程序通过拓扑实时处理记录流,以逐条记录的方式连续、并发地处理数据”。Kafka Streams DSL 提供一系列流处理操作,例如映射、过滤器、连接和聚合。

Kafka 流中的非键连接
Debezium 的 CDC 源连接器可以轻松捕获数据库中的数据更改,并将其近乎实时地推送到 Elasticsearch 等接收器系统。默认情况下,这会导致源数据库中的表、相应的 Kafka 主题以及接收器端的数据表示(例如 Elasticsearch 中的搜索索引)之间形成 1:1 关系。

在 1:n 关系的情况下,例如客户表和地址表之间,消费者通常对单个嵌套数据结构的数据视图感兴趣,例如代表客户及其所有信息的单个 Elasticsearch 文档。地址。

这就是KIP-213 (“Kafka 改进提案”)及其外键连接功能的用武之地:它是在Apache Kafka 2.4中引入的,“以缩小流中 KTable 的语义与关系数据库中表之间的差距”。在 KIP-213 之前,为了连接来自两个 Debezium 更改事件主题的消息,您通常必须手动重新设置至少一个主题的密钥,以确保连接两侧使用相同的密钥。

感谢 KIP-213,这不再需要,因为它允许在从 Kafka 消息值提取的字段上加入两个 Kafka 主题,以完全透明的方式自动处理所需的重新键入。与以前的方法相比,这大大减少了从 Debezium 的 CDC 事件创建聚合事件的工作量。

非键连接或更确切地说外键连接类似于 SQL 中的连接,如下所示:

SELECT * FROM CUSTOMER JOIN ADDRESS ON CUSTOMER.ID = ADDRESS.CUSTOMER_ID
在 Kafka Streams 术语中,此类连接的输出是KTable包含连接结果的新输出。

数据库概述
继续使用我们之前的客户和地址示例,让我们考虑具有以下数据模型的应用程序:
图片来自于原文
在这里插入图片描述

数据库概述
客户和地址这两个实体共享从地址到客户的外键关系,即一个客户可以有多个地址。如上所述,默认情况下 Debezium 将为每个表发出不同主题的事件。使用 Kafka Streams,两个表的更改事件主题将被加载到两个KTables 中,并在客户 id 上连接。Kafka Streams 应用程序将处理来自两个 Kafka 主题的数据。每当任一主题出现新的 CDC 事件(由记录的插入、更新或删除触发)时,都会重新执行联接。

作为 Kafka Streams 应用程序的运行时,我们将使用Quarkus,这是一个用于构建云原生微服务的堆栈,它(以及其他许多服务)还为 Kafka Streams 提供了扩展。虽然通常可以通过简单的main()方法运行 Kafka Streams 拓扑,但使用 Quarkus 和此扩展作为基础具有许多优点:

拓扑管理(例如等待创建所有输入主题)

通过环境变量、系统属性等进行配置。

暴露健康检查

公开指标

开发模式,一种在代码更改后自动热代码替换的流拓扑工作方式

支持通过GraalVM将 Kafka Streams 管道作为本机二进制文件执行,从而显着减少内存消耗和启动时间
图片来自于原文
在这里插入图片描述

变更事件概述
此图显示了我们解决方案的概述。

使用 Quarkus Kafka Streams 扩展创建应用程序
要使用 Kafka Streams 扩展创建新的 Quarkus 项目,请运行以下命令:

mvn io.quarkus:quarkus-maven-plugin:1.12.2.Final:创建
-DprojectGroupId=org.acme
-DprojectArtifactId=客户地址聚合器
-Dextensions =“卡夫卡流”
cd 客户地址聚合器
了解流处理拓扑
我们有一个聚合器应用程序,它将读取两个 Kafka 主题并在流式管道中处理它们:

这两个主题通过客户 ID 连接起来

每个客户都拥有丰富的地址

该聚合数据被写入第三个主题,customersWithAddressesTopic

当使用 Kafka Streams 的 Quarkus 扩展时,我们所需要做的就是声明一个CDI 生产者方法,该方法返回流处理应用程序的拓扑。该方法必须用 注释@Produces,并且它必须返回一个Topology实例。Quarkus 扩展负责配置、启动和停止 Kafka Streams 引擎。现在让我们看一下实际的流查询实现本身。

@ApplicationScoped
public class TopologyProducer {

@ConfigProperty(name = "customers.topic") 
String customersTopic;

@ConfigProperty(name = "addresses.topic")
String addressesTopic;

@ConfigProperty(name = "customers.with.addresses.topic")
String customersWithAddressesTopic;

@Produces
public Topology buildTopology() {
    StreamsBuilder builder = new StreamsBuilder(); 

    Serde<Long> adressKeySerde = DebeziumSerdes.payloadJson(Long.class);
    adressKeySerde.configure(Collections.emptyMap(), true);
    Serde<Address> addressSerde = DebeziumSerdes.payloadJson(Address.class);
    addressSerde.configure(Collections.singletonMap("from.field", "after"), false);

    Serde<Integer> customersKeySerde = DebeziumSerdes.payloadJson(Integer.class);
    customersKeySerde.configure(Collections.emptyMap(), true);
    Serde<Customer> customersSerde = DebeziumSerdes.payloadJson(Customer.class);
    customersSerde.configure(Collections.singletonMap("from.field", "after"), false);

    JsonbSerde<AddressAndCustomer> addressAndCustomerSerde =
            new JsonbSerde<>(AddressAndCustomer.class); 
    JsonbSerde<CustomerWithAddresses> customerWithAddressesSerde =
            new JsonbSerde<>(CustomerWithAddresses.class);

    KTable<Long, Address> addresses = builder.table( 
            addressesTopic,
            Consumed.with(adressKeySerde, addressSerde)
    );

    KTable<Integer, Customer> customers = builder.table(
            customersTopic,
            Consumed.with(customersKeySerde, customersSerde)
    );

    KTable<Integer, CustomerWithAddresses> customersWithAddresses = addresses.join( 
            customers,
            address -> address.customer_id,
            AddressAndCustomer::new,
            Materialized.with(Serdes.Long(), addressAndCustomerSerde)
        )
        .groupBy( 
            (addressId, addressAndCustomer) -> KeyValue.pair(
                    addressAndCustomer.customer.id, addressAndCustomer),
            Grouped.with(Serdes.Integer(), addressAndCustomerSerde)
        )
        .aggregate( 
            CustomerWithAddresses::new,
            (customerId, addressAndCustomer, aggregate) -> aggregate.addAddress(
                    addressAndCustomer),
            (customerId, addressAndCustomer, aggregate) -> aggregate.removeAddress(
                    addressAndCustomer),
            Materialized.with(Serdes.Integer(), customerWithAddressesSerde)
        );

    customersWithAddresses.toStream() 
    .to(
            customersWithAddressesTopic,
            Produced.with(Serdes.Integer(), customerWithAddressesSerde)
    );

    return builder.build();
}

}
主题名称使用MicroProfile Config API注入,值在 Quarkus 配置文件中提供application.properties(例如,可以使用环境变量覆盖它们)
创建 的实例StreamsBuilder,这有助于我们构建拓扑
为了将流管道中使用的 Java 类型序列化为 JSON 或从 JSON 序列化和反序列化,Quarkus 提供了class io.quarkus.kafka.client.serialization.JsonbSerde; Serde实现基于JSON-B
KTable-外键连接功能KTable用于提取customer#id并执行连接;StreamsBuilder#table()用于将两个Kafka主题分别读入KTableaddresses和中customers
来自主题的消息addresses与相应的主题连接customers;连接结果包含客户的数据及其地址之一
groupBy()操作将对记录进行分组customer#id
为了生成一个客户及其所有地址的嵌套结构,该aggregate()操作应用于每组记录(客户地址元组),更新每个CustomerWithAddresses客户的
管道的结果写出到customersWithAddressesTopic主题
当事件在流管道中处理时,该类CustomerWithAddresses会跟踪聚合值。

public class CustomerWithAddresses {

public Customer customer;
public List<Address> addresses = new ArrayList<>();

public CustomerWithAddresses addAddress(AddressAndCustomer addressAndCustomer) {

    customer = addressAndCustomer.customer;
    addresses.add(addressAndCustomer.address);

    return this;
}

public CustomerWithAddresses removeAddress(AddressAndCustomer addressAndCustomer) {

    Iterator<Address> it = addresses.iterator();
    while (it.hasNext()) {
        Address a = it.next();
        if (a.id == addressAndCustomer.address.id) {
            it.remove();
            break;
        }
    }

    return this;
}

}
Kafka Streams 扩展是通过 Quarkus 配置文件配置的application.properties。除了主题名称之外,此文件还包含有关 Kafka 引导服务器和多个流选项的信息:

customers.topic=dbserver1.inventory.customers
addresses.topic=dbserver1.inventory.addresses
customers.with.addresses.topic=customers-with-addresses

quarkus.kafka-streams.bootstrap-servers=localhost:9092
quarkus.kafka-streams.application-id=kstreams-fkjoin-aggregator
quarkus.kafka-streams.application-server= h o s t n a m e : 8080 q u a r k u s . k a f k a − s t r e a m s . t o p i c s = {hostname}:8080 quarkus.kafka-streams.topics= hostname:8080quarkus.kafkastreams.topics={customers.topic},${addresses.topic}

streams options

kafka-streams.cache.max.bytes.buffering=10240
kafka-streams.commit.interval.ms=1000
kafka-streams.metadata.max.age.ms=500
kafka-streams.auto.offset.reset=earliest
kafka-streams.metrics.recording.level=DEBUG
kafka-streams.consumer.session.timeout.ms=150
kafka-streams.consumer.heartbeat.interval.ms=100
构建并运行应用程序
您现在可以像这样构建应用程序:

mvn清理包
为了运行应用程序和所有相关组件(Kafka、Kafka Connect 与 Debezium、Postgres 数据库),我们创建了一个Docker Compose 文件,您可以在debezium-examples存储库中找到该文件。要启动所有容器,同时构建聚合器容器映像,请运行以下命令:

导出 DEBEZIUM_VERSION=1.4

docker-compose up --build
要将 Debezium 连接器注册到 Kafka Connect,您需要指定配置属性,例如连接器名称、数据库主机名、用户、密码、端口、数据库名称等。创建包含以下内容的文件 register - postgres.json :

{
“connector.class”: “io.debezium.connector.postgresql.PostgresConnector”,
“tasks.max”: “1”,
“database.hostname”: “postgres”,
“database.port”: “5432”,
“database.user”: “postgres”,
“database.password”: “postgres”,
“database.dbname” : “postgres”,
“database.server.name”: “dbserver1”,
“schema.include”: “inventory”,
“decimal.handling.mode” : “string”,
“key.converter”: “org.apache.kafka.connect.json.JsonConverter”,
“key.converter.schemas.enable”: “false”,
“value.converter”: “org.apache.kafka.connect.json.JsonConverter”,
“value.converter.schemas.enable”: “false”
}
配置 Debezium 连接器:

http PUT http://localhost:8083/connectors/inventory-connector/config < register-postgres.json
现在运行容器镜像的实例debezium/tooling:

docker run --tty --rm
–network kstreams-fk-join-network
Debezium/工具:1.1
该镜像提供了几个有用的工具,例如kafkacat。在工具容器中,运行 kafkacat 以检查流管道的结果:

kafkacat -b kafka:9092 -C -o 开头 -q
-t 具有地址的客户 | jq .
您应该看到如下所示的记录,每条记录都包含一位客户的所有数据及其所有地址:

{
“addresses”: [
{
“city”: “Hamburg”,
“country”: “Canada”,
“customer_id”: 1001,
“id”: 100001,
“street”: “42 Main Street”,
“zipcode”: “90210”
},
{
“city”: “Berlin”,
“country”: “Canada”,
“customer_id”: 1001,
“id”: 100002,
“street”: “11 Post Dr.”,
“zipcode”: “90211”
}
],
“customer”: {
“email”: “sally.thomas@acme.com”,
“first_name”: “Sally”,
“id”: 1001,
“last_name”: “Thomas”
}
}
获取数据库的 shell,插入、更新或删除一些记录,连接将自动重新处理:

$ docker run --tty --rm -i
–network kstreams-fk-join-network
debezium/tooling:1.1
bash -c ‘pgcli postgresql://postgres:postgres@postgres:5432/postgres’

in pgcli, e.g. to update a customer record:

update inventory.customers set first_name = ‘Sarah’ where id = 1001;
本地运行
Kafka Streams 应用程序可以轻松横向扩展,即负载将在应用程序的多个实例之间共享,每个实例处理输入主题分区的子集。当 Quarkus 应用程序通过 GraalVM 编译为本机代码时,它占用的内存要少得多,并且启动时间非常快。无需担心内存管理,您可以并行启动 Kafka Streams 管道的多个实例。

如果您想在native模式下运行此应用程序,请设置QUARKUS_MODE为native并运行以下命令(确保安装了所需的 GraalVM 工具):

mvn clean 包-Pnative
要了解有关将 Kafka Streams 应用程序作为本机二进制文件运行的更多信息,请参阅参考指南。

关于 Kafka Streams 扩展的更多见解
Quarkus 扩展还可以帮助您解决构建流处理微服务时的一些常见要求。例如,为了在生产中运行 Kafka Streams 应用程序,您可以轻松地为数据管道添加运行状况检查和指标。

Micrometer Metrics提供了有关 Quarkus 应用程序的丰富指标,即通过监视应用程序内部发生的情况及其性能特征。Quarkus 允许您使用 JSON 格式或 OpenMetrics 格式通过 HTTP 公开这些指标。从那里,它们可以被Prometheus等工具抓取并存储以进行分析和可视化。

应用程序启动后,指标将在 下公开q/metrics,默认返回 OpenMetrics 格式的数据:

HELP kafka_producer_node_request_total The total number of requests sent

TYPE kafka_producer_node_request_total counter

kafka_producer_node_request_total{client_id=“kstreams-fkjoin-aggregator-b4ac1384-0e0a-4f19-8d52-8cc1ee4c6dfe-StreamThread-1-producer”,kafka_version=“2.5.0”,node_id=“node–1”,status=“up”,} 83.0

HELP kafka_producer_record_send_rate The average number of records sent per second.

TYPE kafka_producer_record_send_rate gauge

kafka_producer_record_send_rate{client_id=“kstreams-fkjoin-aggregator-b4ac1384-0e0a-4f19-8d52-8cc1ee4c6dfe-StreamThread-1-producer”,kafka_version=“2.5.0”,status=“up”,} 0.0

HELP jvm_gc_memory_allocated_bytes_total Incremented for an increase in the size of the (young) heap memory pool after one GC to before the next

TYPE jvm_gc_memory_allocated_bytes_total counter

jvm_gc_memory_allocated_bytes_total 1.1534336E8

HELP http_requests_total

TYPE http_requests_total counter

http_requests_total{status=“up”,uri="/api/customers",} 0.0

如果您不使用 Prometheus,您还有一些选择,例如 Datadog、Stackdriver 等。有关详细指南,请查看Quarkiverse Extensions。

另一方面,我们有MicroProfile Health规范,它提供有关应用程序活跃度的信息,即表明您的应用程序是否正在运行以及您的应用程序是否能够处理请求。要监控现有 Quarkus 应用程序的运行状况,您可以添加扩展smallrye-health:

mvn quarkus:add-extension -Dextensions=“smallrye-health”
Quarkus 将通过 HTTP 公开所有健康检查q/health,在我们的例子中显示管道的状态和任何缺失的主题:

{
“status”: “DOWN”,
“checks”: [
{
“name”: “Kafka Streams topics health check”,
“status”: “DOWN”,
“data”: {
“missing_topics”: “dbserver1.inventory.customers,dbserver1.inventory.addresses”
}
}
]
}
概括
Kafka Streams 的 Quarkus 扩展提供了在 JVM 上以及本机模式下运行流处理管道所需的一切,以及执行运行状况检查、指标等的额外好处。例如,您可以使用 Quarkus REST 支持轻松公开用于交互式查询的 REST API,并可能使用 MicroProfile REST 客户端 API从横向扩展的 Kafka Streams 应用程序的其他实例中检索数据。

在本文中,我们讨论了 Kafka Streams 中外键联接的流处理拓扑,以及如何使用 Quarkus Kafka Streams 扩展在 JVM 模式下运行和构建应用程序。您可以在 Debezium 示例存储库中找到实现的完整源代码。如果您有任何问题或反馈,请在下面的评论中告诉我们。我们期待您的建议!

参考
使用 Quarkus 构建 Kafka Streams 应用程序

使用 Debezium 和 Kafka Streams 更改数据捕获管道

千分尺应用监视器

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

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

相关文章

文心一言用户规模破亿 /微软宣布:支付费用延长旧版Windows 10使用寿命|魔法半周报

我有魔法✨为你劈开信息大海❗ 高效获取AIGC的热门事件&#x1f525;&#xff0c;更新AIGC的最新动态&#xff0c;生成相应的魔法简报&#xff0c;节省阅读时间&#x1f47b; &#x1f525;资讯预览 百度大模型重要突破&#xff0c;文心一言用户规模破亿&#xff0c;飞桨开发者…

LeetCode、198. 打家劫舍【中等,一维线性DP】

文章目录 前言LeetCode、198. 打家劫舍【中等&#xff0c;一维线性DP】题目及分类思路线性DP&#xff08;一维&#xff09; 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注…

Netty源码 之 bind绑定流程

1.Netty框架总览 Netty是一个基于NIO异步通信框架 Netty框架是由许多组件&#xff0c;优化的数据结构所构建成。 正是通过灵活的组件构建&#xff0c;优化后的数据结构&#xff0c;进而才能保证Netty框架面对高并发场景具有一定的能力 1.1 Netty相关组件 Netty重要的组件有…

SpringBoot中使用Spring自带线程池ThreadPoolTaskExecutor与Java8CompletableFuture实现异步任务示例

场景 关于线程池的使用&#xff1a; Java中ExecutorService线程池的使用(Runnable和Callable多线程实现)&#xff1a; Java中ExecutorService线程池的使用(Runnable和Callable多线程实现)_executorservice executorservice executors.newfix-CSDN博客 Java中创建线程的方式…

Unity_ShaderGraph节点问题

Unity_ShaderGraph节点问题 Unity版本&#xff1a;Unity2023.1.19 为什么在Unity2023.1.19的Shader Graph中找不见PBR Master节点&#xff1f; 以下这个PBR Maste从何而来&#xff1f;

Arthas使用教程—— 阿里开源线上监控诊断产品

文章目录 1 简介2背景3 图形界面工具 arthas 阿里开源3.1 &#xff1a;启动 arthas3.2 help :查看arthas所有命令3.3 查看 dashboard3.4 thread 列出当前进程所有线程占用CPU和内存情况3.5 jvm 查看该进程的各项参数 &#xff08;类比 jinfo&#xff09;3.6 通过 jad 来反编译 …

【Chrono Engine学习总结】1-安装配置与程序运行

本文仅用于个人安装记录。 官方安装教程 https://api.projectchrono.org/8.0.0/tutorial_install_chrono.html Windows下安装 windows下安装就按照教程好了。采用cmake-gui进行配置&#xff0c;建议首次安装只安装核心模块。然后依此configure下irrlicht&#xff0c;sensor…

JVM 性能调优 - 参数调优(3)

查看 JVM 内存的占用情况 编写代码 package com.test;public class PrintMemoryDemo {public static void main(String[] args) {// 堆内存总量long totalMemory Runtime.getRuntime().totalMemory();// jvm 试图使用的最大堆内存long maxMemory Runtime.getRuntime().maxM…

国内游戏服务器价格表

游戏服务器租用多少钱一年&#xff1f;1个月游戏服务器费用多少&#xff1f;阿里云游戏服务器26元1个月、腾讯云游戏服务器32元&#xff0c;游戏服务器配置从4核16G、4核32G、8核32G、16核64G等配置可选&#xff0c;可以选择轻量应用服务器和云服务器&#xff0c;阿腾云atengyu…

PHP客服系统-vue客服聊天系统

PHP-Vue客服聊天系统是一款高效、灵活的客户服务解决方案&#xff0c;基于ThinkPHP6、Vue3和Workerman(Gateworker)框架开发&#xff0c;专为单商户场景打造。 系统亮点&#xff1a; 分布式部署支持&#xff0c;轻松应对高并发场景&#xff1b;本地消息存储功能&#xff0c;确…

Python 数据分析(PYDA)第三版(四)

原文&#xff1a;wesmckinney.com/book/ 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 八、数据整理&#xff1a;连接、合并和重塑 原文&#xff1a;wesmckinney.com/book/data-wrangling 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 此开放访问网络版本的…

STM32F1 - 点灯-寄存器模式

点灯 实验概述&#xff1a;Step1> 建立工程Step2> 宏定义 - 寄存器地址 实验概述&#xff1a; 用配置寄存器的方式&#xff0c;开关一个LED灯&#xff0c; 只用标准库中提供的启动文件&#xff0c; Step1> 建立工程 出现错误&#xff1a;导入文件类型错误 keil5编译中…

QMUI_Android:提升Android开发效率与质量的利器

QMUI_Android&#xff1a;提升Android开发效率与质量的利器 在Android应用开发过程中&#xff0c;开发者常常面临着重复编写基础组件和处理兼容性问题的挑战&#xff0c;这不仅耗费时间&#xff0c;也降低了开发效率。为了解决这一问题&#xff0c;Tencent推出了QMUI_Android框…

如何使用Python + 百度翻译API 自动大批量免费翻译Excel文件中的外语内容

手里有一个Excel文件,包括了大量的亚马逊德语搜索词(关键词),每个单元格1个,需要翻译为中文。但是文件大小超过了10M,不能使用百度或Google免费的文档功能,如果手工一个个的翻译然后粘贴又太麻烦,于是想到用Python加免费翻译API完成。 一、openpyxl库 用Python编辑处…

【数据结构】排序之冒泡排序和快速排序

简单不先于复杂&#xff0c;而是在复杂之后。 文章目录 1. 交换排序1.1 冒泡排序1.2 快速排序1.3 快速排序优化1.4 快速排序非递归 1. 交换排序 基本思想&#xff1a;所谓交换&#xff0c;就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置&#xff0c;交换…

day44_jdbc

今日内容 0 复习昨日 1 讲作业 2 数据库连接池(druid) 3 反射 4 改造DBUtil 5 完成CRUD练习 0 复习昨日 1 sql注入 2 预处理语句 3 事务操作 4 DBUtil 1 作业【重要】 利用ORM完成,以下的几个方法非常重要,将来写项目就是这些操作 写项目步骤 搭建环境 创建项目导入依赖工具类数…

《动手学深度学习(PyTorch版)》笔记7.4

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过&…

前端学习第四天

目录 一、复合选择器 1.后代选择器 2.子代选择器 3.并集选择器 4.交集选择器 5.伪类选择器 1.伪类-超链接&#xff08;拓展&#xff09; 二、CSS特性 1.继承性 2.层叠性 3.优先级 1.优先级-叠加计算规则 2.emmet写法 三、背景属性 1.背景图 ​编辑2.背景图平铺方…

【知识整理】一文理解系统服务高可用

一、如何理解高可用 1、什么是高可用 高可用性&#xff08;英语&#xff1a; High Availability&#xff0c;缩写为 HA&#xff09;&#xff0c;指系统无中断地执行其功能的能力&#xff0c;代表系统的可用性程度&#xff0c;是进行系统设计时的准则之一。 2、决定可用性的两…

指针进阶(上)

二级指针 二级指针是用来存放一级指针地址。 如何使用和解引用呢&#xff1f; #include <stdio.h>int main() {int a 5;int* p &a;int** p2 &p;**p2 10;printf("%d\n", a);return 0; }这里的解引用使用两颗星号的原因是&#xff1a;一个星号找到…