30分钟带你图解 Kafka 生产者初始化核心流程

Kafka指定分区消费及consumer-id,client-id相关概念解析 在最近使用Kafka过程中,发现使用@KafkaListener指定分区消费时(指定了所有分区),如果服务是多节点,会出现重复消费的现象,即两个服务节点中的消费者均会消费到相同信息,这与消费者组中只有一个消费者可以消费到消息的规则不相符,于是花时间找了找原因Consumer 机制小龙虾你抓不到(上面博主的专栏)Kafka的assign和subscribe订阅模式使用kafka-consumer-group.sh查看消息消费情况,CONSUMER-ID,HOST,CLIENT-ID不显示问题分析。 阅读详情

认真读完这篇文章,我相信你会对 Kafka 生产初始化源码有更加深刻的理解。

这篇文章干货很多,希望你可以耐心读完。

 

01 总体概述

我们都知道在 Kafka 中,我们把产生消息的一方称为生产者即 Producer,它是 Kafka 核心组件之一,也是消息的来源所在。那么这些生产者产生的消息是如何传到 Kafka 服务端的呢?初始化过程是怎么样的呢?接下来会逐一讲解说明。

02 生产者初始化核心组件及流程剖析

我们先从生产者客户端构造 KafkaProducer 开始讲起:

Properties properties = new Properties();

//构造 KafkaProducer

KafkaProducer producer = new KafkaProducer(properties);

//调用send异步回调发送

producer.send(record,new DemoCallBack(record.topic(), record.key(), record.value()));

上面代码主要做了2件事情:

1)初始化 KafkaProducer 实例 。

2)调用 send 接口发送数据,支持同步和异步回调方式 。

待构造完 KafkaProducer 就正式进入生产者源码的入口了,如下图所示:

 

接下来我们分析一下 KafkaProducer 的源码, 先看下该类里面的 「 重要字段 」 :

public class KafkaProducer<K, V> implements Producer<K, V> {

    private final Logger log;

    private static final String JMX_PREFIX = "kafka.producer";

    public static final String NETWORK_THREAD_PREFIX = "kafka-producer-network-thread";

    public static final String PRODUCER_METRIC_GROUP_NAME = "producer-metrics";

    // 生产者客户端Id

    private final String clientId;

    // 消息分区器

    private final Partitioner partitioner;

    // 消息的最大的长度,默认1M,生产环境可以提高到10M

    private final int maxRequestSize;

    // 发送消息的缓冲区的大小,默认32M

    private final long totalMemorySize;

    // 集群元数据

    private final ProducerMetadata metadata;

    // 消息累加器

    private final RecordAccumulator accumulator;

    // 执行发送消息的类

    private final Sender sender;

    // 执行发送消息的线程

    private final Thread ioThread;

    // 消息压缩类型

    private final CompressionType compressionType;

    // key的序列化器

    private final Serializer<K> keySerializer;

    // value的序列化器

    private final Serializer<V> valueSerializer;

    // 生产者客户端参数配置

    private final ProducerConfig producerConfig;

    // 等待元数据更新的最大时间,默认1分钟

    private final long maxBlockTimeMs;

    // 生产者拦截器

    private final ProducerInterceptors<K, V> interceptors;

    // api版本

    private final ApiVersions apiVersions;

    // 事务管理器

    private final TransactionManager transactionManager;

    ........

}

 

重要且核心字段含义如下:

1) clientId: 生产者客户端的ID。

2) partitioner: 消息的分区器,即通过某些算法将消息分配到某一个分区中。

3) maxRequestSize: 消息的最大的长度,默认1M,生产环境可以提高到10M。

4) totalMemorySize: 发送消息的缓冲区的大小,默认32M。

5) metadata: 集群的元数据。

6) accumulator: 消息累加器,主要负责缓冲消息。

7) sender: 执行发送消息的类型,主要负责发送消息。

8) ioThread: 执行发送消息的线程,主要负责封装Sender类。

9) compressionType: 消息压缩的类型。

10) keySerializer: key的序列化器。

11) valueSerializer: value的序列化器。

12) producerConfig :生产者客户端的配置参数。

13) maxBlockTimeMs: 等待元数据更新和缓冲区分配的最长时间,默认1分钟。

14) interceptors: 生产者拦截器。主要负责在消息发送前后对消息进行拦截和处理。

接下来我们看下KafkaProducer的构造方法,来剖析生产者发送消息的过程中涉及到的「核心组件」。

