互联网大厂Java面试实录:从电商秒杀到微服务治理,谢飞机与面试官的极限拉扯(含Spring Cloud、Kafka、Redis、JVM、安全框架、大数据等全栈技术详解)

第一轮:电商秒杀场景下的JVM与缓存架构

面试官端坐在对面,目光如炬,手中的简历上写着“精通Java,熟悉高并发”。谢飞机穿着印着“Bug?”的T恤,有些紧张地搓了搓手。

面试官:先做个简单自我介绍吧。我们部门主要做电商核心交易链路,日活千万级,经常搞秒杀。你之前有接触过这类业务吗?

谢飞机:(清了清嗓子)有的有的,我之前在“拼少少”商城实习过,搞过图书秒杀,当天来了几百个人,服务器差点崩了,后来我加了缓存就好了。

面试官:哦?那你说说,秒杀场景下,你如何设计一个商品的库存扣减方案?假设库存只有100件,但瞬间有10万请求,怎么保证不超卖?

谢飞机:这个我知道,加锁!用Redis的setnx,把库存放Redis里,先看有没有key,有就减一,减到0就返回卖完了。用incr也行,反正别用数据库update,会锁表,慢死了。

面试官:那如果Redis里的库存和数据库里的库存不一致了怎么办?Redis挂了怎么办?还有,你的setnx能处理分布式锁的过期时间吗?如果业务执行时间超过了锁的过期时间,会出现什么问题?

谢飞机:(挠头)啊……这个……锁过期,那别的线程就进来了,就超卖了……嗯,可以用Redisson的看门狗,自动续期!Redis挂了就……就……用本地锁?Ehcache?不对不对,本地锁在集群下就失效了。

面试官:那你再说说,秒杀请求进来后,JVM层面你会怎么调优?你的服务部署了多少实例?线程池怎么设置?GC用哪种收集器?

谢飞机:JVM调优……我一般就调-Xmx-Xms一样大,避免扩容。GC用CMS?不,现在都用G1了。线程池用ThreadPoolExecutor,核心线程数……CPU核数+1?队列用有界队列,满了用CallerRunsPolicy?呃,好像是AbortPolicy……我不确定,反正拒绝策略要抛异常吗?我有点记混了。

面试官:(微微一笑,没有直接评价)好的,你通过Redis预减库存,但数据库最终一致性怎么保证?如果Redis扣减成功了,但数据库更新订单失败了,你怎么办?

谢飞机:用消息队列?把扣减成功的消息发到Kafka,消费者异步去更新库存,如果失败就重试。如果重试还失败就……就记日志,人工补偿?哈哈,我们之前就人工改过数据库。

面试官:你提到Kafka,那如果Kafka自己挂了,或者消息丢了,你的数据不就对不上了?

谢飞机:Kafka不是有复制吗?ACK机制,设置acks=allmin.insync.replicas……而且发送方用producer.send带回调,失败了可以重试。但应该不会丢吧?我听说Kafka很稳的。

面试官:那你再思考一个更深入的问题:秒杀接口你如何防刷?除了验证码,从安全角度讲,你如何在网关层做限流和风控?比如SameSite、Token、OAuth2这些?

谢飞机:防刷……用Nginx限制IP连接数,再用Spring Cloud Gateway的RequestRateLimiter做令牌桶限流。Token用JWT,登录后发一个,每次请求带上。OAuth2是授权码模式,但那是第三方登录用的。SameSite……好像是Cookie的属性,防止CSRF攻击的?具体我忘了。风控我用过Alibaba Sentinel,但不熟。

面试官:行,你基础还是有点的,虽然细节把握不准。我们换个方向,聊聊微服务和数据一致性吧。

第二轮:微服务拆分、分布式事务与链路追踪

面试官:你在技术栈里写了Spring Cloud、Dubbo、gRPC这些。如果让你把一个电商系统拆分为微服务,你怎么拆分?服务之间怎么调用?比如用户下单,需要调用库存服务、订单服务、支付服务,还有优惠券服务,这个流程你用什么方案保证数据一致性?

