谢飞机面试大厂Java岗:从音视频到AI大模型,一场“水”与“火”的较量

📅 2026/8/13 7:26:03
谢飞机面试大厂Java岗:从音视频到AI大模型,一场“水”与“火”的较量
谢飞机面试大厂Java岗从音视频到AI大模型一场“水”与“火”的较量面试官端坐在会议室里面前放着一台笔记本电脑屏幕上已经打开了在线面试系统。他抬眼看了看走进来的年轻人——谢飞机穿着一件印着“Hello World”的T恤背着一个双肩包看起来还算精神。但面试官隐约感到这位求职者可能不简单。“请坐谢飞机同学。我们开始吧先做一下自我介绍”面试官语气平淡但眼神犀利。“面试官您好我叫谢飞机三年Java开发经验熟悉Spring全家桶会用Redis缓存也会写点JUnit测试平时喜欢研究JVM调优比如-Xmx和-XX:UseG1GC这些参数我都能背出来。”谢飞机搓了搓手自信满满。面试官点点头翻开简历开始了第一轮提问。第一轮音视频场景下的基础功“既然你提到熟悉Spring全家桶那我们结合一个实际业务场景比如一个短视频App用户上传视频后需要转码、生成缩略图还要支持弹幕实时推送。你会怎么设计后端技术架构”面试官抛出了问题。谢飞机心想这还不简单我背过八股文。“这个嘛首先用Spring Boot搭个服务然后接收上传用线程池异步处理。转码的话可以用FFmpeg不过我是Java工程师一般调用命令行。生成缩略图可以用Java的ImageIO。弹幕推送嘛用WebSocket就行Spring支持得特别好。”谢飞机回答得很流畅。面试官追问“那如果上传量特别大比如像抖音那样每秒有几千个视频上传你的线程池和单机服务扛得住吗你考虑过消息队列削峰吗还有视频转码是个CPU密集任务你怎么做资源隔离”谢飞机愣住了支支吾吾“啊……这个……消息队列我听说过Kafka但自己没真正在线上用过。不过我知道Kafka是分布式消息队列吞吐量特别高资源隔离的话……Docker容器算不算”面试官微微皱眉又问“那么视频转码完成后用户要知道状态。你打算怎么通知客户端是轮询还是长连接”“用……用WebSocket刚才说了可以推给用户。”谢飞机赶紧答道。面试官不置可否继续问“那如果转码失败需要重试。你怎么保证消息不丢失Kafka的ack机制和offset提交你了解吗”谢飞机额头冒汗“呃……这个……我大概知道Kafka有ackall能保证不丢但具体怎么配置我得查一下文档。offset……是消费的位置吧我一般是自动提交的。”面试官沉默了几秒换了个问题“那缓存方面视频的元信息、点赞数这些热点数据你会怎么设计缓存Redis具体用什么数据结构”谢飞机稍稍松了口气“Redis用String存点赞数用Hash存视频元信息。过期时间可以设置成热点视频永久不过期普通视频24小时。用Spring Cache的Cacheable注解非常方便。”面试官追问“那缓存穿透、击穿、雪崩你分别怎么解决”谢飞机终于来了精神“缓存穿透用布隆过滤器缓存击穿用互斥锁或者逻辑过期缓存雪崩就是加随机过期时间还有限流降级。这个我熟”他差点把“九阳真经”背出来。面试官点点头但没夸他而是问了一个更实际的问题“好那如果Redis集群中某个节点挂了主从切换时数据会不会丢你怎么配置Redisson的分布式锁”“这个……”谢飞机卡壳了“主从切换……可能会丢一点数据因为异步复制。但Redisson锁默认是用的看门狗续期嗯……只要设置他的主从节点都加锁就不会有点问题但我不太记得具体API了。”面试官看了看时间说“第一轮先到这里。我们进入第二轮。”谢飞机擦了擦汗心想这面试官怎么总问细节我那些模板答案怎么不管用了第二轮电商与本地生活服务的微服务挑战面试官调整了一下坐姿说道“我们公司除了音视频还有电商和本地生活业务。假设你现在要做一个外卖下单系统涉及订单服务、库存服务、优惠券服务、支付服务。你如何做微服务拆分服务间怎么通信”谢飞机这次学乖了先想了想“用Spring Cloud Alibaba服务发现用Nacos服务间用OpenFeign。每个服务独立数据库用Seata处理分布式事务。”他偷偷看了一眼面试官的脸色好像没刚才那么严肃了。“嗯思路没错。那如果用户下单时先扣库存再减优惠券最后创建订单但支付环节超时了库存已经扣了怎么办”面试官追问。“这个……可以用本地消息表就是先在一个事务里更新库存和写消息表然后通过消息队列通知其他服务。或者用Seata的Saga模式补偿性事务。”谢飞机记得这些名词但解释起来有点乱。面试官没有深究接着问“那高并发下比如秒杀活动瞬间有十万用户抢购。你的订单服务和库存服务怎么扛限流怎么做”谢飞机来了劲“用Redis预减库存先把库存加载到Redis里请求先走Redis判断是否还有库存有的话再异步发送消息到Kafka然后由订单服务消费创建订单。限流可以用Sentinel或者Resilience4j支持滑动窗口、令牌桶算法。”“那Sentinel的限流和熔断规则你具体配置过吗FlowRule和DegradeRule的参数怎么设”“呃……我一般是在控制台配的那比如设置QPS每秒100超了直接拒绝。熔断就是错误率超过50%主动降级。”面试官似乎勉强接受继续问“支付结果通知是异步回调。你怎么保证幂等性”“幂等性……用唯一订单号做唯一索引回调的时候先查一下状态如果已经是已支付就不处理。或者用Redis的SETNX锁设置一个处理中标记。”“那万一回调重复发送第一个请求处理到一半第二个请求也进来了怎么办你用的是数据库唯一约束还是Redis锁如果Redis锁过期了呢”面试官连环追问。谢飞机有点晕“那……我就用数据库唯一约束插入一个支付流水如果主键冲突就说明已经处理过了。Redis锁过期就在续期用Redisson的看门狗……但具体源码我没深入。”面试官不置可否转个方向“我注意到安全方面你们的支付服务怎么签名的OAuth2和JWT分别用在什么场景”“JWT是无状态token用在登录认证。OAuth2是授权框架第三方登录用比如微信登录就是OAuth2。签名的话我们是用MD5加盐或者RSA私钥签名然后对方用公钥验签。”谢飞机这次回答总算没太跑偏。面试官终于点了点头“嗯JWT和OAuth2的区别你说得大致对。那Spring Security的过滤器链你用过吗比如在网关层的Token校验怎么实现”谢飞机迟疑了几秒说“Spring Security的过滤器链……我大概知道有UsernamePasswordAuthenticationFilter、JwtAuthenticationFilter这些但让我自己写一个我可能需要查一下。网关用的Spring Cloud Gateway可以写GlobalFilter去校验token。”面试官说“行那我们就最后一轮吧。”谢飞机心里一紧还有一轮第三轮AIGC与智能推荐——面试官终于露出了獠牙“你已经扛过两轮了。最后一轮结合当前热点我们做AIGC内容社区。用户输入一段文字后端调用大模型生成图片同时根据用户行为做个性化推荐。你的技术选型”面试官眼神中闪过一丝兴奋。谢飞机深吸了一口气“这个我了解最近很火。大模型调用可以用OpenAI的API或者用HuggingFace的模型。Java的话调用Python服务可以用gRPC或者HTTP接口。推荐的话……用协同过滤算法但我没实际写过。”“不考虑Python服务纯Java生态。你了解Spring AI框架吗或者LangChain4j如果不用这些你怎么实现和模型交互”面试官步步紧逼。“Spring AI哦好像是一个新项目。我没实际用过不过我们知道它是用来对接OpenAI、Azure这些模型API的。LangChain4j也听说但没深入。纯Java的话我可以直接用RestTemplate调用HTTP接口把prompt发过去然后接收生成的文本或图片URL。”谢飞机勉强挤出几句。“那生成的图片用户可能会频繁生成比如一天几十次。这些数据量怎么存入数据库图片的元信息、模型参数、提示词你如何设计表结构如何做分库分表”谢飞机有点崩溃“这个……我虽然知道分库分表有ShardingSphere但没真实搞过。就先用MySQL存然后加索引呗。如果量太大再用ES存”他自己都觉得不靠谱。面试官笑了笑“那内容推荐怎么做比如用户A收藏了一幅赛博朋克风格的图片用户B也喜欢。你怎么在Redis里存储他们的偏好然后推荐相似内容”“可以……用Redis的Set存用户偏好标签然后做交集找到相似用户再推荐他们喜欢的图片。用ZSet存图片热度按分数排序。还可以用Caffeine做本地缓存减少Redis压力。”谢飞机居然说出了一个可行的思路。面试官追问“那把热门图片的详情放Caffeine冷数据放Redis再底层内存MySQL你如何保证Caffeine和Redis一致如果后台更新了图片怎么让Caffeine失效”“用Spring Cache的CacheEvict在更新图片后调用一下就会删除缓存。Caffeine可以设置一个很短的过期时间比如几秒这样不一致窗口很小。”谢飞机回答得倒是挺快。面试官点了点头又抛出一个杀手锏“大模型生成的Prompt用户可能输入恶意文本。你需要做内容安全过滤。你如何用Java实现一个敏感词过滤如果调用第三方审核API超时了怎么办熔断降级怎么做”“敏感词……用AC自动机把敏感词库加载到内存构建Trie树匹配效率是O(n)。如果第三方API超时我可以设置HttpClient的连接超时和读取超时比如2秒如果超过就用Resilience4j的TimeLimiter和CircuitBreaker熔断直接返回默认审核结果或者人工审核。”面试官终于露出了一点赞赏之色“你居然知道AC自动机。那你能手写一个吗”谢飞机挠了挠头“手写……我只能写个大概就是先建Trie树然后失败指针然后匹配。但具体代码我记不全了。”面试官笑了“好谢飞机同学今天的面谈就到这里吧。你的知识面还可以但很多技术细节掌握得不够扎实。我们需要一个能深入原理、解决实际问题的工程师。你先回去等通知吧。”谢飞机站起来心想“果然是大厂问得真细。虽然我凉了但学到了不少。回去得把Kafka和Redisson源码啃一遍。”他正要出门面试官又叫住他“对了你简历上写的‘熟悉分布式事务’能说说Seata的AT模式具体怎么实现的吗”谢飞机愣住了张了张嘴“那个……就是……通过全局事务管理器呃……生成前后镜像然后……嘿嘿面试官您等着我回去一定把答案整理出来下次再来”面试官默默关闭了面试房间。附完整答案详解——每一轮的技术点和业务场景梳理为了让像谢飞机一样的小白能真正学到东西我们把面试中的问题一一拆解结合业务场景给出清晰的答案。第一轮音视频场景场景描述短视频App用户上传视频后端需要转码格式转换、压缩、生成缩略图支持弹幕实时推送。涉及高并发上传、异步处理、缓存热点数据。问题1怎么设计后端技术架构答案上传服务使用Spring Boot构建一个HTTP接口接收multipart文件但不要把大文件直接给后端。通常采用分片上传断点续传客户端把文件切成5MB左右的分片上传完成后由服务端合并。分片信息存在Redis中比如用Hash记录uploadId - 分片序号 - ETag。异步处理因为转码是耗时任务收到完整文件后把转码任务包括视频路径、分辨率、码率等参数发送到Kafka。Kafka的Topic可以叫video-transcode消费者端部署独立的转码服务可以单独用Docker容器或K8s Pod资源与Web服务隔离。转码实现Java调用FFmpeg命令行或者构建微服务用ProcessBuilder执行ffmpeg -i input.mp4 -c:v libx264 -preset fast -b:v 2000k output.flv。生成缩略图可以用FFmpeg的-ss 1 -vframes 1提取某一帧。弹幕推送使用WebSocketSpring Boot支持ServerEndpoint但在高并发下需要做集群解决连接会话在哪个节点的问题。可以用Redis发布订阅或Kafka将弹幕消息广播到所有WebSocket节点的内存队列再推给连接在本节点的用户。问题2上传量大消息队列有什么用如何资源隔离答案消息队列削峰上传请求先进入网关写入Kafka然后立即返回“上传成功正在处理”。转码服务按自身消费能力拉取消息即使瞬间产生十万条转码请求也不会打垮转码服务。资源隔离Docker容器限制CPU和内存比如--cpus2 --memory4g。Kubernetes里给转码服务设置resources.limits.cpu: 4。这样Web服务堆积也不会影响转码服务反过来转码服务高负载也不会拖垮Web服务。问题3转码进度如何通知客户端答案推荐使用WebSocket主动推送。客户端建立连接时带上videoId后端在转码的每个阶段已上传、转码中、转码成功、转码失败通过WebSocket发送消息消息体包含状态码和进度百分比。另外可以提供一个REST APIGET /videos/{id}/status供客户端轮询但轮询有延迟且浪费资源。优先使用WebSocket若客户端不支持则降级为轮询。问题4Kafka消息不丢失ack和offset怎么配置答案生产者不丢消息设置acksall或者-1只有分区所有ISR副本都收到消息才返回成功。同时设置retries3和enable.idempotencetrue幂等性。消费者不丢消息关闭自动提交enable.auto.commitfalse在业务逻辑处理完成后手动提交offset。比如消费转码消息先执行FFmpeg命令然后consumer.commitSync()。如果执行失败不提交offset下一条消息拉取时会重新消费。Broker不丢消息复制因子replication.factor3min.insync.replicas2避免leader故障时数据丢失。问题5Redis缓存设计穿透、击穿、雪崩答案缓存结构视频元信息用Hash存储字段如video:123- {title, author, coverUrl, duration}。点赞数用String存整数字符串用Redis的INCR/DECR操作。也可以把点赞数写成video:likes:123。缓存穿透查询一个不存在的videoId缓存和数据库都没有。解决布隆过滤器把所有存在的videoId提前放入或缓存空值设置短过期时间如5分钟。缓存击穿某个热点视频过期大量请求同时打到数据库。解决互斥锁SET lock:video:123 NX EX 10、热点数据逻辑过期在value中设置逻辑过期时间异步刷新。缓存雪崩大量key同时过期。解决过期时间加随机数比如300~600秒之间或使用多级缓存本地缓存Redis或对数据库限流降级。问题6Redis集群主从切换数据丢失Redisson分布式锁如何设置答案主从切换丢数据问题在Redis主从复制是异步的若主节点故障未同步到从节点的数据会丢失。使用Redisson的RedissonMultiLock联锁可以同时锁主节点和所有从节点只要全部加锁成功才算成功。配置方法创建多个RLock然后multiLock.lock()。但实际生产更推荐使用Redlock算法实现于Redisson它建议对5个独立Redis节点加锁超过3个成功才算获取锁。确实能提高安全性但也不能100%保证存在时钟漂移等场景。简答时可以说“使用Redisson的联锁并对关键操作结合Zookeeper或数据库乐观锁做兜底”。第二轮电商与本地生活服务场景描述外卖下单系统包含订单、库存、优惠券、支付服务。典型的微服务分布式事务问题、高并发秒杀问题、支付幂等性问题、安全认证问题。问题1微服务拆分与服务间通信答案按业务域拆分为order-service、inventory-service、coupon-service、payment-service、user-service。每个服务独立数据库避免跨库关联查询。服务间通信同步调用用OpenFeign Spring Cloud LoadBalancer或Ribbon异步调用用Kafka/RabbitMQ。比如下单时订单服务调用库存服务扣减库存同步扣减成功后再向订单状态变更Topic发送消息由支付服务监听触发支付流程。分布式事务优先使用最终一致性方案如本地消息表、事务消息RocketMQ或Seata的AT/TCC模式。严格一致性场景如支付扣款用TCC或Saga。问题2支付超时库存已扣了怎么办答案本地消息表方案在订单服务里将“扣库存”和“写消息表”放在同一个本地事务中步骤更新库存扣减量插入一条状态为NEW的消息。然后后台任务把消息发送给MQ消费者处理后续逻辑如果处理失败MQ重试重试多次失败则调用补偿接口如释放库存。Seata Saga模式定义正向服务扣库存、减优惠券、创建订单和反向补偿服务回滚库存、恢复优惠券、取消订单。通过状态机编排任何一步失败自动执行已经完成步骤的补偿操作。实际面试时不能说“用Seata就完事”要具体说Saga适合长事务但无隔离性需要业务层防脏读。例如扣库存时加“冻结库存”支付超时后解冻。问题3秒杀场景高并发限流怎么做答案四层防护前端按钮置灰、随机延时、验证码。网关Sentinel或Spring Cloud Gateway全局限流按用户ID限流如每秒1次、按IP限流如每秒10次。Redis预减库存秒杀前把库存预热到RedisSET seckill:stock:1 100。请求进入时执行Lua脚本原子性操作if redis.call(GET, key) 0 then return 0 end return redis.call(DECR, key)如果返回0直接返回“已抢光”。减库存成功后再把用户ID和数据写入Kafka异步创建订单和扣减DB库存。数据库限流防止超卖UPDATE inventory SET stock stock - 1 WHERE id ? AND stock 0影响行数为1才成功。Sentinel配置FlowRule设置资源名、QPS阈值例如200和拒绝策略直接拒绝或预热。DegradeRule设置熔断条件如异常比例超过0.2熔断10秒。问题4支付回调的幂等性答案支付平台如微信、支付宝会多次异步回调同一订单可能最多发几十次通知。服务端必须幂等。方案在payment_notify表创建唯一索引(order_id, transaction_id)。回调处理器首先查询该表如果存在说明已处理直接返回“SUCCESS”。如果不存在则尝试插入若插入成功则继续更新订单状态若插入时发生主键冲突并发重复请求到了则回滚并进行查询确认已经处于处理状态后返回成功。也可以使用Redis的SET order:pay:123 1 NX EX 120但Redis锁过期可能造成并发问题最好持久化唯一约束作为兜底。问题5JWT、OAuth2和签名答案JWT一种无状态Token由Header.Payload.Signature组成。适合单点登录但无法主动失效因此需要配合黑名单或短过期时间。用于认证“你是谁”。OAuth2授权框架解决第三方应用如何获得用户授权。比如微信登录用户跳转微信授权页微信返回code后端用code换取access_token。用于授权“允许谁访问”。两者关系OAuth2可以用JWT作为access_token的格式。Spring Security OAuth2里JwtBearerTokenAuthenticationConverter可以解析JWT。API签名商户平台对接支付网关时通常用私钥对请求参数按字典序拼接加nonce和时间戳做RSA签名支付网关用公钥验签防止篡改和重放。MD5加盐更弱推荐RSA/SHA256withRSA。问题6Spring Security过滤器链和网关Token校验答案Spring Security的过滤器链核心是FilterChainProxy常见过滤器有SecurityContextPersistenceFilter恢复上下文、UsernamePasswordAuthenticationFilter处理表单登录、JwtAuthenticationFilter自定义解析Token、ExceptionTranslationFilter、AuthorizationFilter鉴权。可以自己实现一个OncePerRequestFilter在doFilterInternal里解析HTTP Header中的Authorization: Bearer xxx把Authentication对象保存到SecurityContext。在微服务网关Spring Cloud Gateway中可以使用GlobalFilterOrdered。实现思路获取请求头token调用用户服务校验或本地JWT解析如果合法则把用户ID写入请求头转发给下游不合法则返回401。同时网关也可以做白名单校验如/login、/register不用token。第三轮AIGC与智能推荐场景描述AIGC内容社区用户输入prompt后端调用大模型生成图片同时根据用户行为做个性化推荐。涉及调用外部AI服务、内容安全、缓存一致性、推荐算法基础。问题1Java如何调用大模型Spring AI和LangChain4j是什么答案Spring AISpring官方提供的AI框架类似Spring生态的AI客户端。支持ChatModel、TextToImageModel等。比如Service public class AiService { private final OpenAiChatModel chatModel; public String generateImage(String prompt) { return textToImageModel.call(prompt); } }通过自定义RestTemplate或WebClient调用OpenAI的/v1/images/generations接口把prompt和参数size、n放进JSON请求体解析返回的图片URL。LangChain4jJava版LangChain支持面向AI模型的可组合能力、RAG、记忆等但实际生产中也还是走HTTP。如果不用框架可以用RestClient来发HTTP请求再配合Jackson解析JSON。需要注意超时连接超时2秒、读超时30秒因为图片生成慢。为了不阻塞主线程应该用异步WebClient调用或者把请求抛出到消息队列由专门的工作线程池处理再由WebSocket推送生成结果给用户。问题2数据量大、分库分表怎么设计答案表设计prompt_record提示词记录id、user_id、prompt_text、model_name、parameters_json、create_time。image_record生成图片记录id、record_id、object_keyOSS地址、width、height、size、status生成中/成功/失败、audit_status未审核/通过/拒绝、create_time。分库分表用户量大后按user_id对数据库分片比如32个库每个库64张表库号 user_id % 32表号 (user_id / 32) % 64。同时create_time作为二级索引的分页查询如果用ES就能避免跨库分页问题。不要用SELECT *尽量只查热点字段。元信息放MySQL大文本prompt原文、生成参数JSON可以存到MongoDB或OSSMySQL只存关联ID。问题3基于Redis做个性化推荐答案用户偏好标签用户收藏图片时给图片打标签如“赛博朋克”、“落日”、“机械姬”在Redis中维护一个Setuser:tags:123里添加cyberpunk、anime等。相似用户用SINTER user:tags:123 user:tags:456获取共同标签定义相似度为交集大小。然后找最相似用户的喜欢图片也存成Setuser:likes:456。候选推荐把相似用户喜欢的图片ID取出来过滤掉当前用户已看过的可以用SISMEMBER然后给图片分数排序。分数可以用ZSetrecommend:images:123中每个图片的分数按喜欢人数×0.6 标签匹配度×0.4计算。本地缓存热门的图片详情用Caffeine缓存冷数据和用户行为用Redis底层MySQL落库。注意Caffeine缓存和Redis之间的数据一致性在更新图片时使用CacheEvict删除本机Caffeine同时发布Redis消息其他服务监听到后删除各自的Caffeine缓存。或者设置Caffeine很短的过期时间比如5秒降低不一致窗口。问题4敏感词过滤和第三方审核API熔断答案敏感词过滤使用AC自动机Aho-Corasick。将敏感词库建立Trie树每个节点有fail指针指向失配时跳跃的位置。匹配时遍历文本字符根据fail指针跳转可以在O(n)时间内找出所有敏感词。Java实现伪代码class AcNode { MapCharacter, AcNode children new HashMap(); AcNode fail; boolean isEnd; // 是否为一个敏感词的结尾 int length; // 词长 } public boolean containsSensitive(String text) { AcNode cur root; for (char ch : text.toCharArray()) { while (cur ! root !cur.children.containsKey(ch)) { cur cur.fail; } cur cur.children.containsKey(ch) ? cur.children.get(ch) : root; for (AcNode p cur; p ! root; p p.fail) { if (p.isEnd) return true; } } return false; }实际可结合第三方审核如阿里云内容安全做图片和文本的机审人工审核。超时熔断使用HttpClient时设置connectTimeout(2s)、readTimeout(2s)。使用Resilience4jTimeLimiter设置最大调用时间2秒CircuitBreaker设置滑动窗口大小比如10秒内50%请求失败则熔断熔断后返回本地默认结果比如auditResultpass低风险场景或转人工审核队列。Bulkhead隔离限制并发调用数量比如最大并发10个超出就排队或快速失败。问题5AC自动机能手写吗答案面试官不要求你立刻写500行代码但你需要说出构建过程构建Trie树插入每个敏感词路径上的字符作为分支。构建失败指针BFS遍历Trie树。根节点的孩子fail指向root其他节点假设当前节点为p父节点为fp是f的孩子字符为c。则看f.fail有没有字符为c的孩子有则p.fail f.fail.children.get(c)否则继续向上。直到root。匹配从root开始遍历文本字符如果当前节点有该字符的子节点就往下走否则跳到fail直到找到匹配路径或回到root。每次到达节点从该节点沿fail链检查是否有isEnd如果有则说明命中敏感词。总结用空间换时间适用于敏感词量不大的场景几千条。如果几百万词条则考虑更高效的双数组TrieDouble-Array Trie。面试官最后一句“回家等通知”说明了什么谢飞机虽然表现了不少亮点能说出AC自动机、Respire4j但总体深度不足。面试官在结束时补充的那个问题其实就是一种含蓄的否决“你简历上写的熟悉分布式事务但我一问细节你就答不上来。” 所以大家在校招或社招面试时不要背干巴巴的八股文一定要掌握技术背后的原理并且结合项目/场景去说清楚。否则下一轮谢飞机就是你。不过没关系谢飞机回去之后把这些问题都整理成了文档又买了几本《Kafka权威指南》《Redisson源码解析》开始扎扎实实地啃源码。下次面试他一定能挺进最后一轮祝他好运。