源码位置:

kafka\clients\src\main\java\org\apache\kafka\clients\producer\KafkaProducer.java 323行

public class KafkaProducer<K, V> implements Producer<K, V> {

......

KafkaProducer(Map<String, Object> configs,

                  Serializer<K> keySerializer,

                  Serializer<V> valueSerializer,

                  ProducerMetadata metadata,

                  KafkaClient kafkaClient,

                  ProducerInterceptors<K, V> interceptors,

                  Time time) {

  // 1.生产者配置初始化

  ProducerConfig config = new ProducerConfig(ProducerConfig.appendSerializerToConfig(configs, keySerializer,

                valueSerializer));

  try {

     // 2.获取客户端配置参数

     Map<String, Object> userProvidedConfigs = config.originals();

     this.producerConfig = config;

     this.time = time;

     // 3.用于事务传递的TransactionalId,保证会话的可靠性,如果配置表示启用幂等+事务

     String transactionalId = (String) userProvidedConfigs.get(ProducerConfig.TRANSACTIONAL_ID_CONFIG);

     // 4.设置生产者客户端id

     this.clientId = config.getString(ProducerConfig.CLIENT_ID_CONFIG);

     LogContext logContext;

     // 根据事务id是否配置来记录不同日志

     if (transactionalId == null)

        logContext = new LogContext(String.format("[Producer clientId=%s] ", clientId));

     else

        logContext = new LogContext(String.format("[Producer clientId=%s, transactionalId=%s] ", clientId, transactionalId));

     log = logContext.logger(KafkaProducer.class);

     log.trace("Starting the Kafka producer");

     ........省略Metrics

     // 5.设置对应的分区器

     this.partitioner = config.getConfiguredInstance(ProducerConfig.PARTITIONER_CLASS_CONFIG, Partitioner.class);

     // 6.失败重试的退避时间,配置参数:retry.backoff.ms 默认100ms

     long retryBackoffMs = config.getLong(ProducerConfig.RETRY_BACKOFF_MS_CONFIG);

     // 7.定义key、value对应的序列化器

     if (keySerializer == null) {

        this.keySerializer = config.getConfiguredInstance(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,

        this.keySerializer.configure(config.originals(Collections.singletonMap(ProducerConfig.CLIENT_ID_CONFIG, clientId)), true);

     } else {

        config.ignore(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG);

        this.keySerializer = keySerializer;

     }

     if (valueSerializer == null) {

        this.valueSerializer = config.getConfiguredInstance(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,

        this.valueSerializer.configure(config.originals(Collections.singletonMap(ProducerConfig.CLIENT_ID_CONFIG, clientId)), false);

     } else {

        config.ignore(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG);

        this.valueSerializer = valueSerializer;

     }

     // load interceptors and make sure they get clientId

     userProvidedConfigs.put(ProducerConfig.CLIENT_ID_CONFIG, clientId);

     ProducerConfig configWithClientId = new ProducerConfig(userProvidedConfigs, false);

     // 8.定义生产者拦截器列表

     List<ProducerInterceptor<K, V>> interceptorList = (List) configWithClientId.getConfiguredInstances(

           ProducerConfig.INTERCEPTOR_CLASSES_CONFIG, ProducerInterceptor.class);

     if (interceptors != null)

         this.interceptors = interceptors;

     else

         this.interceptors = new ProducerInterceptors<>(interceptorList);

     ClusterResourceListeners clusterResourceListeners = configureClusterResourceListeners(keySerializer,

            valueSerializer, interceptorList, reporters);

     // 9.设置消息的最大的长度,默认1M,生产环境可以提高到10M

     this.maxRequestSize = config.getInt(ProducerConfig.MAX_REQUEST_SIZE_CONFIG);

     // 10.设置发送消息的缓冲区的大小,默认32M

     this.totalMemorySize = config.getLong(ProducerConfig.BUFFER_MEMORY_CONFIG);

     // 11.设置消息压缩类型

     this.compressionType = CompressionType.forName(config.getString(ProducerConfig.COMPRESSION_TYPE_CONFIG));

     // 12.设置等待元数据更新的最大时间,默认1分钟

     this.maxBlockTimeMs = config.getLong(ProducerConfig.MAX_BLOCK_MS_CONFIG);

     // 13.设置消息投递的超时时间

     int deliveryTimeoutMs = configureDeliveryTimeout(config, log);

     this.apiVersions = new ApiVersions();

     // 事务管理器

     this.transactionManager = configureTransactionState(config, logContext);

     ....省略,看下面各小节源码

     config.logUnused();

     AppInfoParser.registerAppInfo(JMX_PREFIX, clientId, metrics, time.milliseconds());

     log.debug("Kafka producer started");

  } catch (Throwable t) {

     // call close methods if internal objects are already constructed this is to prevent resource leak. see KAFKA-2121

     close(Duration.ofMillis(0), true);

     // now propagate the exception

     throw new KafkaException("Failed to construct kafka producer", t);

  }

 }

}

下面通过一张图来描述 KafkaProducer 的 初始化源码的过程:

 

Kafka Producer 初始化核心组件如下:

1)初始化生产者配置 (ProducerConfig) 。

2)设置客户端配置文件的配置信息 (userProvidedConfigs) 。