谢飞机:拆分按业务域拆啊,用户、商品、订单、库存、支付、优惠券,每个独立数据库。调用用OpenFeign,声明式HTTP客户端,方便!或者用Dubbo,RPC性能好。分布式事务用Seata的AT模式,或者TCC模式。但TCC要写Confirm和Cancel,太复杂了,我们之前用消息最终一致性,把下单事件发到RocketMQ,下游服务消费,各自处理。

面试官:那如果A服务调用B服务,B服务调C服务,C服务调D服务,最后D返回超时,导致B的线程阻塞,怎么定位是哪个服务出了问题?

谢飞机:用链路追踪!Jaeger或者Zipkin,还有Micrometer,把traceId和spanId打到日志里,用ELK搜索,看哪个环节慢了。还有SkyWalking也行,不过没写在我简历上。

面试官:你提到了ELK,那你知道从几百GB的日志中查询一条报错,有什么优化手段吗?比如索引怎么设计?Kafka在ELK中扮演什么角色?

谢飞机:日志采集用Filebeat,发到Kafka,然后Logstash消费,清洗,写到Elasticsearch,Kibana展示。优化就是……分索引,按天建索引,用别名查询。可以用Elasticsearch的倒排索引,搜关键词很快。但日志多的时候,查询也慢,要设置refresh_interval,或者用冷热分离?我还没弄过。

面试官:接着说,你刚才提到服务间用OpenFeign,那如果下游服务响应很慢,你如何做超时控制和熔断降级?你了解Resilience4j吗?

谢飞机:OpenFeign可以配置connectTimeoutreadTimeout。熔断用Resilience4j,有CircuitBreaker,默认滑动窗口大小……是10?失败率超过50%就打开断路器,然后走Fallback方法。还可以用线程池隔离或者信号量隔离。但具体配置参数我不太记得了,之前是抄的网上的配置。

面试官:那如果网关层需要聚合多个服务的数据,比如商品详情页需要显示商品信息、商家信息、评论数、销量,你会怎么做?用异步编排还是并行调用?

谢飞机:用CompletableFuture!把几个调用组合起来,allOf等待全部完成,或者thenCombine合并结果。也可以用Spring WebFlux的Mono.zip,但WebFlux我没实战过。哦对了,也可以用gRPC,支持流式调用,但聚合还是得自己写。

面试官:你刚才提到Redis Pub/Sub,这也能用做消息队列吗?和Kafka有什么区别?

谢飞机:Redis Pub/Sub是轻量级的,消息会丢,没有持久化,消费者必须在线才能收到。Kafka是持久化的,有offset,消费者可以重放。所以我们只用Redis Pub/Sub做集群内广播,比如缓存刷新通知,不用来做核心消息。如果要可靠消息,得用Kafka或者RabbitMQ。

面试官:那么RabbitMQ和Kafka你分别用在什么业务场景?为什么两个都写?

谢飞机:Kafka吞吐量高,适合日志、埋点、异步大数据流。RabbitMQ有完善的交换机路由,延迟低,适合业务消息,比如订单状态变更发通知。我们订单创建后发一个RabbitMQ消息给积分服务,积分服务消费加积分。

面试官:如果订单服务发了一条消息,但是积分服务消费失败了,而且这个消息已经被自动ACK了,怎么办?

谢飞机:可以把ACK改成手动,消费失败就nack,重新入队,或者结合重试机制。如果重试多次还不行,就存到死信队列里,人工补偿。RabbitMQ有死信交换机,配置x-dead-letter-exchange

面试官:好,你对这些中间件的理解差不多。那么我们聊聊数据存储和查询优化,比如MySQL和Elasticsearch如何保持同步?

第三轮:数据库同步、对象存储与大数据分析

面试官:电商系统中,商品信息、订单列表这类查询量大的数据,你一般怎么处理?MySQL是关系型数据库,但复杂查询和全文搜索很慢,你会怎么优化?

