面灵AI→

TCL Java 后端开发实习一面:Kafka 连环追问与计算机基础

轮次
一面
时间
2026-09
来源
牛客网

《面试题目》

  1. 自我介绍,并展开讲讲你在实习里做了什么。
  2. 你的短剧项目是自研的吗?你是如何把查询从 800ms 降低到 150ms 的?
  3. 为什么 800ms 还要去优化?你认为这个速度是否已经足够快?
  4. 你是如何监控这个查询的?怎么把测试数据测出来的?
  5. 使用 Prometheus 需要怎样配置和使用?
  6. 做链路追踪的时候有遇到过多线程问题吗?遇到时你是如何处理的?
  7. 你自己做的整个链路追踪是怎样的流程?如何进行检测?
  8. 介绍一下你实习时整个项目的背景,以及你所在小组负责的方面。
  9. 再详细说说你在里面做了什么模块。
  10. 你实习中用到了 Kafka,为什么使用 Kafka 而不是直接调用对方的接口?
  11. 使用 Kafka 必然会遇到很多问题,例如你如何保证消息不丢失?如何保证对面百分百消费了,而不是只是积压了没消费?还有可能多次消费,你这边是如何处理的?
  12. 虽然你能够保证消息百分百入列,但万一对面消费者挂了、而且挂了很久导致消息已经过期,你又是如何保证它能够被消费掉?
  13. 你这边是提供了一个接口让对面最后回调、改变数据库字段从而知道被消费了,是吧?说说具体是怎么用的。
  14. 你有去看过 Kafka 队列里面堆积的消息大概有多少、平常跑多少条消息吗?出现过堆积问题吗?
  15. 跟我讲解一下 Kafka 里面的队列、消费者组、生产者这些概念。
  16. 在使用过程中你有遇到过整个消费者组消费异常的问题吗?会重复消费吗?
  17. 除了 Kafka 以外你还会其他的消息队列吗?Kafka 的特性是什么?
  18. 你现在是通过什么方式学习的?只是 B 站和项目吗?
  19. 最近你用 AI 学习过哪些知识?举例说明一下,只要是技术范围内都可以。
  20. 知道 JEV 是哪家公司出的吗?它的作用是什么?
  21. 除了这一块,还有什么知识是通过 AI 学习的?
  22. 你是网络工程专业的学生,能讲一下你在学校里最感兴趣的课程吗?
  23. 你提到了端口,端口属于哪一层?它对应的概念是什么?
  24. 端口这些东西一般和什么抽象对象对应起来?
  25. 那你讲讲进程是什么东西?
  26. 一个进程可以拥有多个端口吗?一个端口可以被多个进程调用吗?
  27. CPU 的调度单位是什么?
  28. 多线程可以拥有同一个端口吗?
  29. 你自己做过抓包之类的实践吗?
  30. timeout 在计算机网络里其实是对什么生效?它的覆盖范围是进程级别的,还是整个端口级别的?
  31. TCP 层面有没有一些 timeout 相关的参数,是我们写程序可以直接控制的?还是说要在系统层面配置?
  32. 你自己做过算法题吗?力扣 100 是吗?常见的排序算法有哪些?
  33. 快速排序的时间复杂度是多少?你目前知道哪些时间复杂度?给我说说。
  34. 如果是一棵排序好的二叉树,现在需要查询并获取叶子节点的数据,整个时间复杂度是多少?需要走多少步才能到达?
  35. 你说的 logn 是对的,那这相当于二叉树里的什么特征,术语叫什么?
  36. 你最近使用什么 AI 开发工具比较多?
  37. 你现在人在哪里?
  38. 你有没有听说过沙箱?跟我讲讲。
  39. 反问:我进去一到两个月大概率做什么?咱们现在做的业务是什么?
  40. 反问:我听到咱们这边是做 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 白名单和超时熔断,因为容器本身不是强隔离边界,内核漏洞可以逃逸。