3)设置事务ID (transactionaID) 。

4)设置生产者客户端ID (clientId) 。

5)设置对应的分区器 (partitioner) , 支持自定义,用来将消息分配给某个主题的某个分区的。

6)设置失败重试的退避时间 (retryBackoffMs) 。在客户端请求服务端时,可能因为网络或服务端异常造成请求超时。这时请求失败会重试,但是如果重试的频率过高又可能造成服务端网络拥堵。因此必须等一段时间再请求,默认100ms。

7)初始化key的序列化器 (keySerializer) 和value的序列化器 (valueSerializer) 。key和value的序列化器是用户在初始化 KafkaProducer 的时候自定义的。

8)设置生产者拦截器 (ProducerInterceptor) ,拦截器的主要作用是按照一定的规则统一对消息进行处理。

9)设置 消息的最大的长度 (maxRequestSize) 。默认是1M,超了会报异常。在生产环境中建议设置为10M。

10)设置发送消息的缓冲区的大小 (totalMemorySize) ,默认是32M。

11)设置消息压缩的类型 (compressionType) 。默认是none表示不压缩。在消息发送的过程中,为了提升发送消息的吞吐量会把消息进行压缩再发送。

12)设置等待元数据更新和缓冲区分配的最长时间 (maxBlockTimeMs) ,默认60S。

13)设置消息投递超时时间 (deliveryTimeoutMs) ,默认120S。消息投递时间是从发送到收到响应的时间。

我们分析了KafkaProducer的核心组件,接下来我们分析下初始化过程中的核心流程。

01

初始化消息累加器

// 初始化消息累加器---缓冲区

this.accumulator = new RecordAccumulator(logContext,

   config.getInt(ProducerConfig.BATCH_SIZE_CONFIG),

   this.compressionType,

   lingerMs(config),

   retryBackoffMs,

   deliveryTimeoutMs,

   metrics,

   PRODUCER_METRIC_GROUP_NAME,

   time,

   apiVersions,

   transactionManager,

   new BufferPool(this.totalMemorySize, config.getInt(ProducerConfig.BATCH_SIZE_CONFIG), metrics, time, PRODUCER_METRIC_GROUP_NAME));

初始化消息累加器对象 「 accumulator 」 ,部分重要参数如下:

1) batchSize : 消息批次大小,默认16KB;

2) compressionType: 消息压缩方式,主要包括none、gzip、snappy、lz4、zstd。默认是不进行压缩,如果你的 Topic 占用的磁盘空间比较多的话,可以考虑启用压缩,以节省资源。

3) lingerMs: 消息 batch 延迟多久再发送的时间,这是吞吐量与延时之间的权衡。为了不频繁发送网络请求,设置延迟时间后 batch 会尽量积累更多的消息再发送出去。

4) retryBackoffMs: 设置失败重试的退避时间。

5) deliveryTimeoutMs: 设置消息投递超时时间。

6) apiVersion: 客户端 api版本。

7) transactionalManager : 事务管理器。

8) BufferPool 分配 : 后续篇在进行深度剖析。

消息累加器---缓冲区的设计是 Kafka Producer 非常优秀和经典的设计 ,Kafka 中消息不是生产后立马就发送给服务端的,而是 会先写入一个缓冲池中,然后直到多条消息组成了一个 Batch,达到一定条件才会一次网络通信把 Batch 发送过去 , 利用该设计来避免 JVM 频繁的 Full GC 的问题 ,后续会单独对其进行深度剖析。