谢飞机:把商品数据同步到Elasticsearch,用ES做搜索和过滤。同步方式用Canal监听MySQL的binlog,解析后发到Kafka,下游消费数据写入ES。或者用定时任务扫表增量更新,但实时性不行。也可以用Spring Data Elasticsearch直接写。

面试官:那订单表数据量到了上亿,你怎么做分库分表?用什么中间件?

谢飞机:分库分表啊,用ShardingSphere!按用户ID取模分表,比如分1024张表。查询订单列表一定要带上userId,否则会全路由。跨分页查询用中间件聚合。不过我没真做过上亿的数据,我们之前就几百万,加索引就查得动。

面试官:在电商场景里,还有商品图片、用户头像,这些文件存在哪里?如果要用Java服务上传下载,你会用什么?

谢飞机:存对象存储,阿里云OSS或者MinIO。本地开发用MinIO,上传用multipart,下载用InputStreamResource。如果图片处理,比如生成缩略图,用Thumbnator或者ImageIO。还有,文件名要用UUID,加上日期目录,防止重名。签名URL可以做临时访问。

面试官:除了图片,还有音视频,比如商品介绍视频,用户上传视频之后需要对视频转码、切片,你怎么设计流程?

谢飞机:视频上传后,发个消息到Kafka,有个专门的转码服务去消费,用FFmpeg转成HLS格式,再分片传回OSS。转码进度存Redis,前端轮询进度条。如果要智能审核,接入第三方的鉴黄接口?哈哈,我们没做过。

面试官:假设现在要做用户行为的实时分析,比如推荐商品点击流,数据源是日志,实时计算PV/UV,你用什么技术?

谢飞机:用Flink!Flink消费Kafka里的用户点击日志,做窗口聚合,比如每10秒输出一次PV/UV,结果写入Redis,然后Grafana展示。或者用Spark Streaming也行,但Flink的窗口更强大。但我的Flink水平一般,就会写个KeyedProcessFunction

面试官:那如果要做一个用户画像,比如根据用户的消费记录、浏览记录、收藏记录,给用户打标签,比如“高消费”“母婴用户”,你怎么存储这些标签?怎么查询?

谢飞机:标签用位图存储?用Redis的Bitmap?或者用ES的聚合?不太懂。我们当时用一个宽表,把所有标签存成字段,用户ID为主键,查询时直接查。更新标签就写异步任务更新字段。但这样字段会很多,得用动态Schema?好像不好搞。可以试试ClickHouse,但没写过。

面试官:互联网医疗怎么样?我们公司也做健康管理,比如患者上传体检报告,需要解析PDF,提取指标异常项,你怎么做?

谢飞机:PDF解析用Apache PDFBox,OCR的话用Tesseract。提取文本后,用正则或者自然语言处理。指标异常的话,根据参考范围写规则引擎,比如肝功能,ALT大于40就是异常。如果结构化,用Fastjson解析,存入MySQL。但这个准确率不高吧?需要算法团队配合。

面试官:你提到了Fastjson,那你对序列化方案怎么选?比如服务间通信用JSON、Protobuf、Avro?各有什么优缺点?

谢飞机:JSON简单通用,用Jackson或者Gson,但是性能差一点。Protobuf性能高,二进制,需要定义.proto文件,生成代码。Avro兼容Schema演化,适合大数据。我们内部RPC用Protobuf,对外API用JSON。但有一次我们改了字段类型,导致老客户端解析报错,后来才知道兼容性很重要。

面试官:最后一个问题,你如何理解“云原生”?Kubernetes在服务部署中怎么用?你写过Dockerfile吗?

谢飞机:容器化用Docker,镜像用JDK17的基础镜像,然后buildpush到仓库。Kubernetes用Deployment管理Pod,用Service暴露端口,用Ingress做网关。还有HPA水平自动扩缩容,根据CPU使用率。滚动更新,先启动新Pod,等就绪后再杀掉旧Pod。不过我实习时只是看看YAML,都是运维在弄,但我知道流程。

