美云智数 Java软件开发 二面 面经

1. 深入聊聊你的项目,从需求到上线的完整流程 (30min)

项目背景:

我做的是一个在线教育平台项目,主要面向K12学生提供在线课程和作业辅导服务。项目上线后日活用户达到5万左右,高峰期并发量在3000左右。我在项目中主要负责课程管理模块和作业系统模块的后端开发。

技术架构:

整体采用微服务架构,前后端分离。后端使用Spring Cloud技术栈,包括Gateway网关、Nacos注册中心、Feign服务调用、Sentinel限流熔断。数据库使用MySQL主从架构实现读写分离,Redis做缓存和分布式锁。消息队列使用RabbitMQ处理异步任务。文件存储使用阿里云OSS。

服务拆分为用户服务、课程服务、订单服务、作业服务等。每个服务独立部署,使用Docker容器化,通过Jenkins实现CI/CD自动化部署。

开发流程:

需求阶段,产品经理提出需求,我们进行需求评审,明确功能点和验收标准。然后进行技术方案设计,评估技术难度和工作量。

开发阶段,我们使用敏捷开发模式,两周一个迭代。每天站会同步进度,遇到问题及时沟通。开发完成后进行代码review,保证代码质量。

测试阶段,先自测功能,然后提交给测试人员。测试发现bug后在禅道系统提单,我们修复后回归测试。

上线阶段,先在测试环境验证,然后灰度发布到生产环境。先发布10%流量观察,没问题再全量发布。上线后监控系统指标,及时处理异常。

技术难点:

最大的挑战是作业批改的性能问题。学生提交作业后,系统要自动批改选择题和填空题。高峰期有上千份作业同时提交,同步处理会导致接口超时。

我的解决方案是改为异步处理。学生提交作业后立即返回成功,作业信息发送到RabbitMQ。后台消费者从队列获取作业进行批改,批改完成后通过WebSocket推送结果给学生。这样接口响应时间从5秒降到200毫秒,用户体验大幅提升。

另一个难点是课程视频的防盗链。我们使用阿里云OSS的签名URL机制,每次播放视频时生成临时URL,设置1小时过期。同时在播放器中加入防录屏水印,显示学生ID和时间戳。

项目成果:

项目上线后运行稳定,接口平均响应时间在200毫秒以内,99分位响应时间在500毫秒以内。系统可用性达到99.9%以上。通过Redis缓存,数据库查询压力降低了70%。用户反馈体验良好,续费率提升了15%。

2. JVM内存模型和垃圾回收机制,如何进行JVM调优?

JVM内存模型:

JVM运行时数据区分为线程共享和线程私有两部分。

线程私有的有程序计数器、虚拟机栈、本地方法栈。程序计数器记录当前线程执行的字节码行号。虚拟机栈存储局部变量、操作数栈、方法出口等信息,每个方法对应一个栈帧。本地方法栈为Native方法服务。

线程共享的有堆和方法区。堆是最大的内存区域,存储对象实例和数组,是垃圾回收的主要区域。方法区存储类信息、常量、静态变量、JIT编译后的代码,JDK8后改为元空间使用本地内存。

垃圾回收机制:

垃圾回收主要针对堆内存。首先要判断对象是否存活,主要使用可达性分析算法。从GC Roots开始遍历对象引用链,不可达的对象就是垃圾。GC Roots包括虚拟机栈中的引用、方法区的静态变量、常量引用、本地方法栈的引用等。

垃圾回收算法有标记-清除、标记-复制、标记-整理三种。新生代使用标记-复制算法,分为Eden和两个Survivor区,比例8:1:1。每次使用Eden和一个Survivor,回收时把存活对象复制到另一个Survivor。老年代使用标记-整理算法,标记存活对象后让它们向一端移动,清理边界外的内存。

垃圾收集器:

常用的垃圾收集器有Serial、Parallel、CMS、G1、ZGC。

Serial是单线程收集器,适合单核CPU和小内存场景。Parallel是多线程收集器,注重吞吐量,适合后台计算任务。

CMS是并发收集器,追求最短停顿时间。分为初始标记、并发标记、重新标记、并发清除四个阶段。只有初始标记和重新标记需要停顿,其他阶段和应用程序并发执行。缺点是会产生内存碎片,而且并发阶段占用CPU资源。

G1是目前主流的收集器,把堆划分为多个Region,可以设置停顿时间目标。G1会优先回收价值最大的Region,实现可预测的停顿。适合大堆内存场景。

JVM调优:

首先要设置合适的堆内存大小。一般设置为物理内存的一半到三分之二。Xms和Xmx设置为相同值,避免动态扩容。新生代大小设置为堆的三分之一左右。

选择合适的垃圾收集器。JDK8推荐使用G1,设置最大停顿时间目标,比如200毫秒。