02

初始化集群元数据

元数据的获取涉及的组件比较多,主要分为:

1)KafkaProducer 主线程负责加载元数据 。

2)Sender 子线程负责拉取元数据 。

首先我们来看下 KafkaProducer 主线程是如何加载元数据。

元数据 「 metadata 」 的初始化的时候是在 KafkaProducer 主线程里面的,源代码如下:

// 初始化 Kafka 集群元数据,元数据会保存到客户端中,并与服务端元数据保持一致

if (metadata != null) {

    this.metadata = metadata;

} else {

    // 初始化集群元数据

    this.metadata = new ProducerMetadata(retryBackoffMs,

          // 元数据过期时间:默认5分钟

          config.getLong(ProducerConfig.METADATA_MAX_AGE_CONFIG),

          // topic最大空闲时间,如果在规定时间没有被访问,将从缓存删除,下次访问时强制获取元数据

          config.getLong(ProducerConfig.METADATA_MAX_IDLE_CONFIG),

          logContext,

          clusterResourceListeners,

          Time.SYSTEM);

    // 启动metadata的引导程序    

    this.metadata.bootstrap(addresses);

}

它会保存在客户端内存中,并与服务端保持准实时的数据一致性, 元数据 主要包含:

1)正在上传…重新上传取消问答 Kafka 集群节点信息 。

2) Topic 信息 。

3) Topic对应的分区信息

4)ISR列表信息以及分布情况

5)Leader Partition 所在节点

等等

从上面源代码我们可以看出在 KafkaProducer 的构造方法中初始化了元数据类 「 metadata 」 ,然后调用 「 metadata.bootstrap() 」 来启动引导程序,这个时候 metaData 对象里并没有具体的元数据信息,因为客户端还没发送元数据更新的请求 「 获取是通过唤醒 Sender 线程进行发送的 」 。

而具体的发送和拉取,我们将在下一篇中进行剖析。

03

初始化 Sender 线程

// 初始化 Sender 发送线程类,并同时初始化NetworkClient

this.sender = newSender(logContext, kafkaClient, this.metadata);    

这里非常关键,初始化 「 Sender 」 发送线程类,并同时初始化 「 NetworkClient 」 ,它为 sender 提供了网络IO的能力,后续我们会对其深度剖析。

04

ioThread 启用 Sender 线程

String ioThreadName = NETWORK_THREAD_PREFIX + " | " + clientId;

// 用 ioThread 线程来封装 Sender 线程类,使用 demon 守护线程方式来启动 Sender 线程类

this.ioThread = new KafkaThread(ioThreadName, this.sender, true);

this.ioThread.start();




public KafkaThread(final String name, Runnable runnable, boolean daemon) {

   super(runnable, name);

   configureThread(name, daemon);

}




private void configureThread(final String name, boolean daemon) {

    setDaemon(daemon);

    setUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception in thread '{}':", name, e));

}

 

从上面源代码可以看出使用 「 ioThread 」 线程来封装 「 Sender 」 线程类,并使用 demon 守护线程方式来启动 Sender 线程类。

这里的设计模式非常值得我们去学习,就是在设计一些后台线程的时候,可以把 「 线程 本身 」 和 「 线程执行 」 的逻辑分开,Sender 线程就是线程执行的具体逻辑,而 KafkaThread 其实代表了这个 「 线程本身 」 、 「 线程的名字 」 、 「 未捕获的异常处理 」 , 「 deamon 线程设置 」 。 对 Kafka Thread 的启动会自动执行 Sender 线程的 Run() 方法。

05

doSend 发送

用户可以直接使用 「 producer.send() 」 进行数据的发送,先看一下 「 Send() 」 接口的源码实现。

// 向 topic 异步发送数据,此时回调为空

public Future<RecordMetadata> send(ProducerRecord<K, V> record) {

    return send(record, null);

}




// 向 topic 异步地发送数据,当发送确认后唤起回调函数

public Future<RecordMetadata> send(ProducerRecord<K, V> record, Callback callback) {

   // intercept the record, which can be potentially modified; this method does not throw exceptions

   ProducerRecord<K, V> interceptedRecord = this.interceptors.onSend(record);

   return doSend(interceptedRecord, callback);

}

生产环境我们一般会使用带回调函数的方式去发送,所以最终实现还是调用了 KafkaProducer 的 「 doSend() 」 接口。

