北京明光云振铎数据科技Java面经

Nacos、OpenFeign、Gateway 三个组件的作用及协作流程

首先:

  • Nacos 主要负责服务注册发现和配置中心
  • Gateway 作为统一网关入口,负责路由、鉴权、限流
  • OpenFeign 负责服务之间的远程调用

用户请求先进入 Gateway

Gateway 会先做 JWT 鉴权,比如校验 token 是否合法,然后解析 userId,通过 Header 透传给后面的微服务。之后 Gateway 会根据路由规则,把请求转发到对应服务。

这个时候 Gateway 并不是写死 IP,而是通过 Nacos 获取服务实例列表,实现动态服务发现和负载均衡。

比如:

复制代码
/api/order/**
→ cloud-service-order

真正调用时:

Gateway 会从 Nacos 拿到 order-service 的实例。

后面如果 order-service 需要调用 user-service:

就会通过 OpenFeign 发起 RPC 调用。

OpenFeign 底层同样会从 Nacos 获取 user-service 的实例地址,然后通过 LoadBalancer 做负载均衡。所以整体链路是:

复制代码
Client
→ Gateway
→ Nacos 服务发现
→ OrderService
→ OpenFeign
→ UserService

这样整个系统就具备:

  • 服务解耦
  • 统一入口治理
  • 配置动态管理

MySQL 慢查询如何分析?索引优化有哪些?

1.查看慢查询日志

2.explain对应的sql查询语句

3. 分析是否命中索引
4. 分析索引是否失效
5. 分析是否存在回表 ( filesort / temporary)

6.判断是否需要分库分表优化
7.查看是不是数据库连接池满了导致的偶尔慢查询

Redis 缓存穿透、击穿、雪崩的区别及解决方案

缓存穿透:是查询一个不存在的数据。

因为缓存和数据库都没有,请求会直接打到数据库。

解决方案一般是:

  • 缓存空值(redis缓存空值)
  • 布隆过滤器

缓存击穿:是某个热点 key 突然失效。

大量请求同时访问数据库,这个是高并发场景最危险的。

解决方案:

  • Redis 分布式锁(setnx + expire+Lua 保证原子性)
  • 热点数据永不过期

缓存雪崩:是大量 key 同时过期,导致数据库瞬间压力暴增。

解决方案:

  • 过期时间随机化
  • 多级缓存
  • 熔断降级
  • 限流保护

定时任务的实现方式;如何避免集群环境下任务重复执行?如何防止任务堆积?

我项目里定时任务用的是 XXL-JOB。

调度中心统一触发任务 ,业务服务作为 执行器 Executor 注册到调度中心。

这样在集群环境下,调度中心会根据路由策略选择某个执行器节点执行任务,不会让每个节点都各自执行一遍。

比如我会配置:

复制代码
路由策略:第一个 / 轮询 / 一致性 Hash / 分片广播
阻塞处理策略:单机串行
失败重试次数:有限次数

其中避免重复执行主要靠两点:

第一,任务不是每个节点自己触发,而是由 XXL-JOB 调度中心统一调度

第二,对于同一个任务,可以配置 单机串行,避免上一次还没执行完,下一次又重复触发。

依赖包引入导致脏数据的识别规则及自动清理实现方式

识别规则

  1. 唯一性校验

比如用户 id、手机号、业务单号不能重复。

  1. 完整性校验

必填字段不能为空,比如 userId、tenantId、status。

  1. 时间有效性校验

比如过期数据、历史版本数据不能覆盖新数据。

自动清理实现上,我一般不会直接物理删除,而是采用:

定时任务扫描

→ 按规则识别脏数据

→ 标记为 DIRTY / INVALID

→ 记录清理日志

→ 异步清理或人工复核

数据更新时,如何保证用户服务中缓存数据及时清除与更新?

用户数据更新时,我不会直接更新缓存,而是先更新数据库,再删除缓存。

因为缓存本身是派生数据,数据库才是主数据源。

具体流程是:

用户服务更新 MySQL 成功后,删除 Redis 中的缓存

如果系统有多级缓存,还会发布 MQ 消息,让其他服务或其他节点删除本地缓存


对于并发场景,可以加延迟双删,降低旧数据重新写入缓存的概率。

延迟双删是什么

"更新数据库后,删除两次缓存"

如何用Java 和 Redis 实现滑动窗口限流器

我会用 Redis 的 ZSET 实现滑动窗口限流。

ZSET 的 score 存请求时间戳,member 存请求唯一标识。

每次请求进来,先删除窗口外的旧请求,再统计当前窗口内请求数量。

如果数量超过阈值,就拒绝;如果没超过,就把当前请求写入 ZSET。

由于删除、统计、判断、写入这几个操作必须保证原子性,所以我会用 Lua 脚本在 Redis 端一次性执行,避免并发场景下限流失效。

相关推荐
亦暖筑序5 小时前
Java 8老系统AI Workflow实战:把一次性AI对话升级成可恢复工作流
java·后端
敲代码的彭于晏6 小时前
Bean 生命周期完全图解:前端同学也能看懂的 Spring 核心机制
java·前端·后端
plainGeekDev7 小时前
ButterKnife → ViewBinding
android·java·kotlin
像我这样帅的人丶你还1 天前
Java 后端详解(四):分页与搜索
java·javascript·后端
她的男孩1 天前
数据权限为什么不能只靠注解?Forge 的 Mapper 层 SQL 改写源码拆解
java·后端·架构
tntxia1 天前
Mybatis的日志输入
java
亦暖筑序1 天前
Java 8老系统Browser Agent实战:三层拦截把AI操作后台变成可审计流程
java·后端·设计模式
用户298698530141 天前
Java 实现 Word 文档加密与权限解除
java·后端
Yeats_Liao1 天前
14:Servlet中的页面跳转-Java Web
java·后端·架构
未秃头的程序猿1 天前
告别"if-else地狱"!Java 21模式匹配,代码优雅了10倍
java·后端·面试