面灵AI→

汇川技术全栈一面:JWT、AOP 与微服务八股

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

《面试题目》

  1. 自我介绍。
  2. 实习业务介绍。
  3. JWT 的流程是什么?为什么要用 Redis 存 JWT?
  4. 讲一讲项目中用到的 AOP。
  5. 你使用什么数据库比较多?有没有用过其他的?
  6. 做接口优化主要从哪方面来讲?
  7. 创建线程的方式有哪些?
  8. 用过 Docker 吗?
  9. 怎么保证 MQ 发消息丢不了?怎么保证幂等性?
  10. 拷打微服务,讲讲你知道哪些。
  11. 说一下 LangChain 和 LangGraph。
  12. 讲一讲 Spring 的循环依赖。
  13. MySQL 的事务隔离级别有哪些?默认的是哪个?
  14. MySQL 索引哪些情况下会失效?

《参考解析》

JWT 流程与为什么要配 Redis:JWT 是无状态令牌——用户登录后服务端用密钥签发一段 header.payload.signature,之后每次请求带上它,服务端验签并解出用户信息,不需要查库,天然适合分布式横向扩展。但纯 JWT 有两个硬伤:无法主动失效(签发后到期前一直有效,改密码、踢下线、封号都做不到)和无法维护会话状态(多端登录、单点登出、在线设备管理)。所以实践里会把 JWT 的 jti(唯一 ID)或整个令牌写进 Redis,并设成与过期时间一致的 TTL:校验时除了验签还要查 Redis 是否存在,登出就删 key,等于给无状态令牌加了一个可控的吊销列表。另外 JWT 的 payload 只是 Base64 编码不是加密,不能放敏感信息;密钥必须保管好,泄漏等于任意伪造身份。

AOP 在项目里的用途:核心是把横切关注点从业务代码里抽出来,常见场景是统一日志与链路追踪、接口耗时统计、权限校验、事务管理(@Transactional 本身就是 AOP)、幂等与限流、以及异常统一处理。实现上 Spring AOP 默认用 JDK 动态代理(基于接口),类没有接口时退化成 CGLIB 字节码增强;要能说清”同一个类内部方法自调用不会走代理”这个经典坑。

接口优化从哪切入:分四层。① 数据库层——加对索引、避免回表与深分页、减少 N+1 查询、读写分离;② 缓存层——热点数据上 Redis 或本地缓存,注意穿透/击穿/雪崩与一致性;③ 应用层——串行改并行、异步化非关键链路、批量化调用、减少对象创建与序列化开销;④ 架构层——分库分表、加 CDN、限流熔断。回答时最好带一条真实链路:先定位瓶颈在哪一层(压测 + 火焰图 + 慢 SQL),再动手,改完复测。

创建线程的方式:继承 Thread、实现 Runnable、实现 Callable + FutureTask(能拿返回值和异常)、通过线程池 ExecutorService 提交。生产里一律用线程池,因为线程的创建销毁成本高、数量不可控;要能说出线程池七个参数和执行流程(核心线程 → 队列 → 最大线程 → 拒绝策略),以及为什么推荐有界队列。

MQ 不丢消息 + 幂等:不丢要三段都保证——生产者用确认机制(Kafka acks=all + 重试、RocketMQ 同步发送)并处理好发送失败;Broker 侧持久化并做多副本(Kafka 的 ISR、RocketMQ 同步刷盘 + 主从);消费者改成手动 ACK,处理成功再提交位点。幂等则靠”消费端去重”:消息带唯一业务 ID,用 Redis SETNX 抢占或数据库唯一索引兜底,业务更新写成状态机式条件更新(WHERE status = ...)而不是累加,避免重复消费导致重复扣款。

微服务组件:注册与发现(Nacos/Eureka/Consul)、配置中心、网关(Spring Cloud Gateway)、负载均衡(Ribbon/LoadBalancer)、远程调用(Feign/Dubbo)、熔断限流降级(Sentinel/Hystrix/Resilience4j)、链路追踪(SkyWalking/Zipkin)、分布式事务(Seata)与消息中间件。答题时不要报菜名,挑两三个说清它解决的问题和引入的代价(如注册中心的高可用、分布式事务的一致性成本)。

LangChain 与 LangGraph:LangChain 以链式编排为主,用 Chain/LCEL 把提示、模型、工具串成一条流水线,适合线性流程;LangGraph 把流程建模成图——节点是执行单元、边可以是条件边,围绕共享 state 读写,因此天然支持循环、分支、并行和人工介入。更关键的是 LangGraph 有 Checkpoint 机制,能保存每一步的状态快照,从而支持断点续跑与回放,这也是需要多轮工具调用、容易失败重试的 Agent 场景更愿意用它的原因。要注意新版本 LangChain 也内置了 Agent 能力,两者并非互斥。

Spring 循环依赖:A、B 互相注入时,Spring 用三级缓存解决——singletonObjects(成品)、earlySingletonObjects(半成品)、singletonFactories(对象工厂)。创建 A 时先暴露一个能产出 A 早期引用的工厂,注入 B 时 B 再回来取 A 的早期引用,从而打破死循环。限制是:只对单例、且是 setter/field 注入有效;构造器注入的循环依赖解决不了(对象还没造出来就无法暴露引用),会直接启动报错;@Async 代理、原型作用域下同样失效。更推荐的做法是重构掉循环依赖,而不是依赖三级缓存兜着。

索引失效场景:① 对索引列做函数或运算(WHERE YEAR(create_time) = 2026);② 隐式类型转换(字符串列传数字);③ 前导模糊匹配 LIKE '%xx';④ 联合索引不满足最左前缀;⑤ 使用 OR 连接的条件中有一侧无索引;⑥ !=、NOT IN、IS NOT NULL 等在区分度低时走全表更划算;⑦ ORDER BY 的字段顺序与索引不一致导致额外排序。要注意”失效”有两种:一种是优化器根本用不上,另一种是能用但估算成本后主动放弃——后者不是 bug,得看 EXPLAIN 的 rows 和 filtered。