该方法只是把消息发送到缓冲区后直接返回,真正的发送是需要等待 Sender 线程把消息从缓冲区将消息取出来后再进行发送。

源码比较长,这里只简单的分析下都做了哪些事情,后续再进行深度剖析,源码如下 :

private Future<RecordMetadata> doSend(ProducerRecord<K, V> record, Callback callback) {

   TopicPartition tp = null;

   try {

     ....省略

     // 1.等待元数据更新即确认数据要发送到的 topic 的 metadata 是可用的

     clusterAndWaitTime = waitOnMetadata(record.topic(), record.partition(), nowMs, maxBlockTimeMs);

     ....省略

     // 2.序列化 record的key和value

     byte[] serializedKey;

     serializedKey = keySerializer.serialize(record.topic(), record.headers(), record.key());

     byte[] serializedValue;

     serializedValue = valueSerializer.serialize(record.topic(), record.headers(), record.value());

     // 3.获取record消息对应的分区       

     int partition = partition(record, serializedKey, serializedValue, cluster);

     tp = new TopicPartition(record.topic(), partition);

     ....省略

     // 4.验证消息的大小

     ensureValidRecordSize(serializedSize);

     // 5.组装回调方法和拦截器为一个对象

     Callback interceptCallback = new InterceptorCallback<>(callback, this.interceptors, tp);

     ....省略

     // 6.向 accumulator 中追加数据

     RecordAccumulator.RecordAppendResult result = accumulator.append(tp, timestamp, serializedKey,

                    serializedValue, headers, interceptCallback, remainingWaitMs, true, nowMs);

     // 7.新的批次需要重新进行分区

     if (result.abortForNewBatch) {

        int prevPartition = partition;

        partitioner.onNewBatch(record.topic(), cluster, prevPartition);

        partition = partition(record, serializedKey, serializedValue, cluster);

        tp = new TopicPartition(record.topic(), partition);

        // producer callback will make sure to call both 'callback' and interceptor callback

        interceptCallback = new InterceptorCallback<>(callback, this.interceptors, tp);

        result = accumulator.append(tp, timestamp, serializedKey,

                    serializedValue, headers, interceptCallback, remainingWaitMs, false, nowMs);

      }

      // 8.如果 batch 已经满了, 则唤醒 sender 线程发送数据

      if (result.batchIsFull || result.newBatchCreated) {

         log.trace("Waking up the sender since topic {} partition {} is either full or getting a new batch", record.topic(), partition);

         this.sender.wakeup();

      }

      return result.future;

   } catch (ApiException e) {

      ....省略 

      return new FutureFailure(e);

   } catch (InterruptedException e) {

        ....省略

        throw new InterruptException(e);

   } catch (KafkaException e) {

        ....省略

        throw e;

   } catch (Exception e) {

        ....省略

        throw e;

    }

 }

06

整体发送流程

 

03 总结

这里,我们一起来总结一下这篇文章的重点。

1、通过 「 场景驱动 」 的方式从生产者调用出发,抛出初始化和发送的过程是怎样的?

2、带你梳理了 「 Kafka Producer 初始化源码全貌 」,包含主线程的 核心组件模块以及消息累加器的初始化、元数据初始化、 Sender 线程初始化流程。

3、最后通过一张整体发送流程图来勾勒出生产者发送消息的全貌。

kafka2.2源码分析之服务端处理初始化ProducerId请求 概述 control message:因为事务存在commit和abort两种操作,而客户端又有read committed和read uncommitted两种隔离级别,所以消息队列必须能标识事务状态,这个被称作Control Message。 为了区分写入Partition的消息被Commit还是Abort,Kafka引入了一种特殊类型的消息,即Control Message。该类消... 阅读详情

相关推荐

华为OD Java面试全流程与核心考点解析

Java作为企业级开发的主流语言,其技术栈深度与工程实践能力是面试考察的重点。从JVM内存模型到Spring框架原理,再到MySQL索引优化,构建完整的知识体系是应对技术面试的基础。在分布式系统设计中,CAP理论和Redis等中间件的应用成为高频考点。华为OD面试特别注重算法能力,LeetCode中等难度题型如双指针、DFS/BFS模板的掌握程度直接影响机考成绩。项目答辩环节需遵循STAR法则,结合秒杀系统等实战场景展示技术深度。对于Java开发者而言,系统性地准备并发编程、JVM调优等核心知识点,是成功通