开启GC日志,分析GC频率和停顿时间。如果Full GC频繁,可能是老年代空间不足或内存泄漏。如果Young GC频繁,可能是新生代空间太小。

使用jstat、jmap、jstack等工具监控JVM状态。发现内存泄漏时用jmap dump堆内存,用MAT工具分析。发现CPU高时用jstack查看线程堆栈,定位问题代码。

调优要根据实际情况,不能盲目调整参数。要先监控分析,找到瓶颈,再针对性优化。

3. MySQL的主从复制原理,如何保证数据一致性?

主从复制原理:

MySQL主从复制是异步复制机制,用于实现数据备份和读写分离。

复制过程分为三个步骤。第一步,主库执行SQL语句后,把数据变更记录到binlog二进制日志。binlog有三种格式:Statement记录SQL语句,Row记录数据变更,Mixed混合模式。

第二步,从库的IO线程连接主库,读取binlog内容,写入从库的relay log中继日志。主库有一个binlog dump线程负责发送binlog给从库。

第三步,从库的SQL线程读取relay log,解析并执行SQL语句,重放主库的数据变更。这样从库的数据就和主库保持一致。

复制延迟问题:

主从复制是异步的,存在延迟。主库写入后,从库可能还没同步完成,这时读从库会读到旧数据。

延迟的原因有几个。一是网络延迟,主从库之间的网络传输需要时间。二是从库性能差,SQL线程执行慢。三是主库写入量大,从库来不及同步。四是大事务,一个事务修改大量数据,从库重放需要很长时间。

保证数据一致性:

第一种方案是使用半同步复制。主库写入binlog后,至少等待一个从库接收到binlog并写入relay log,才返回成功。这样保证至少有一个从库有最新数据,但会影响性能。

第二种方案是读写分离时强制读主库。对于必须读最新数据的场景,比如用户刚修改完信息立即查询,强制读主库。其他场景读从库。

第三种方案是延迟读取。写入后等待一段时间再读取,比如等待1秒,让从库有时间同步。但这种方式用户体验不好。

第四种方案是使用缓存。写入时同时更新缓存,读取时先读缓存。这样即使从库有延迟,也能读到最新数据。

第五种方案是监控主从延迟。通过show slave status查看Seconds_Behind_Master,如果延迟超过阈值,暂时不读该从库。

实际使用中,要根据业务特点选择方案。对一致性要求高的用半同步复制或强制读主库。对一致性要求不高的可以接受短暂延迟。

4. 分布式锁的实现方式,Redisson的原理

分布式锁的必要性:

在分布式系统中,多个服务实例可能同时操作共享资源,需要分布式锁保证互斥。比如秒杀场景,多个服务实例同时扣减库存,需要加锁保证原子性。

实现方式:

基于数据库实现,可以用唯一索引或for update悲观锁。优点是简单,缺点是性能差,数据库压力

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java面试圣经 文章被收录于专栏

Java面试圣经,带你练透java圣经

全部评论

相关推荐

昨天 18:11
门头沟学院 Java
想要实习的牛:这么牛逼的简历都吃瘪吗🌚那我不寄了
点赞 评论 收藏
分享
刚刷到字节跳动官方发的消息,确实被这波阵仗吓了一跳。在大家还在纠结今年行情是不是又“寒冬”的时候,字节直接甩出了史上规模最大的转正实习计划——ByteIntern。咱们直接看几个最硬的数,别被花里胡哨的宣传词绕晕了。首先是“量大”。全球招7000多人是什么概念?这几乎是把很多中型互联网公司的总人数都给招进来了。最关键的是,这次的资源分配非常精准:研发岗给了4800多个Offer,占比直接超过六成。说白了,字节今年还是要死磕技术,尤其是产品和AI领域,这对于咱们写代码的同学来说,绝对是今年最厚的一块肥肉。其次是大家最关心的“转正率”。官方直接白纸黑字写了:整体转正率超过50%。这意味着只要你进去了,不划水、正常干,每两个人里就有一个能直接拿校招Offer。对于2027届(2026年9月到2027年8月毕业)的同学来说,这不仅是实习,这简直就是通往大厂的快捷通道。不过,我也得泼盆冷水。坑位多,不代表门槛低。字节的实习面试出了名的爱考算法和工程实操,尤其是今年重点倾斜AI方向,如果你简历里有和AI相关的项目,优势还是有的。而且,转正率50%也意味着剩下那50%的人是陪跑的,进去之后的考核压力肯定不小。一句话总结: 27届的兄弟们,别犹豫了。今年字节这是铁了心要抢提前批的人才,现在投递就是占坑。与其等到明年秋招去千军万马挤独木桥,不如现在进去先占个工位,把转正名额攥在手里。
喵_coding:别逗了 50%转正率 仔细想想 就是转正与不转正
哪些公司开暑期实习了?
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务