当前位置: 首页 > news >正文

Apche Kafka + Spring的消息监听容器

目录

  • 一、消息的接收
    • 1.1、消息监听器
  • 二、消息监听容器
    • 2.1、 实现方法
      • 2.1.1、KafkaMessageListenerContainer
        • 2.1.1.1、 基本概念
        • 2.1.1.2、如何使用 KafkaMessageListenerContainer
      • 2.1.2、ConcurrentMessageListenerContainer
    • 三、偏移
  • 四、监听器容器自动启动

一、消息的接收

消息的接收:可以通过配置MessageListenerContainer并提供消息侦听器或使用@KafkaListener注释来接收消息。本章我们主要说明通过配置MessageListenerContainer并提供消息侦听器的方式接收消息。

1.1、消息监听器

当使用消息监听容器时,就必须提供一个监听器来接收数据。目前有八个支持消息侦听器的接口:

public interface MessageListener<K, V> { // 当使用自动提交或容器管理的提交方法之一时,使用此接口处理从 Kafka 消费者 poll() 操作接收到的各个 ConsumerRecord 实例。void onMessage(ConsumerRecord<K, V> data);
}public interface AcknowledgingMessageListener<K, V> { // 当使用手动提交方法之一时,使用此接口处理从 Kafka 消费者 poll() 操作接收到的各个 ConsumerRecord 实例。void onMessage(ConsumerRecord<K, V> data, Acknowledgment acknowledgment);
}public interface ConsumerAwareMessageListener<K, V> extends MessageListener<K, V> { // 当使用自动提交或容器管理的提交方法之一时,使用此接口处理从 Kafka 消费者 poll() 操作接收到的各个 ConsumerRecord 实例。提供对 Consumer 对象的访问。void onMessage(ConsumerRecord<K, V> data, Consumer<?, ?> consumer);}public interface AcknowledgingConsumerAwareMessageListener<K, V> extends MessageListener<K, V> { //当使用手动提交方法之一时,使用此接口处理从 Kafka 消费者 poll() 操作接收到的各个 ConsumerRecord 实例。提供对 Consumer 对象的访问。void onMessage(ConsumerRecord<K, V> data, Acknowledgment acknowledgment, Consumer<?, ?> consumer);}public interface BatchMessageListener<K, V> { //当使用自动提交或容器管理的提交方法之一时,使用此接口处理从 Kafka 消费者 poll() 操作接收到的所有 ConsumerRecord 实例。使用此接口时不支持 AckMode.RECORD,因为侦听器会获得完整的批次。void onMessage(List<ConsumerRecord<K, V>> data);}public interface BatchAcknowledgingMessageListener<K, V> { // 当使用手动提交方法之一时,使用此接口处理从 Kafka 消费者 poll() 操作接收到的所有 ConsumerRecord 实例。void onMessage(List<ConsumerRecord<K, V>> data, Acknowledgment acknowledgment);}public interface BatchConsumerAwareMessageListener<K, V> extends BatchMessageListener<K, V> { // 当使用自动提交或容器管理的提交方法之一时,使用此接口处理从 Kafka 消费者 poll() 操作接收到的所有 ConsumerRecord 实例。使用此接口时不支持 AckMode.RECORD,因为侦听器会获得完整的批次。提供对 Consumer 对象的访问。void onMessage(List<ConsumerRecord<K, V>> data, Consumer<?, ?> consumer);}public interface BatchAcknowledgingConsumerAwareMessageListener<K, V> extends BatchMessageListener<K, V> { //当使用手动提交方法之一时,使用此接口处理从 Kafka 消费者 poll() 操作接收到的所有 ConsumerRecord 实例。提供对 Consumer 对象的访问。void onMessage(List<ConsumerRecord<K, V>> data, Acknowledgment acknowledgment, Consumer<?, ?> consumer);}

注意:1、 Consumer对象不是线程安全的;2、不应执行任何Consumer<?, ?>影响消费者位置和/或监听器中已提交偏移量的方法;容器需要管理这些信息。

二、消息监听容器

2.1、 实现方法

MessageListenerContainer 提供了两种实现方式 :
1、KafkaMessageListenerContainer,
2、ConcurrentMessageListenerContainer

2.1.1、KafkaMessageListenerContainer

2.1.1.1、 基本概念

KafkaMessageListenerContainer在单个线程上接收来自所有主题或分区的所有消息。委托ConcurrentMessageListenerContainer给一个或多个KafkaMessageListenerContainer实例以提供多线程消费。

  • 从2.2.7版本开始,可以添加一个记录拦截器(RecordInterceptor)监听器容器;它将在调用侦听器之前调用,以允许检查或修改记录。如果拦截器返回 null,则不会调用侦听器。
  • 从版本 2.7 开始,它具有在侦听器退出后(通常或通过抛出异常)调用的附加方法。
  • 批处理拦截器(BatchInterceptor)为批量监听器(Batch Listeners)提供类似的功能。
  • 此外,ConsumerAwareRecordInterceptor(和 BatchInterceptor)提供对 Consumer<?, ?> 的访问。 例如,这可以用于访问拦截器中的消费者指标。
  • CompositeRecordInterceptor and CompositeBatchInterceptor可以调用多个拦截器。
  • 默认情况下,当使用事务时,拦截器在事务启动后被调用。从版本 2.3.4 开始,可以设置侦听器容器的 interceptBeforeTx 属性在事务开始之前调用拦截器。
  • 从版本 2.3.8、2.4.6 开始,当并发大于 1 时 ConcurrentMessageListenerContainer 支持静态成员资格。 group.instance.id 后缀为 -n ,起始n于1。这与增加 session.timeout.ms 的值 一起可用于减少重新平衡事件,例如,当应用程序实例重新启动时。
  • 静态成员资格是指在提高流应用程序、消费者组和其他构建在组再平衡协议之上的应用程序的可用性。再平衡协议依赖组协调器为组成员分配实体 ID。这些生成的 ID 是短暂的,并且会在成员重新启动和重新加入时发生变化。对于基于消费者的应用程序,这种“动态成员资格”可能会导致在管理操作(例如代码部署、配置更新和定期重新启动)期间将大部分任务重新分配给不同的实例。对于大型状态应用程序,洗牌任务在处理之前需要很长时间才能恢复其本地状态,从而导致应用程序部分或完全不可用。受这一观察的启发,Kafka 的组管理协议允许组成员提供持久的实体 ID。根据这些 ID,组成员资格保持不变,因此不会触发重新平衡。

同样的,拦截器中不应该执行任何影响消费者的位置和/或提交的偏移量的方法,容器需要管理这些信息。

如果拦截器改变了记录(通过创建新记录),则topic、partition和offset必须保持不变,以避免意外的副作用,例如记录丢失。

2.1.1.2、如何使用 KafkaMessageListenerContainer

  • KafkaMessageListenerContainer 构造函数

    public KafkaMessageListenerContainer(ConsumerFactory<K, V> consumerFactory,ContainerProperties containerProperties)
    

    该构造函数接收接收消费者工厂(ConsumerFactory)有关对象中主题和分区以及其他配置的信息。

  • 容器属性(ContainerProperties)包含3个构造函数,下面我们一个一个介绍它们。
    1、以TopicPartitionOffset为参数

    public ContainerProperties(TopicPartitionOffset... topicPartitions)
    

    该构造函数采用一个主题分区偏移量(TopicPartitionOffset)参数数组来显式指示容器要使用哪些分区(使用消费者assign()方法)并带有可选的初始偏移量。默认情况下,正值是绝对偏移量,负值是相对于分区内当前最后一个偏移量。TopicPartitionOffset提供了一个带有附加参数的构造函,boolean如果是true,则在容器启动时相对于该消费者的当前位置初始偏移(正或负)。
    2、以String为参数

    public ContainerProperties(String... topics)
    

    该构造函数采用主题数组,Kafka 根据属性分配分区group.id——在组中分配分区
    3、以Pattern为参数

    public ContainerProperties(Pattern topicPattern)
    

    该构造函数使用正则表达式Pattern来选择主题。

  • 如何将监听器分配给容器
    监听器有了容器也有了,如何将监听器分配给容器呢?。要将 MessageListener 分配给容器,可以在创建 Container 时使用 ContainerProps.setMessageListener 方法:

    ContainerProperties containerProps = new ContainerProperties("topic1", "topic2");
    containerProps.setMessageListener(new MessageListener<Integer, String>() {...
    });
    DefaultKafkaConsumerFactory<Integer, String> cf =new DefaultKafkaConsumerFactory<>(consumerProps());
    KafkaMessageListenerContainer<Integer, String> container =new KafkaMessageListenerContainer<>(cf, containerProps);
    return container;
    

    要注意的是,在创建 DefaultKafkaConsumerFactory 时,使用仅接受上述属性的构造函数意味着从配置中选取键和值反序列化器类。 或者,反序列化器实例可以传递到 DefaultKafkaConsumerFactory 构造函数以获取键和/或值,在这种情况下,所有消费者共享相同的实例。 另一种选择是提供Supplier(从版本2.3开始),它将用于为每个消费者获取单独的Deserializer实例:

     DefaultKafkaConsumerFactory<Integer, CustomValue> cf =new DefaultKafkaConsumerFactory<>(consumerProps(), null, () -> new      CustomValueDeserializer());KafkaMessageListenerContainer<Integer, String> container =new KafkaMessageListenerContainer<>(cf, containerProps);
    return container;
    

从版本 2.3.5 开始,引入了一个名为authorizationExceptionRetryInterval 的新容器属性。 这会导致容器在从 KafkaConsumer 获取任何 AuthorizationException 后重试获取消息。 例如,当配置的用户被拒绝读取特定主题时,就会发生这种情况。 定义authorizationExceptionRetryInterval应该有助于应用程序在授予适当的权限后立即恢复。

2.1.2、ConcurrentMessageListenerContainer

ConcurrentMessageListenerContainer只有一个构造函数与构造函数类似 KafkaListenerContainer。

public ConcurrentMessageListenerContainer(ConsumerFactory<K, V> consumerFactory,ContainerProperties containerProperties)

它有一个concurrency属性,这个属性的作用是创建几个 KafkaMessageListenerContainer 实例。例如:container.setConcurrency(3) 创建三个 KafkaMessageListenerContainer 实例。

当监听多个主题时,默认的分区分布可能不是我们所期望的。 例如,如果有 3 个主题,每个主题有 5 个分区,并且我们想要使用 concurrency=15,但是我们只会看到 5 个活动使用者,每个使用者从每个主题分配一个分区,而其他 10 个使用者处于空闲状态。 这是因为默认的 Kafka PartitionAssignor 是 RangeAssignor。 对于这种情况,我们需要考虑使用 RoundRobinAssignor,它将分区分配给所有使用者。 然后,为每个消费者分配一个主题或分区。 我们可以在提供给DefaultKafkaConsumerFactory的属性中设置partition.assignment.strategy消费者属性来更改要更改PartitionAssignor。(ConsumerConfigs.PARTITION_ASSIGNMENT_STRATEGY_CONFIG)。
在springboot中可以这样:
spring.kafka.consumer.properties.partition.assignment.strategy=
org.apache.kafka.clients.consumer.RoundRobinAssignor

当使用 TopicPartitionOffset 配置容器属性时,ConcurrentMessageListenerContainer 会在委托 KafkaMessageListenerContainer 实例之间分发 TopicPartitionOffset 实例。

假设提供了 6 个 TopicPartitionOffset 实例,并发度为 3; 每个容器有两个分区。 对于五个
TopicPartitionOffset 实例,两个容器获得两个分区,第三个容器获得一个分区。 如果并发数大于TopicPartition的数量,则降低并发数,使每个容器获得一个分区。

三、偏移

spring提供了几个偏移选项, 如果 enable.auto.commit 消费者属性为 true,Kafka会根据其配置自动提交偏移量。 如果为 false,则容器支持多种 AckMode 设置。 默认 AckMode 为 BATCH。

从版本 2.3 开始,框架将 enable.auto.commit 设置为 false,除非在配置中明确设置。以前,如果未设置该属性,则使用 Kafka 默认值 (true)。

消费者 poll() 方法返回一个或多个 ConsumerRecord。 为每条记录调用 MessageListener。 以下列表描述了容器对每个 AckMode 采取的操作(当未使用事务时):

  • RECORD:当侦听器处理记录后返回时提交偏移量。

  • BATCH:当 poll() 返回的所有记录都已处理完毕时提交偏移量。

  • TIME:当 poll() 返回的所有记录都处理完毕后,只要超过了自上次提交以来的 ackTime,就提交偏移量。

  • COUNT:当 poll() 返回的所有记录都已处理完毕时,提交偏移量,只要自上次提交以来已收到 ackCount 条记录。

  • COUNT_TIME:与 TIME 和 COUNT 类似,但如果任一条件为真,则执行提交。

  • MANUAL:消息侦听器负责acknowledge() 确认。 之后,应用与 BATCH 相同的语义。

  • MANUAL_IMMEDIATE:当侦听器调用 Acknowledgment.acknowledge() 方法时立即提交偏移量。

使用事务(transactions)时,偏移量将发送到事务,语义相当于 RECORD 或 BATCH,具体取决于侦听器类型(记录或批处理)。MANUAL 和 MANUAL_IMMEDIATE 要求侦听器是 AcknowledgingMessageListener 或 BatchAcknowledgingMessageListener。

根据syncCommits容器属性,使用消费者上的commitSync()或commitAsync()方法。 默认情况下,syncCommits 为 true。

作者个人建议建议设置:ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG 为 false。

从版本 2.3 开始,Acknowledgment 接口增加了两个方法 nack(long sleep) 和 nack(int index, long sleep)。 第一个与记录侦听器一起使用,第二个与批处理侦听器一起使用。 为侦听器类型调用错误的方法将引发 IllegalStateException。在此之前他是这样:

public interface Acknowledgment {void acknowledge();}
  • 如果要提交部分批次,使用 nack()。
  • 使用事务时,将 AckMode 设置为 MANUAL;
  • 调用 nack() 会将成功处理的记录的偏移量发送到事务。
  • nack() 只能在调用侦听器的消费者线程上调用。
  • 当调用 nack() 时,将提交所有挂起的偏移量,丢弃上次轮询的剩余记录,并在其分区上执行查找,以便在下一次轮询时重新传递失败的记录和未处理的记录( )。
  • 通过设置 sleep 参数,消费者线程可以在重新交付之前暂停。 这与在容器配置了 SeekToCurrentErrorHandler 时抛出异常的功能类似。

当通过组管理使用分区分配时,确保 sleep 参数(加上处理先前轮询的记录所花费的时间)小于使用者 max.poll.interval.ms属性,这个非常重要

四、监听器容器自动启动

侦听器容器实现 SmartLifecycle,并且 autoStartup 默认为 true。 容器在后期启动 (Integer.MAX-VALUE - 100)。 实现 SmartLifecycle 来处理来自侦听器的数据的其他组件应在早期阶段启动。 -100 为后续阶段留出了空间,使组件能够在容器之后自动启动。

相关文章:

Apche Kafka + Spring的消息监听容器

目录 一、消息的接收1.1、消息监听器 二、消息监听容器2.1、 实现方法2.1.1、KafkaMessageListenerContainer2.1.1.1、 基本概念2.1.1.2、如何使用 KafkaMessageListenerContainer 2.1.2、ConcurrentMessageListenerContainer 三、偏移 四、监听器容器自动启动 一、消息的接收 …...

[JavaWeb]【五】web后端开发-Tomcat SpringBoot解析

目录 一 介绍Tomcat 二 基本使用 2.1 解压绿色版 2.2 启动TOMCAT 2.3 关闭TOMCAT 2.4 常见问题 2.5 修改端口号 2.6 部署应用程序 三 SpringBootWeb入门程序解析 前言&#xff1a;tomcat与SpringBoot解析 一 介绍Tomcat 二 基本使用 2.1 解压绿色版 2.2 启动TOMCAT 2…...

css 用过渡实现,鼠标离开li时,背景色缓慢消息的样式

要实现鼠标悬停时背景颜色变为黄色&#xff0c;鼠标离开时背景颜色慢慢消失并变回白色的效果&#xff0c; 可以使用CSS的过渡&#xff08;transition&#xff09;属性 li {background: #fff;color: #000;transition: background 0.5s ease-out; }li:hover {background: #fbb31…...

pytorch 线性层Linear详解

线性层就是全连接层&#xff0c;以一个输入特征数为2&#xff0c;输出特征数为3的线性层为例&#xff0c;其网络结构如下图所示&#xff1a; 输入输出数据的关系如下&#xff1a; 写成矩阵的形式就是&#xff1a; 下面通过代码进行验证&#xff1a; import torch.nn as nn …...

LeetCode 833. 字符串中的查找与替换

2235. 两整数相加 添加链接描述 给你两个整数 num1 和 num2&#xff0c;返回这两个整数的和。 示例 1&#xff1a; 输入&#xff1a;num1 12, num2 5 输出&#xff1a;17 解释&#xff1a;num1 是 12&#xff0c;num2 是 5 &#xff0c;它们的和是 12 5 17 &#xff0c;…...

Oracle故障案例之-19C时区补丁DSTV38更新

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&#x1f61…...

设计模式之组合模式(Composite)的C++实现

1、组合模式的提出 在软件开发过程中&#xff0c;使用者Client过多依赖所操作对象内部的实现结构&#xff0c;如果对象内部的实现结构频繁发生变化&#xff0c;则使用者的代码结构将要频繁地修改&#xff0c;不利于代码地维护和扩展性&#xff1b;组合模式可以解决此类问题。组…...

mongo的include方法踩坑

前言 又是不认识自己代码的一天 问题 Query query new Query(); if(StringUtils.isNotNull(reqVO.getFieldLimitList()) && reqVO.getFieldLimitList().size() > 0){for(String filedName : reqVO.getFieldLimitList()){query.fields().include(filedName);} }看到…...

阿里云无影云电脑/云桌面收费价格表_使用申请方法

阿里云无影云电脑配置具体收费价格表&#xff0c;4核8G企业办公型云电脑可以免费使用3个月&#xff0c;无影云电脑地域不同价格不同&#xff0c;无影云电脑费用是由云桌面配置、云盘、互联网访问带宽、AD Connector 、桌面组共用桌面session 等费用组成&#xff0c;阿里云百科分…...

jvm内存溢出排查(使用idea自带的内存泄漏分析工具)

文章目录 1.确保生成内存溢出文件2.使用idea自带的内存泄漏分析工具3.具体实验一下 1.确保生成内存溢出文件 想分析堆内存溢出&#xff0c;一定在运行jar包时就写上参数-XX:HeapDumpOnOutOfMemoryError&#xff0c;可以看我之前关于如何运行jar包的文章。若你没有写。可以写上…...

JS内存泄漏

JS内存泄漏 1.意外的全局变量 全局变量的生命周期很长&#xff0c;直到页面关闭&#xff0c;它都存活&#xff0c;所以全局变量上的内存一直都不会被回收 当全局变量使用不当&#xff0c;没有及时回收&#xff08;手动赋值null&#xff09;&#xff0c;或者拼写错误等将某个变…...

线程和进程同步互斥你真的掌握了吗?(同步互斥机制保姆级讲解与应用)

目录 同步互斥的概念 互斥锁 初始化互斥锁 销毁互斥锁 申请上锁 解锁 案例1&#xff1a;没有互斥锁 多任务的运行情况 案例2&#xff1a;有互斥锁 多任务的运行情况 死锁 读写锁 初始化读写锁 销毁读写锁 申请读锁 申请写锁 释放读写锁 案例&#xff1a;两个任务…...

Android 9.0 Vold挂载流程解析(上)

前言 我们分2篇文章来介绍Android 9.0中存储卡的挂载流程&#xff0c;本篇文章先介绍总体的挂载模块、Vold进程的入口main函数的详细分析&#xff0c;有了这些基础知识&#xff0c;下一篇中我们再详细介绍收到驱动层消息是怎么挂载和卸载存储卡的&#xff0c;还有framework层如…...

界面组件Telerik UI for WinForms R2 2023——拥有VS2022暗黑主题

Telerik UI for WinForms拥有适用Windows Forms的110多个令人惊叹的UI控件。所有的UI for WinForms控件都具有完整的主题支持&#xff0c;可以轻松地帮助开发人员在桌面和平板电脑应用程序提供一致美观的下一代用户体验。 Telerik UI for WinForms R2 2023于今年6月份发布&…...

vue+elementui 实现文本超出长度显示省略号,鼠标移上悬浮展示全部内容

一、场景 表单内的输入框一般为固定宽度&#xff0c;当输入框内容长度超出输入框宽度时&#xff0c;需要显示省略号&#xff0c;并设置鼠标移到输入框上时悬浮展示全部内容。 <el-tooltipplacement"top-start"effect"light":content"basicData[Or…...

【STM32RT-Thread零基础入门】 5. 线程创建应用(线程创建、删除、初始化、脱离、启动、睡眠)

硬件&#xff1a;STM32F103ZET6、ST-LINK、usb转串口工具、4个LED灯、1个蜂鸣器、4个1k电阻、2个按键、面包板、杜邦线 文章目录 前言一、线程管理接口介绍二、任务&#xff1a;使用多线程的方式同时实现led闪烁和按键控制喇叭&#xff08;扫描法&#xff09;1. RT-Thread相关接…...

计算机竞赛 python+深度学习+opencv实现植物识别算法系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的植物识别算法研究与实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;4分 &#x1f9ff; 更多…...

深度探索ChatGPT:如何进行专业提问以获取精确答案

ChatGPT&#xff0c;作为OpenAI的先锋&#xff0c;已经展示出其惊人的交流和理解能力。但如何才能充分利用其潜能&#xff0c;并与之进行更深入、更专业的交流呢? 下面&#xff0c;我们将从专业的角度探讨一些提问策略&#xff0c;并附上实际案例&#xff0c;让你更加熟练地与…...

1.vue3+vite开发中axios使用及跨域问题解决

一、跨域问题解决 1.基于vitevue3配置时&#xff0c;在vite.congig.js文件server项目中添加 proxy代理 文件名&#xff1a;vite.congig.js server: {open: true,//启动项目自动弹出浏览器port: 3000,proxy: {/api: {target: http://localhost:8000/api/,changeOrigin: true,rew…...

【LangChain】P1 LangChain 应用程序的核心构建模块 LLMChain 以及其三大部分

LangChain 的核心构建模块 LLMChain LangChain 应用程序的核心构建模块语言模型 - LLMs提示模板 - Prompt templates输出解析器 - Output Parsers LLMChain 组合 LangChain 应用程序的核心构建模块 LangChain 应用程序的核心构建模块 LLMChain 由三部分组成&#xff1a; 语言…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

QT3D学习笔记——圆台、圆锥

类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体&#xff08;对象或容器&#xff09;QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质&#xff08;定义颜色、反光等&#xff09;QFirstPersonC…...

Java编程之桥接模式

定义 桥接模式&#xff08;Bridge Pattern&#xff09;属于结构型设计模式&#xff0c;它的核心意图是将抽象部分与实现部分分离&#xff0c;使它们可以独立地变化。这种模式通过组合关系来替代继承关系&#xff0c;从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

GraphQL 实战篇:Apollo Client 配置与缓存

GraphQL 实战篇&#xff1a;Apollo Client 配置与缓存 上一篇&#xff1a;GraphQL 入门篇&#xff1a;基础查询语法 依旧和上一篇的笔记一样&#xff0c;主实操&#xff0c;没啥过多的细节讲解&#xff0c;代码具体在&#xff1a; https://github.com/GoldenaArcher/graphql…...

Linux 下 DMA 内存映射浅析

序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存&#xff0c;但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程&#xff0c;可以参考这篇文章&#xff0c;我觉得写的非常…...

leetcode_69.x的平方根

题目如下 &#xff1a; 看到题 &#xff0c;我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历&#xff0c;我们是整数的平方根&#xff0c;所以我们分两…...

react更新页面数据,操作页面,双向数据绑定

// 路由不是组件的直接跳转use client&#xff0c;useEffect&#xff0c;useRouter&#xff0c;需3个结合&#xff0c; use client表示客户端 use client; import { Button,Card, Space,Tag,Table,message,Input } from antd; import { useEffect,useState } from react; impor…...