weixin_30315723的博客 411

在需要 ClientId 鉴权的 Kafka 集群中,高效使用 Producer 和 Consumer 的方法

ClientId 在 Kafka 中主要用于标识和管理客户端应用程序,以及为监控、日志记录和资源管理提供支持。通过为每个客户端分配唯一的 ClientId,你可以更好地跟踪和管理 Kafka 集群中的各个客户端连接。然而在某些公司,ClientId 是用于鉴权和限流的,因此在使用 Kafka 时需要确保 ClientId 与申请 Topic 时的 ClientId 保持一致。

gelaozideha的博客 1562

PySpark Structured Streaming实时情感分析生产实践

实时情感分析是流式数据处理中的典型低延迟AI应用,其核心在于将自然语言理解能力与持续查询引擎深度集成。原理上需兼顾Kafka消息有序性、Structured Streaming的watermark乱序容忍机制、状态一致性保障及MLlib模型在线推理能力。技术价值体现在秒级业务响应——如客服预警、舆情监控、大促风控等场景中,将用户情绪从‘事后归因’转变为‘事中干预’。本文聚焦PySpark与Kafka协同调优的关键路径,涵盖分区对齐、JSON Schema强校验、pandas_udf向量化清洗、Checkpo

weixin_33724659的博客 373

Kafka知识总结之生产者简单使用

一. 测试环境搭建 引入依赖: <dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka-clients</artifactId> <version>2.8.0</version> </dependency> 创建一个Bean,这里的配置后面会解析。 import org.apache.kafka.clients

学习和工作总结,共勉! 3615

DataHub与Vault集成:5分钟实现动态密钥管理与安全架构升级

在现代数据平台架构中,密钥管理是保障系统安全的核心环节。传统的硬编码或静态配置文件方式存在泄露风险,且难以满足权限审计与动态轮换的需求。其基本原理是通过集中式的密钥存储与动态生成机制,实现凭据的即用即取与生命周期管理。这一技术价值在于将安全边界从应用层前移至基础设施层,遵循最小权限原则,显著降低密钥暴露面。在数据治理、微服务架构及云原生场景中,动态密钥管理已成为最佳实践。本文聚焦于DataHub与HashiCorp Vault的集成,通过AppRole认证、环境变量注入与Kubernetes初始化容器等工程

anqiu4023的博客 303

数据科学实战书单:从入门到架构师的7本关键读物

数据科学不是算法堆砌,而是问题驱动的工程实践。理解统计思维、掌握特征工程、实现模型可解释性、构建数据密集型系统,是从业者能力跃迁的核心路径。本书单聚焦真实工作流中的高频痛点——如缺失值处理、A/B测试归因、在线学习延迟优化、SHAP解释落地、Kafka分区故障排查等,精选经生产环境验证的免费资源与付费经典,强调‘阶段适配’与‘实操验证’。它不替代动手,但帮你绕过90%初学者踩过的环境配置、知识迁移与业务语境断层陷阱,让每一次阅读都直指今天下午要跑通的XGBoost模型或明天晨会要听懂的200ms延迟指标。

abo1977的博客 535

Kafka源码分析11:PartitionStateMachine分区状态机(图解+秒懂+史上最全)

很久很久之前,你不与任何其他电脑相连接,孤苦伶仃。 直到有一天,你希望与另一台电脑 B 建立通信,于是你们各开了一个网口,用一根网线连接了起来。 用一根网线连接起来怎么就能"通信"了呢?我可以给你讲 IO、讲中断、讲缓冲区,但这不是研究网络时该关心的问题。 如果你纠结,要么去研究一下操作系统是如何处理网络 IO 的,要么去研究一下包是如何被网卡转换成电信号发送出去的,要么就仅仅把它当做电脑里有个小人在开枪吧~ 反正,你们就是连起来了,并且可以通信。 第一层 有一天,一个新伙伴 C 加入了,但聪明的你们

架构师尼恩 1168

Java后端面试7天冲刺:从八股文到实战的系统复习指南

这次我们来看一个针对Java后端面试的八股文学习项目。这个项目号称“7天刷完面试所需的Java后端八股文,面试通过率95%”,覆盖了从Java基础、并发编程、JVM、MySQL到Spring全家桶等核心后端技术栈,并包含了场景题。对于正在准备面试的开发者来说,这听起来像是一个高效、集中的复习方案。 本文的核心不是讨论“7天”是否足够,而是拆解这个学习路径的可行性、内容结构,以及如何将其转化为可执行的、高效的复习计划。我们将重点关注这套八股文的知识体系完整性、学习节奏安排、以及如何结合实践进行巩固,最终目标是