面试官:好的,谢飞机,你今天的表现让我印象深刻——虽然很多地方你只知道“大概”“可能”,但至少你的知识广度还不错,而且没有不懂装懂。我们这轮面试先到这里,你回去等通知吧。如果后续有结果,HR会在一周内联系你。谢谢你。

谢飞机:(站起来,鞠了一躬)谢谢面试官!我回去一定把“确定性”补好,争取下次能答得更准确!

面试官:(笑了笑)记住,学习要从原理出发,别只背概念。再见。


附:详细答案与业务场景技术点解析

这篇文章不仅是段子,更是一份Java后端面试学习指南。下面按面试官提问的顺序,结合业务场景,详细讲述每个技术点的核心知识和学习思路。

第一轮详解:电商秒杀场景

1. 秒杀库存扣减方案,如何保证不超卖?

典型业务场景:商品秒杀,库存100件,瞬时10万请求。目标:不超卖、不崩溃、最终一致。

错误方案

  • 直接用数据库UPDATE stock SET remaining = remaining - 1 WHERE product_id = ? AND remaining > 0,虽然单条SQL原子,但高并发下数据库连接池被打满,行锁竞争严重。

正确方案

  • 采用“Lua脚本 + Redis”原子扣减。把库存放入Redis,用Lua脚本保证读取库存和扣减库存原子性,如:
local stock = tonumber(redis.call('get', KEYS[1]))
if stock <= 0 then
  return -1
end
redis.call('decrby', KEYS[1], ARGV[1])
return stock - ARGV[1]
  • 这样,每个请求只产生一次Redis原子操作,性能高且不超卖。
  • 数据库层最终扣减:异步通过消息队列(如Kafka)同步到数据库,使用UPDATE ... WHERE product_id = ? AND remaining >= ?做兜底,保证最终一致。

补充:如果使用分布式锁扣库存,不建议将库存操作放在锁内(性能太低);一般用Redis原子操作替代分布式锁。

2. Redis和数据库不一致了怎么办?Redis挂了怎么办?
  • 不一致原因:Redis扣减成功,但发送消息失败或数据库更新失败。
  • 解决方案
    1. 本地消息表:在同一个数据库中记录“库存扣减事件表”,先写事件后发消息,发消息成功后标记已发送。
    2. 重试机制:消费者消费失败,使用Kafka的重试机制(retriesenable.idempotence)或Spring Retry。
    3. 对账补偿:定时任务比较Redis剩余库存和数据库剩余库存,差异进行回滚或补偿。
    4. Redis持久化:开启AOF和RDB,即使Redis重启也能恢复,防止库存丢失。
    5. Redis高可用:使用主从复制 + 哨兵或Redis Cluster,避免单点故障。
3. 分布式锁的过期时间问题与Redisson看门狗
  • 问题setnx + expire不是原子操作,如果设置过期时间后业务线程还没执行完,锁自动释放,另一个线程进入临界区,导致数据异常。
  • 正确方式
    • 使用set key value NX PX 30000,原子设置锁和过期时间。
    • 使用Redisson的RLock,其默认看门狗机制会每10秒检查一次,如果锁还持有,就自动续期到30秒,避免业务未完成锁被释放。
    • 释放锁时要校验value(线程标识),防止误删别人的锁。
4. 秒杀服务的JVM调优
  • 堆内存-Xms-Xmx设置相同,避免运行期动态扩容减小停顿。
  • GC选择
    • JDK8默认ParallelGC,但秒杀场景要求低延迟,推荐使用G1(-XX:+UseG1GC)。
    • G1可设置-XX:MaxGCPauseMillis=50,控制GC停顿时间。
  • 线程池
    • 不能使用无界队列,否则请求堆积导致内存溢出。有界队列大小结合压测结果,如ArrayBlockingQueue(1000)
    • 拒绝策略建议使用CallerRunsPolicy(调用者执行,降低请求速度)或抛异常,但不能直接丢弃。
    • 核心线程数设置:IO密集型一般设置为CPU核数 * 2,或者使用ThreadPoolTaskExecutor配置参数。
  • 秒杀请求越早拒绝越好:在入口处用令牌桶限流(如Guava RateLimiter或Sentinel),避免流量冲击JVM。
