TCL Java 后端开发实习一面:Kafka 连环追问与计算机基础
- 轮次
- 一面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- 自我介绍,并展开讲讲你在实习里做了什么。
- 你的短剧项目是自研的吗?你是如何把查询从 800ms 降低到 150ms 的?
- 为什么 800ms 还要去优化?你认为这个速度是否已经足够快?
- 你是如何监控这个查询的?怎么把测试数据测出来的?
- 使用 Prometheus 需要怎样配置和使用?
- 做链路追踪的时候有遇到过多线程问题吗?遇到时你是如何处理的?
- 你自己做的整个链路追踪是怎样的流程?如何进行检测?
- 介绍一下你实习时整个项目的背景,以及你所在小组负责的方面。
- 再详细说说你在里面做了什么模块。
- 你实习中用到了 Kafka,为什么使用 Kafka 而不是直接调用对方的接口?
- 使用 Kafka 必然会遇到很多问题,例如你如何保证消息不丢失?如何保证对面百分百消费了,而不是只是积压了没消费?还有可能多次消费,你这边是如何处理的?
- 虽然你能够保证消息百分百入列,但万一对面消费者挂了、而且挂了很久导致消息已经过期,你又是如何保证它能够被消费掉?
- 你这边是提供了一个接口让对面最后回调、改变数据库字段从而知道被消费了,是吧?说说具体是怎么用的。
- 你有去看过 Kafka 队列里面堆积的消息大概有多少、平常跑多少条消息吗?出现过堆积问题吗?
- 跟我讲解一下 Kafka 里面的队列、消费者组、生产者这些概念。
- 在使用过程中你有遇到过整个消费者组消费异常的问题吗?会重复消费吗?
- 除了 Kafka 以外你还会其他的消息队列吗?Kafka 的特性是什么?
- 你现在是通过什么方式学习的?只是 B 站和项目吗?
- 最近你用 AI 学习过哪些知识?举例说明一下,只要是技术范围内都可以。
- 知道 JEV 是哪家公司出的吗?它的作用是什么?
- 除了这一块,还有什么知识是通过 AI 学习的?
- 你是网络工程专业的学生,能讲一下你在学校里最感兴趣的课程吗?
- 你提到了端口,端口属于哪一层?它对应的概念是什么?
- 端口这些东西一般和什么抽象对象对应起来?
- 那你讲讲进程是什么东西?
- 一个进程可以拥有多个端口吗?一个端口可以被多个进程调用吗?
- CPU 的调度单位是什么?
- 多线程可以拥有同一个端口吗?
- 你自己做过抓包之类的实践吗?
- timeout 在计算机网络里其实是对什么生效?它的覆盖范围是进程级别的,还是整个端口级别的?
- TCP 层面有没有一些 timeout 相关的参数,是我们写程序可以直接控制的?还是说要在系统层面配置?
- 你自己做过算法题吗?力扣 100 是吗?常见的排序算法有哪些?
- 快速排序的时间复杂度是多少?你目前知道哪些时间复杂度?给我说说。
- 如果是一棵排序好的二叉树,现在需要查询并获取叶子节点的数据,整个时间复杂度是多少?需要走多少步才能到达?
- 你说的 logn 是对的,那这相当于二叉树里的什么特征,术语叫什么?
- 你最近使用什么 AI 开发工具比较多?
- 你现在人在哪里?
- 你有没有听说过沙箱?跟我讲讲。
- 反问:我进去一到两个月大概率做什么?咱们现在做的业务是什么?
- 反问:我听到咱们这边是做 AI 相关的内容,那我有机会参与吗?
《参考解析》
把查询从 800ms 降到 150ms 怎么讲
面试官问「为什么 800ms 还要优化」,其实是在验证你是不是真的做过、还是背了个数字。要区分「响应时间」和「单次查询耗时」:如果一次页面交互里串了十几个查询,其中一个占 800ms,那它就是整个链路的瓶颈。优化路径要有先后顺序:先用慢查询日志和执行计划定位,看是全表扫描、回表过多、还是排序和临时表;然后加或调整联合索引覆盖查询条件,消掉 Using filesort 和 Using temporary;再看是不是取了用不到的列、能不能减少回表;数据量再大就考虑冷热分离、把聚合结果预计算成宽表、或者加一层 Redis 缓存。每一步都要能说出「改前多少、改后多少、怎么测的」——用压测工具固定并发和数据量跑,或者用 APM 上的 P99 对比,而不是拿单次点一下的耗时说话。
Prometheus 配置与使用
Prometheus 是拉模型:在 prometheus.yml 里配置 scrape_configs,写清 job_name、metrics_path、scrape_interval 和 targets(静态配置或服务发现);应用侧暴露 /metrics 端点,Java 服务一般用 Micrometer 或 micrometer-registry-prometheus 自动埋 JVM、HTTP、连接池指标,业务指标用 Counter、Gauge、Histogram 手写。指标有了之后用 PromQL 做聚合和告警,比如按 rate 算 QPS、用 histogram_quantile 算 P99,配 alertmanager 发通知。链路追踪是另一套东西(OpenTelemetry、SkyWalking),Prometheus 管指标、不管单次请求的调用链,两者靠 traceId 关联。
Kafka 消息不丢失、不重复、消费者宕机
不丢失要三段都保证:生产端设 acks=all,让消息写入所有 ISR 副本才算成功,同时开重试并设 max.in.flight.requests.per.connection=1(或配合幂等生产者)避免重试乱序;Broker 端设 replication.factor ≥ 3、min.insync.replicas ≥ 2,并且别用 unclean.leader.election;消费端关掉自动提交,改成处理完业务再手动提交 offset。不重复消费本质上是「消费端要做到幂等」——Kafka 只保证至少一次,重复是设计的一部分。做法是用业务唯一键建唯一索引或去重表、用数据库的乐观锁版本号、或者把「消费记录 + 业务写入」放进同一个本地事务。消费者挂了很久导致消息过期,要靠两件事兜底:一是 topic 的 retention 设得足够长,二是别把 Kafka 当唯一真相来源,落一份业务表或对象存储,消费者恢复后先重放积压、再对账补齐漏掉的部分。消费失败的消息投到死信 topic 单独处理,别让它卡住分区。
Kafka 核心概念与常见故障
Producer 把消息按分区策略(指定 key 时按 key 哈希、否则轮询或粘性)发到某个 partition;一个 topic 分成多个 partition 来并行;每个 partition 内部严格有序,跨分区不保证顺序。Consumer Group 是消费的基本单位:组内每个 partition 只会被一个消费者消费,所以消费者数超过 partition 数就会有实例空闲——这也是扩容消费能力的上限。offset 记录消费位点,可以存在 Kafka 内部 topic 或外部存储。重复消费最常见的三个来源:处理完成但提交 offset 之前进程挂了、消费者被踢出组触发 rebalance 后新消费者从上一次已提交位点重新拉、以及生产者重试造成重复写入。消费异常常见的是 rebalance 风暴(session.timeout.ms 和 max.poll.interval.ms 设置不当,或者单批处理太慢导致被判定为死掉),处理办法是缩小 max.poll.records、把耗时逻辑异步化,并给消费逻辑加退避重试。Kafka 的特性是高吞吐、分区内有序、持久化保留可重放、水平扩展好;缺点是分区数就是并行上限,而且它不擅长延迟消息和复杂的路由,那类需求 RocketMQ、RabbitMQ 更顺手。
端口、进程与 CPU 调度
端口属于传输层,是传输层协议用来区分同一台主机上不同应用的标识:IP 定位主机,端口定位主机上的进程。TCP 和 UDP 各有自己的 65536 个端口空间,所以「TCP 80」和「UDP 80」是两个不同的东西。一个进程可以监听多个端口,一个端口在同一时刻也可以被多个进程共有——前提是设置 SO_REUSEPORT,否则同一 (协议, 地址, 端口) 只能被一个监听套接字绑定。服务端的多线程/多进程共享监听套接字时,是靠内核把新连接分给其中一个来 accept,并不是每个线程各自绑一个端口。CPU 的调度单位是线程(内核调度实体),进程是资源分配的单位,线程共享进程的地址空间和文件描述符表,所以多线程可以共同处理同一个端口上的连接。
TCP timeout 怎么控制
timeout 不是作用在「进程」或「端口」上的开关,而是分层的:应用层自己设(HTTP 客户端的 connectTimeout / readTimeout、gRPC 的 deadline),这些只能通过代码控制;传输层由内核参数和套接字选项控制,套接字级别有 SO_RCVTIMEO / SO_SNDTIMEO,系统级别有 tcp_syn_retries、tcp_keepalive_time、tcp_retries2、tcp_fin_timeout 这些,改的是整台机器的行为。时序上还有 TIME_WAIT(默认 2MSL)和 CLOSE_WAIT 两个状态,前者是主动关闭方等待旧报文消散,后者是对方关了、本端还没调 close,CLOSE_WAIT 大量堆积基本可以断定是代码没关连接。
快排与二叉树的复杂度
快速排序平均 O(n log n),每层划分扫一遍是 O(n),平均分 log n 层;最坏是每次选到的基准都是最大或最小值,退化成 O(n²),常见于对已经有序的数组取首元素当基准,用随机化或三数取中能大幅降低概率;空间复杂度是递归栈的 O(log n),最坏 O(n);它不稳定。常见复杂度从快到慢排:O(1) 哈希查找、O(log n) 二分和平衡树、O(n) 线性扫描、O(n log n) 排序、O(n²) 冒泡插入选择、O(2ⁿ) 和 O(n!) 是暴力枚举和全排列。在一棵有序(二叉搜索)树里查一个值,走的是从根到目标的唯一路径,每层排除一半,复杂度 O(log n),需要走的步数就是树高,平衡时约等于 log₂n;这个特征叫「树高平衡」,对应的术语是平衡二叉树(AVL、红黑树),一旦退化成链表就变回 O(n)。如果问的是「取到所有叶子节点」,那要遍历整棵树,复杂度是 O(n),因为每个节点都要访问。
沙箱
沙箱是一层隔离机制,让不受信任的代码在受限环境里跑,碰不到宿主机的文件、网络和进程。实现层次差别很大:语言级的有 JVM 的 SecurityManager(已废弃)和 JS 引擎的 realm 隔离;进程级最常用的是 Linux 的 namespace 加 cgroup,Docker 就是这么做的,namespace 让容器看不到别人的进程和网络,cgroup 限制 CPU、内存和 IO;更硬的是 seccomp 过滤系统调用、以及 KVM/Firecracker 这种轻量虚拟机做硬件级隔离。实际做「跑用户代码」的场景(判题系统、AI 生成的代码执行)一般是多层叠加:容器起一个无网络、只读文件系统、非 root 用户、限制内存和 CPU 的进程,再加 seccomp 白名单和超时熔断,因为容器本身不是强隔离边界,内核漏洞可以逃逸。