371

kafka:Kafka Streams深度解析

作为Kafka原生的流处理库,Kafka Streams采用独特的"嵌入式SDK"模式,与Flink/Spark等框架形成鲜明对比。在阿里双十一大屏实时统计场景中,我们曾用10台普通服务器通过Kafka Streams实现了每秒50万订单事件的实时处理,延迟控制在200ms以内。

weixin_43290370的博客 781

Pascal Context数据集实战:从安装到格式转换的完整指南

本文提供了Pascal Context数据集的完整实战指南,从安装配置到格式转换的详细步骤。Pascal Context是计算机视觉领域重要的语义分割数据集,包含540个精细标注类别。文章涵盖数据集下载、环境配置、预处理技巧和常见问题解决方案,帮助开发者高效利用这一数据集进行场景理解任务。

weixin_30216561的博客 616

清华数据结构列车调度AC100分

两种思路(1)将目标栈的数据和待混洗的数据逐个进行比较#pragma warning(disable:4996) #include&lt;stdio.h&gt; #include&lt;string.h&gt; #define maxn 1600005 using namespace std; char a[2*maxn][5];//记录操作命令 int stack[maxn]; char pop...

hgtjcxy的博客 925

Java面试7天高效突击:核心考点与场景题应对策略

在Java开发领域,掌握核心知识体系是技术面试成功的关键。Java基础、并发编程、JVM、MySQL和Spring框架构成了企业级开发的基石,其原理与优化直接影响系统性能与稳定性。理解这些技术不仅有助于构建健壮的应用,更是应对高并发、大数据量场景的必备能力。例如,深入理解JVM内存模型与垃圾回收机制,能有效诊断线上OOM问题;精通MySQL索引与事务隔离级别,可大幅提升数据库查询效率与数据一致性。在实际面试中,面试官常通过场景题考察候选人将分散知识点串联解决实际问题的能力,如系统性能调优、高并发设计等。本文

weixin_33768481的博客 332

Android wifi源码分析(二) Wifi关闭流程

接着上一篇,这篇说一下Wifi的关闭流程。 由上一篇可以知道,framework层提供的wifi开关接口都是**setWifiEnabled**,只是参数不同而已。*true*表示开启wifi、*false*表示关闭wifi。

奋斗的菜鸟ing 3300

【阿里后端面经深度复盘】从LRU手撕到RPC容错,8道高频题全链路拆解与万字图解

拿到这局面经的时候,我第一反应不是"题目好多",而是"考察维度好全"。从宏观架构审视到微观代码手撕,从单机并发到分布式容错,从存储引擎到消息中间件——几乎把后端工程师日常工作中会碰到的每一层都戳了一遍。这不是那种"背八股就能过"的面试,面试官明显在试探你能不能把知识串成体系,能不能从现象推到根因,能不能在约束条件下给出工程级方案。我把整场面试拆成以下知识图谱,后面逐一展开:fill:#333;important;important;fill:none;阿里后端面经架构思维组内技术架构审视。

吾为热爱计科,致力于知识分享的践行者,此博客是我在这条道路上留下的足迹。 361

Fastjson2 2.0.26反序列化漏洞深度剖析:从原理到PoC构造

本文深度剖析了Fastjson2在2.0.26及之前版本中存在的反序列化漏洞。该漏洞允许攻击者通过构造包含恶意TemplatesImpl对象的JSONArray,并利用BadAttributeValueExpException触发,在反序列化过程中实现远程代码执行。文章从漏洞原理、PoC构造、利用链分析到防御建议,提供了全面的技术解读,帮助开发者和安全人员理解其危害并采取应对措施。

d3e4f的博客 1052

第11节:GPU 线程层级:线程、线程块、网格

层级标识变量功能线程threadIdx执行单元,处理数据线程块blockIdx管理多个线程,提供共享内存网格管理多个 blockGPU 编程中,合理安排线程层级,是提升性能的关键。

yumeiguo的博客 212

图解 Fluss(三):读写全链路 —— 一次写入、一次扫描、一次点查