5. 消息队列Kafka的可靠性
  • Kafka防止消息丢失
    • 生产者:acks=all(等待所有ISR副本确认),retries>0enable.idempotence=true
    • 消费者:关闭自动提交(enable.auto.commit=false),在业务处理成功后手动提交偏移量(commitSynccommitAsync)。
    • 副本:min.insync.replicas=2,保证至少两个副本同步。
  • Kafka挂了? Kafka本身就是分布式集群,用副本机制保证可用性。但如果在极端情况下集群全部宕机,可以在生产者发送时配置max.block.ms,并且使用本地写盘或失败回滚业务。
6. 秒杀接口防刷与安全
  • 网络层:Nginx限制单IP并发连接数和速率。
  • 网关层:Spring Cloud Gateway使用RequestRateLimiter(Redis实现令牌桶)进行全局限流。
  • 验证码:图片/滑块验证码,防止脚本轰炸。
  • JWT Token
    • 登录后服务器签发JWT,客户端在Authorization头携带。
    • JWT结构:Header.Payload.Signature,签名防篡改,用于身份认证,但无法主动失效(需配合黑名单或短过期时间)。
  • OAuth2:如果涉及第三方登录或开放API,使用授权码模式,通过Keycloak等搭建授权服务器。
  • SameSite属性:Cookie的SameSite=Lax/Strict可以防止CSRF攻击,因为跨站请求不会携带Cookie。
  • 结构化风控:使用Redis记录用户行为(如点击频率、黑名单),或者接入专业风控系统。

第二轮详解:微服务治理与消息中间件

1. 微服务拆分原则
  • 按业务域(也叫限界上下文)拆分,例如:用户服务、商品服务、订单服务、库存服务、支付服务、优惠券服务。
  • 每个服务独立数据库,服务间通过API而不是直接访问数据库。
  • 服务调用方式:
    • 同步:OpenFeign(HTTP + JSON),适合强一致性、低延迟小数据量。
    • 异步:消息队列,适合最终一致性、削峰填谷。
    • 高性能RPC:Dubbo / gRPC,二进制协议,适合内部高吞吐调用。
2. 分布式事务方案
  • Seata AT模式:无侵入,全局事务 через @GlobalTransactional,适合中小并发,但牺牲性能。
  • TCC模式:Try(锁定资源)、Confirm(提交)、Cancel(回滚),需要业务实现三个方法,适合对一致性要求高的场景。
  • 消息最终一致性
    • 本地消息表:在开启本地事务时写业务表 + 消息表,然后通过定时任务或MQ发送。
    • 事务消息:RocketMQ支持半消息,先发送half消息,本地事务成功后再发送commit消息,消费者只能消费commit后的消息。
  • 在本案例中:用户下单流程中,先创建订单(状态=待支付),发消息给库存服务锁定库存;支付成功后发消息通知库存服务确认真实扣减,通知用户服务增加积分等。
3. 链路追踪与排查
  • Spring Cloud SleuthMicrometer Tracing生成traceId和spanId。
  • 将traceId放在MDC(Mapped Diagnostic Context)中,这样日志输出时自动带上traceId。
  • 日志收集至Elasticsearch后,用Kibana搜traceId,就能看整条调用链路各环节耗时。
  • Zipkin/Jaeger专门做链路追踪,展示调用拓扑和耗时瀑布图。
4. ELK优化要点
  • Filebeat轻量采集日志,发送到Kafka缓冲。
  • Logstash消费Kafka进行清洗、解析,再输出到ES。
  • ES索引优化
    • 按天创建索引:log-2025-01-01,过期数据可删除或归档。
    • 设置别名(alias)让应用透明访问。
    • 设置refresh_interval=30s-1,减少IO开销。
    • 使用routing字段(如按服务名路由)提高查询并发。
  • 冷热分离:热数据放在SSD,冷数据用普通磁盘或对象存储。
