快手可灵 - Java后端 二面 总结
1. 简单介绍一下自己和项目经历
您好,我是XXX,目前在XXX大学读计算机专业。
技术方面,熟悉Java后端开发,用过Spring Boot、MySQL、Redis这些。也有一定的算法基础和底层开发经验。
项目上主要做过两个,一个是对话系统,实现了多轮对话、上下文管理、对话分支等功能。另一个是电商系统,处理过秒杀场景的高并发优化。
实习经历是在一家做系统软件的公司,做过Linux内核驱动开发,对操作系统底层有比较深入的了解。
我对AI和视频生成技术很感兴趣,希望能在快手可灵学习和成长。
2. 深入聊聊你的对话系统项目,架构是怎样的?
这个项目是一个AI对话平台,用户可以和AI进行多轮对话。
架构上采用前后端分离。前端用Vue实现聊天界面,后端用Spring Boot提供API服务。
后端分了几个模块:
用户模块负责登录注册和权限管理。对话模块管理对话会话和消息,包括创建会话、发送消息、获取历史记录。AI模块负责调用大模型API生成回复,做了一层封装支持多个模型切换。存储模块处理数据持久化。
数据库用MySQL存储用户信息、对话记录。Redis做缓存,缓存用户会话信息和最近的对话历史,减少数据库查询。
对话流程是:用户发送消息,后端保存到数据库,然后调用AI模型生成回复,保存回复后返回给前端。为了提升体验,用了流式返回,AI生成的内容实时推送给前端。
技术难点主要是上下文管理和性能优化。上下文要控制长度,太长影响性能,太短影响效果。我用了滑动窗口策略,保留最近N轮对话。性能优化方面,用Redis缓存热点数据,异步处理非关键操作。
3. 如果对话系统的并发量很大,你会怎么优化?
高并发场景要从多个层面优化。
接入层用Nginx做负载均衡,把请求分发到多个后端实例。可以配置限流规则,防止单个用户或IP频繁请求。
应用层做水平扩展,部署多个实例。使用无状态设计,会话信息存在Redis,任何实例都能处理请求。
缓存策略很重要。用户信息、对话历史这些热点数据放Redis,设置合理的过期时间。可以用本地缓存加Redis的多级缓存,减少网络开销。
AI调用是瓶颈,因为模型推理比较慢。可以用消息队列异步处理,用户发送消息后立即返回,后台慢慢生成回复,通过WebSocket推送给用户。也可以对模型做批处理,把多个请求打包一起推理,提升吞吐量。
数据库层做读写分离,查询走从库,写入走主库。对话记录表按用户ID或时间分表,降低单表压力。给常用查询字段建索引。
限流降级也要做。用Sentinel做接口限流,超过阈值就拒绝或排队。非核心功能可以降级,比如推荐、统计这些,保证核心对话功能可用。
监控方面,实时监控QPS、响应时间、错误率,设置告警。用链路追踪工具定位性能瓶颈。
通过这些优化,系统能承受更高的并发量。
4. JVM的内存模型和垃圾回收了解吗?
JVM内存主要分几个区域:
堆是最大的一块,存放对象实例,是GC的主要区域。分为新生代和老年代,新生代又分Eden和两个Survivor区。
方法区存放类信息、常量、静态变量,JDK8后改成元空间,使用本地内存。
虚拟机栈是线程私有的,存放局部变量、操作数栈、方法出口等。
本地方法栈为native方法服务。
程序计数器记录当前线程执行的字节码行号。
垃圾回收主要针对堆。判断对象是否可回收用可达性分析,从GC Roots开始遍历,不可达的对象是垃圾。
回收算法有几种:
标记-清除,标记存活对象然后清除其他的,简单但会产生碎片。
标记-复制,把内存分两块,每次用一块,回收时把存活对象复制到另一块。新生代用这个算法。
标记-整理,标记存活对象然后移动到一端,清理边界外的内存。老年代用这个算法。
常见的垃圾回收器:
Serial单线程,适合小应用。Parallel多线程,吞吐量优先。CMS并发标记清除,停顿时间短。G1分区回收,可预测停顿,JDK9后的默认回收器。ZGC低延迟回收器,停顿时间在10ms内。
实际使用要根据应用特点选择回收器,通过JVM参数调优,使用工具分析GC日志。
5. 说说ConcurrentHashMap的实现原理
ConcurrentHashMap是线程安全的HashMap,性能比Hashtable好很多。
JDK1.7用分段锁,把数据分成多个Segment,每个Segment是一个小HashMap,继承自Reent
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
Java面试圣经,带你练透java圣经
查看19道真题和解析