一条记录从客户端到落盘要经过哪些步骤、Exactly-Once 是怎么保证的、列裁剪为什么能把网络传输降两个数量级、以及 Lookup Join 的三级缓存如何让维表关联做到亚毫秒级。难度:⭐⭐⭐ | 适合人群:要做性能调优与 Exactly-Once 方案设计的开发。

u010804586的博客 472

【教程4>第5章>第28节】基于帧同步+相位同步+位同步的QPSK调制解调通信系统整体性能分析

综上所述:1.SNR<10db时,整体性能下降,当SNR=0db时,系统无法正常工作。2.gardner环性能达到2.5e-5时偏可以正常工作,增加之后无法正常工作。3.当相位偏差大于pi/4的时候,对最后的性能会有一定影响。

FPGA/MATLAB学习教程/源码/项目合作开发 556

通义灵码深度实践:工程感知、多文件协同与质量防护

AI 编程工具正从简单代码补全迈向工程级智能协作。其核心在于理解项目上下文、识别技术栈语义、对齐开发规范,而非孤立生成代码片段。通义灵码通过工程自动感知引擎、多文件语义协同编辑、TestAgent 质量策略执行器等能力,实现代码生成、重构、测试与合规检查的闭环。这种基于任务目标而非语法片段的交互范式,显著提升 Spring Boot、MyBatis Plus、Redis 等主流框架下的开发效率与交付质量。尤其在金融、电商等强规范场景中,它能主动规避 NPE、事务失效、缓存序列化失败等典型问题,真正成为嵌入研

weixin_30859423的博客 288

【首发独家】华为OD机试 - 服务中心选址、服务中心的最佳位置(C 语言解题)

最近更新的博客 华为 od 2023 | 什么是华为 od,od 薪资待遇,od 机试题清单 华为 OD 机试真题大全,用 Python 解华为机试题 | 机试宝典 【华为 OD 机试】全流程解析+经验分享,题型分享,防作弊指南 华为 od 机试,独家整理 已参加机试人员的实战技巧 本篇华为 OD 机试题解:服务中心选址、服务中心的最佳位置

梦想橡皮擦,专栏100例写作模式先行者,现象级专栏 《Python 爬虫 100 例》作者、《滚雪球学 Python 专栏》原创者 1万+

Vue - 解决 Property or method “__v_isRef“ is not defined on the instance but referenced(浏览器控制台报错,代码没错)

vue,vue2控制台报错,vue报错Propertyormethod"__v_isRef"isnotdefinedontheinstancebutreferenced,vue代码没问题,但是报错Propertyormethod"__v_isRef"isnotdefinedontheinstancebutreferenced,vue2如何解决报错,vue怎么解决报错问题,vue2Propertyormethod"__v_isRef"isnotdefi

🏆 前端领域优质创作者 4503

《2026全新 | Java 100 天进阶之路:从零基础到上岗就业,108篇完整学习地图》

摘要:不背八股文,不堆概念。44篇基础 + 64篇进阶(共108篇),100天助你达到Java就业水平,从容面对技术面试。 👉 零基础入门 / 校招社招突击 / 1-3年Java后端提升 / 想系统复盘全栈知识的开发者。

折哥|智能物流与Java实战,副高数据库工程师。深耕出版社物流WMS/OMS(线体/立库/VGA),也分享Java进阶与OMS电商对接。Oracle→PG实战派。相信沉淀可迁移能力,分享通用技术,不藏不虚。 1万+

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

推荐系统重构:从行为预测到用户意图建模

推荐系统本质上是人机协同的决策支持工具,其核心已从传统协同过滤与矩阵分解,演进为对用户认知状态、实时意图与环境上下文的动态建模。理解‘行为不等于意图’这一基本原理,是突破点击率瓶颈、缓解兴趣窄化、提升长期留存的关键前提。技术价值体现在构建可解释、可干预、可验证的实时反馈闭环,支撑多目标协同优化与可控性设计。典型应用场景涵盖电商冷启动、知识平台深度学习引导、短视频信息流多样性调控等复杂决策环境。本文聚焦推荐引擎底层逻辑的范式迁移,深入解析意图图谱构建、毫秒级反馈机制与混合式可解释基础设施。

weixin_34378969的博客 354

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

上一篇: 面试还不懂如何回答面试JVM相关的问题,看这一篇就够了
下一篇: 字节面试官:“这35道 Spring Cloud 面试题都答不上来?”
java_lujj
博客等级 码龄4年 18粉丝 152原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值