5. OpenFeign超时与Resilience4j熔断
  • OpenFeign全局超时配置
feign:
  client:
    config:
      default:
        connectTimeout: 3000
        readTimeout: 5000
  • Resilience4j核心组件:
    • CircuitBreaker:支持滑动窗口(计数/时间),默认失败率阈值50%,最小调用数等。
    • Bulkhead:线程池隔离或信号量隔离。
    • RateLimiter:限速。
    • Retry:重试。
  • 降级:在@FeignClientfallback接口中实现默认逻辑,比如返回兜底的数据或空对象。
6. 异步聚合服务调用
  • 如果网关需要调用多个服务,串行耗时为N个服务耗时之和;并行可缩短为最大耗时。
  • Java实现方式:
    • CompletableFutureallOf(service1.getData(), service2.getData()).join(),然后组合结果。
    • 线程池需要专门配置,不要让任务走默认ForkJoin池。
  • Spring WebFlux的Mono.zip是响应式方式,但需要全链路响应式,学习成本高。
7. Redis Pub/Sub vs Kafka

| 特性 | Redis Pub/Sub | Kafka | | --- | --- | --- | | 消息持久化 | 无 | 有(磁盘) | | 消费模式 | 广播(所有订阅者都收到) | 消费者组(一条消息一个组内消费者消费) | | 消费者离线 | 错过消息 | 可重放(根据offset) | | 吞吐量 | 较低 | 极高 | | 适用场景 | 服务间广播、缓存失效通知 | 日志、事件流、异步业务 |

注意:Redis Pub/Sub的“频道”不是消息队列,不保证可靠性,不适用于核心交易。

8. RabbitMQ手动ACK与死信队列
  • 手动ACK:
    • channel.basicAck(deliveryTag, false)确认成功。
    • channel.basicNack(deliveryTag, false, true)拒收并重新入队。
    • 如果消费失败,不要无限重新入队,设置重试次数或延迟重试。
  • 死信队列(DLX):
    • 通过参数x-dead-letter-exchange声明一个死信交换机。
    • 当消息被basicNack且requeue=false、或TTL过期、或队列满了时,消息进入死信队列。
    • 死信队列消费程序可以记录错误、告警并人工处理。

第三轮详解:数据层、文件存储与大数据

1. MySQL与Elasticsearch的数据同步
  • Canal方案
    1. Canal伪装成MySQL Slave,拉取binlog(row格式)。
    2. 将binlog解析为事件,发送到Kafka。
    3. 消费者(如Java服务)更新Elasticsearch。
  • 优点:实时性好,无侵入。
  • 其他方案:定时任务扫描,如每5分钟查update_time > lastTime,批量写入ES。适合对实时性要求不高的场景。
  • 注意:ES不是主数据库,只做搜索和查询,数据必须允许最终一致。
2. 分库分表与ShardingSphere
  • 分库分表核心策略
    • 订单表按用户ID分片:user_id % 1024,保证同一用户的订单落在同一分片。
    • 查询时必须带上分片键,否则全路由导致性能骤降。
    • 全局ID用Snowflake算法生成。
  • ShardingSphere优势:
    • Java生态中的客户端分库分表方案,支持分片、读写分离、分布式事务(接入Seata)。
    • 也有Proxy模式,但性能和功能不如客户端。
  • 注意:分页查询跨分片时,每个分片都查limit offset+size,中间件聚合排序,深层分页性能很差,建议用游标或深度分页优化。
3. 对象存储与文件上传
  • MinIO / OSS解决海量文件存储、高可用、CDN加速。
  • 关键实践:
    • 桶(Bucket)内按日期和业务建目录。
    • 文件名为UUID,避免覆盖。
    • 上传时使用预签名URL(Pre-signed URL),直接由前端上传到OSS,减轻服务器压力。
    • 下载同理,使用临时URL。
  • 图片处理:OSS自带图片缩放、裁剪、水印;本地也可以用Thumbnator。
4. 视频上传与转码流程
  • 用户上传原始视频到OSS。
  • 上传完成后,服务端生成一条“视频转码任务”发到Kafka。
  • 转码服务消费任务,使用FFmpeg命令行或JavaCV进行转码处理:
    • 将MP4转成HLS(m3u8 + ts分片),支持流式播放。
    • 生成多种清晰度(720p、1080p)。
    • 抽取第一帧作为封面。
  • 转码结果写回OSS,将状态更新到数据库。
  • 前端轮询任务状态接口,或使用WebSocket推送进度。
5. Flink实时点击流分析
  • 典型场景:统计商品PV/UV。
  • 实现:
    • 日志通过Nginx上报到Kafka。
    • Flink从Kafka读取数据流,使用KeyedProcessFunction按商品ID分组。
    • 用滚动窗口(TumblingEventTimeWindows)或滑动窗口(SlidingEventTimeWindows)计算。
    • UV使用SetStateDescriptor去重,或者使用HyperLogLog(近似)。
    • 结果输出到Redis或Elasticsearch。
  • Flink的checkpoint机制保证exactly-once语义。
6. 用户画像标签存储
  • 标签模型:多值标签(如“高消费”“母婴”“90后”)。
  • 存储选型
    • 宽表:用户ID为行,每个标签为列。适合标签数量固定;但新增标签需要改表。
    • Redis Bitmap:每个标签一个bitmap,用户ID作为offset,通过位运算快速计算交集/并集。适合千人千面标签圈选。
    • Elasticsearch:每条用户数据是一个document,标签为字段,用TermQuery筛选。适合多维查询,但内存占用大。
    • 列式数据库:ClickHouse / StarRocks,适合海量标签分析和人群圈选。
  • 实际工业级做法是用ClickHouse存储用户标签宽表,位图和倒排索引加速。
7. PDF解析与医疗指标提取
  • 使用Apache PDFBox或iText读取PDF文本。
  • 如果是扫描件,需要OCR(Tesseract)识别。
  • 指标提取:通过正则表达式解析“项目名称:值 单位 参考范围”。
  • 异常判断:内置规则引擎,如谷丙转氨酶ALT > 40 U/L为偏高。
  • 结构化数据存入MySQL或MongoDB,前端展示。
  • 注意:医疗数据必须合规,脱敏存储。
8. JSON、Protobuf、Avro选型
  • Jackson/Gson:JSON动态灵活,便于调试,但序列化长度大、解析慢。
  • Protobuf:二进制,体积小、解析快,需要编写.proto文件并编译生成Java类。适合内部RPC。
  • Avro:二进制,自带Schema,支持Schema演化(读写Schema可以不同),适合大数据场景(如Kafka、Hadoop)。
  • 选型建议:
    • 外部API用JSON。
    • 服务间高并发RPC用Protobuf。
    • 数据仓库 / Flink处理用Avro。
9. 云原生与Kubernetes部署
  • Docker化:编写Dockerfile,使用多阶段构建,基础镜像尽量选择eclipse-temurin:17-jre-alpine
  • Kubernetes核心概念
    • Deployment:管理Pod副本数、滚动更新策略。
    • Service:为Pod提供稳定访问入口。
    • Ingress:七层负载均衡,按域名路由。
    • HPA(HorizontalPodAutoscaler):根据CPU/内存或自定义指标自动扩缩容。
  • 优雅上线/下线
    • 配置readinessProbelivenessProbe
    • 服务停止时,Spring Cloud会向注册中心下线,再处理完存量请求后退出。
  • 面试中应至少说出一个完整部署流程:代码提交 → CI构建镜像 → 推送镜像仓库 → 更新Deployment → K8s滚动升级。

后记:谢飞机虽然答得“稀碎”,但他展示了知识面广的优点。真正的Java工程师不仅要会用,还要知道底层原理、为什么这么设计。建议读者按照这篇文章的路线,从电商场景出发,把每个技术点的官方文档和经典案例都过一遍,形成自己的知识体系。下次面试,就不用回家等通知了,而是现场拿Offer。

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