news 2026/10/5 2:25:17

项目中遇到过什么问题

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
项目中遇到过什么问题

1.spring cloud gateway 使用 redis-ratelimiter 来做限流,要引入spring-boot-starter-data-redis-reactive。

Spring Cloud Gateway 是 WebFlux 响应式框架,限流必须用spring-boot-starter-data-redis-reactive,不能用普通的 spring-boot-starter-data-redis,否则会报错。

Gateway 的RedisRateLimiter底层需要ReactiveRedisTemplate+ReactiveRedisConnectionFactory(响应式 Bean),只有spring-boot-starter-data-redis-reactive才会自动装配这两个 Bean; 如果你只引入普通spring-boot-starter-data-redis(Lettuce/Jedis 阻塞式):

✅项目可以正常启动,不会启动报错

❌一访问带 RequestRateLimiter 的路由,直接抛出 Bean 找不到异常

1. 最常见报错(标准日志)

No qualifying bean of type 'org.springframework.data.redis.core.ReactiveRedisTemplate<java.lang.String, java.lang.String>' available

或者:

No qualifying bean of type 'org.springframework.data.redis.connection.ReactiveRedisConnectionFactory' available

含义:RedisRateLimiter 尝试注入响应式 Redis 模板 Bean,但是容器里没有,因为普通 starter 只创建阻塞式 RedisTemplate,不创建 ReactiveRedisTemplate。

追问

阻塞式 RedisTemplate(spring-data-redis里的)

RedisTemplate是同步阻塞的 Redis 客户端 API,底层:

  • Jedis:本身就是阻塞 IO
  • Lettuce:虽然底层是 Netty 非阻塞,但RedisTemplate封装的方法是同步阻塞调用

一句话:调用redisTemplate.opsForValue().get("key")这行代码,当前线程会卡住,等待 Redis 返回结果,期间什么都不干,拿到结果才继续往下执行。

1. 对比:RedisTemplate(阻塞) vs ReactiveRedisTemplate(响应式)

特性RedisTemplate(阻塞)ReactiveRedisTemplate(响应式)
依赖 starterspring-boot-starter-data-redisspring-boot-starter-data-redis-reactive
返回值String、Object普通对象Mono<T>/Flux<T>
线程行为调用时阻塞当前线程,等待 IO不阻塞线程,注册回调,IO 就绪后再执行
适用场景Spring MVC(Servlet 线程池)WebFlux(事件循环 EventLoop 线程,Spring Cloud Gateway)

2. 阻塞的直观代码例子

// 阻塞式 RedisTemplate String value = redisTemplate.opsForValue().get("demo:key"); // 👉 执行get()的时候,当前线程停在这里! // 网络慢、Redis卡顿,线程就一直卡在这行等待返回

Servlet 体系下,这没问题:Servlet 线程池有很多线程,卡住一个只是消耗一个线程,线程池可以扩容。

但是 Spring Cloud Gateway 是 WebFlux,只有少量 EventLoop 事件线程!Gateway 默认就几个 EventLoop 线程(比如等于 CPU 核心数),EventLoop 线程绝对不能被阻塞。 如果在 EventLoop 里调用阻塞 RedisTemplate,一旦 Redis 慢:

  • EventLoop 线程卡死,没法处理其他请求
  • 请求排队堆积,网关整体卡死,雪崩

这就是为什么 Gateway 限流组件强制要求ReactiveRedisTemplate。

3. 补充:Lettuce 的特殊点(很多面试会问)

Lettuce 底层通信是 Netty 非阻塞,但是:

  • RedisTemplate包装的是同步 API,内部会调用.block()等待结果,变成阻塞调用
  • ReactiveRedisTemplate直接用 Lettuce 的响应式 API,不 block

误区纠正:不是 Lettuce = 响应式,Jedis = 阻塞。 Lettuce 两套 API 都有,看你用的是RedisTemplate还是ReactiveRedisTemplate。

4. 回到你刚才的 Gateway 限流场景

Gateway 的RedisRateLimiter是 WebFlux 过滤器,运行在 EventLoop 线程,它内部代码直接注入ReactiveRedisTemplate。

  • 只引入普通 redis starter:容器没有ReactiveRedisTemplateBean → 请求进来直接报 Bean 找不到
  • 如果你强行手动创建ReactiveRedisTemplate但用阻塞 API,属于高危操作,高并发会 eventLoop 阻塞。

面试精简版

阻塞式 RedisTemplate,是 SpringDataRedis 提供的同步 Redis 操作模板。调用方法时当前线程会阻塞等待 Redis IO 返回。适用于 Servlet MVC。Gateway 基于 WebFlux 事件循环,不能使用阻塞 API,必须用 ReactiveRedisTemplate,不能用普通 RedisTemplate。

2.

Gateway 网关服务 gateway‑server

❗重要:不要引入 spring‑boot‑starter‑web!gateway 使用 webflux。

Gateway 依赖 webflux,不能共存 web。

3.

Spring Cloud 版本和 Boot 版本不匹配,网关启动异常。

4.

Feign 调用服务名写错,大小写敏感。

5.

Nacos:服务注册成功,但是 Feign 调用报错找不到实例:检查 namespace、group 两边必须一致。

ex:Namespace 写错:服务注册在 dev 命名空间,消费方在 public,服务列表看不见。

6.

Nacos-配置中心:

  1. ❗SpringBoot3,必须引入 spring‑cloud‑starter‑bootstrap,否则 bootstrap.yml 不加载,读取不到 nacos 配置。
  2. 忘记加@RefreshScope:修改 nacos 配置,程序不刷新。

7.

Seata Server 有两种存储模式
  1. file:内存存储,重启事务数据丢失,只用于 demo 测试。
  2. db:数据库存储(生产必须),事务信息持久化 mysql。

8.

Seata:

有发起方加 @GlobalTransactional(rollbackFor = Exception.class);远程被调用微服务只需要普通 @Transactional。
Feign 调用,XID 全局事务 ID 会自动透传 Header,Seata 拦截 Feign 请求自动传递 xid,不需要手动处理。
rollbackFor = Exception.class:一定要写;默认 RuntimeException 才回滚,普通 Exception 不会回滚。

9.

启动微服务后,访问一次接口,Sentinel 控制台就能看到该服务。

Sentinel 是懒加载:默认第一次访问资源,才会注册到控制台;配置eager:true启动就注册。

10.

  1. blockHandler 和 fallback 分不清:blockHandler 只管 sentinel 拦截;fallback 管业务异常。
  2. 懒加载:不配置 eager:true,服务启动控制台看不到服务,必须访问一次接口。

11.

网关流控普通流控区别?

网关支持基于http 请求属性(IP、header、query 参数)流控;普通 Sentinel 是方法维度,没有原始 http 请求信息。

12.

为什么网关有 Sentinel,还需要服务内 Sentinel?
场景 1:RPC 调用,不走网关
服务之间 Feign/Dubbo 互相调用,根本不经过 Gateway。

例:A 服务调用 B 服务的接口,这个流量不会经过网关,Sentinel Gateway 感知不到。 如果 B 服务只有网关 Sentinel,没有服务内 Sentinel,一旦 A 疯狂调用 B,直接雪崩。

场景 2:网关是粗粒度,需要接口级防护
网关只能按路由限流:/order/** 全部算一类。 但业务上:下单接口压力大,查询订单压力小。网关没法区分。 服务内 Sentinel 可以单独对 /order/create 做独立限流,查询接口不受影响。

场景 3:多层故障隔离
  • 第一层(网关):挡住外部超大流量,防止洪水直接打进集群;
  • 第二层(服务内):即使网关放行流量,单个接口异常、RPC 超时,服务自己熔断,故障锁在当前服务,不扩散。

网关挂了、绕过网关的请求,服务自身还有一层保护。

场景 4:热点参数限流

热点参数限流是普通 Sentinel 的能力,Sentinel Gateway 不支持。 比如秒杀:限制同一个用户 ID 高频下单,这种需要解析请求参数做热点限流,放在服务内部更合适。

面试一句话回答
Sentinel Gateway 是网关入口防护,只处理经过网关的 HTTP 流量,做路由级的限流熔断;但是微服务之间的 RPC 调用不经过网关,网关监控不到。 所以一般是双层防护:网关挡外部大流量,服务内部 Sentinel 负责接口、RPC 调用、热点参数的细粒度防护,实现多层故障隔离,防止雪崩。

13.

LoadBalancer没有内置权重策略。 如果需要权重负载均衡,使用 Nacos Discovery 自带权重能力;或者自己自定义 LoadBalancer 策略。

Nacos 控制台 → 服务管理 → 实例列表,可以修改实例权重。

权重范围:0‑100;权重 = 0,不会接收流量。

场景:灰度发布。新启动实例权重设置很小 (10),老实例权重 90;少量流量打到新版本,验证没问题再把权重拉满。

开启权重负载均衡:

spring: cloud: nacos: discovery: # 开启nacos权重负载均衡,底层替换LoadBalancer实例选择逻辑 weight: true


开启 weight:true 之后,会覆盖掉 LoadBalancer 自带的 round_robin/random 策略;优先使用 Nacos 权重算法。

14.

LoadBalancer 重试机制

调用服务实例失败,自动换一个实例重试。

⚠️注意:重试一定要注意幂等性!

非幂等接口(新增订单)不要开启重试,会产生重复数据。

15.

重试如何保证接口幂等?

方案 1:全局唯一业务幂等 Token(最常用,适合新增类接口:创建订单、生成单据)

流程:

上游调用方,在业务发起之前,生成全局唯一幂等 Token(UUID / 雪花 ID)。
把 Token 放在请求头(Idempotency‑Token)或者请求体中,每次重试,复用同一个 Token,不要生成新 Token。
✅重点:LoadBalancer 重试,不会重新生成请求,复用原始请求对象,Token 自然不变,这点很关键。

下游服务收到请求:
先拿 Token 去 Redis / 数据库幂等表查询是否已经处理过。
如果不存在:执行业务逻辑,执行成功后,把 Token 存入 Redis(设置过期时间,比如 24h)。
如果 Token 已经存在,直接返回上一次成功的处理结果,不再执行业务逻辑。
Redis 伪代码:

// 使用setnx保证原子性 Boolean ok = redisTemplate.opsForValue().setIfAbsent(token,"processed",24, TimeUnit.HOURS); if(!ok){ // 已经处理过,直接返回历史结果 return 历史成功响应; } // 执行业务逻辑 createOrder() // 业务执行完成,key已经写入,后续重试全部直接返回

注意:

必须保证 检查 + 写入 Token 是原子操作,不能先查询再 set(并发下会穿透),Redis 用SETNX;数据库用唯一索引。
Token 过期时间,大于业务最大重试窗口。
方案 2:数据库唯一约束(唯一索引)

适合数据库落库场景,给业务唯一标识建立数据库唯一索引。 例如:订单外部业务号、单据号。

第一次请求:插入成功。
重试请求再次插入相同唯一值,数据库抛出唯一索引冲突异常。
下游捕获唯一冲突异常,把它转化成正常成功响应返回给上游。
优点:简单,不需要 Redis; 缺点:只针对新增插入;不适合更新操作;高并发下数据库压力大。

方案 3:基于业务状态机(更新、扣减类业务,库存、订单状态流转)

适合状态流转业务,比如:订单状态:待支付→已支付→已发货;库存扣减。

更新语句带上前置状态条件,状态机约束,只允许状态流转一次。

示例:库存扣减

-- 只有库存>=扣减数量才执行扣减;重复重试,条件不满足,update返回0行 update stock set count = count‑#{num} where id=#{id} and count >= #{num};

订单状态更新:

-- 只能从【待支付】修改为【已支付】,重复重试,条件不命中,更新行数0 update `order` set status='PAID' where id=#{orderId} and status='WAIT_PAY';


执行完 SQL 判断 update 返回影响行数:

返回 > 0:执行业务成功;
返回 0:代表状态已经变更过,是重试请求,直接返回成功,不再执行业务。
这个方案非常适配分布式事务、Seata、库存扣减场景。

16.loadbalancer

  • 客户端负载均衡:消费端本地选实例,直连目标服务,不是 Nginx 这种服务端 LB。
  • 内置策略:轮询 round_robin、随机 random。
  • 权重负载均衡靠 Nacos‑Discovery 扩展,LoadBalancer 本身没有权重策略。
  • @LoadBalanced给 RestTemplate 增加拦截器,解析服务名替换真实 ip 端口。

17.

服务器部署
1、开发 / 测试环境(最常见:单台机器混部全部组件)

一台 Linux 服务器,把下面全部装在同一台机器,不同端口区分:

Nacos(端口 8848)
Seata TC(端口 8091)
Sentinel Dashboard(8080)
Prometheus(9090)
Grafana(3000)


✅优点:省机器,部署简单,本地测试、联调非常合适。

❌缺点:资源争抢,一个组件 CPU / 内存打满会影响其他组件;不能用于正式生产。

2、中小规模生产(2 台机器,推荐)

很多中小公司生产就是这么玩的

机器 A:Nacos + Seata TC

机器 B:Prometheus + Grafana + Sentinel Dashboard

理由:

Nacos + Seata TC 属于微服务核心控制面,放一组机器;
监控类组件(Prometheus/Grafana/Sentinel 控制台)属于观测面,放另一组;
Sentinel Dashboard 压力很小,占用资源极低,随便和监控放一起没问题。


注意:生产 Nacos、Seata TC 建议集群部署(多实例),不是单实例。

比如 Nacos 至少 3 节点集群,Seata TC 至少 2 节点集群。

3、大规模生产(4 组独立机器,隔离)

高可用、大流量、企业规范严格场景,分开独立机器 / 独立 K8s 节点组:

Nacos 集群(注册 + 配置中心)
Seata TC 集群
Sentinel Dashboard(单实例即可,压力很小)
监控集群:Prometheus + Grafana


好处:资源隔离,互不影响,故障域隔离。

重点:Sentinel Dashboard 本身压力很小,绝大多数场景不需要单独一台服务器,不需要单独集群。

不需要必须部署在 4 台服务器。

组件是独立进程,但进程可以混部。

测试环境:

全部组件部署在同一台服务器,依靠不同端口隔离。

中小生产:

一般分成两组,一组放 Nacos 和 Seata TC(微服务控制面);

另一组放 Prometheus、Grafana、Sentinel Dashboard(监控观测组件)。

大规模生产,为了故障隔离,会分开部署到多台机器。

注意:Sentinel Dashboard 只是控制台,资源消耗很小,不需要独占一台服务器。

❗️❗️❗️

Sentinel、Seata 的客户端(RM/TM、sentinel client)是嵌在业务服务进程内

18.

2. Nacos 配置不生效,@RefreshScope 失效
现象:Nacos 修改配置,程序没有拿到新值。 根因:

忘记加@RefreshScope;
Bean 被@Conditional提前初始化;
bootstrap 配置没有加载;SpringBoot3 需要引入spring‑cloud‑starter‑bootstrap。
3. 权重灰度不生效
现象:Nacos 控制台修改实例权重,流量没有按权重分配。 根因:没有开启 spring.cloud.nacos.discovery.weight=true;LoadBalancer 默认轮询不会读取权重。

19.

Q1:介绍下你项目用的微服务技术栈,整体架构是怎样的?
答: 我们采用 Spring Cloud 微服务体系。

注册 & 配置中心:Nacos,服务注册发现、配置动态管理,支持 namespace/group 做环境隔离,支持权重实现灰度发布。
网关:Spring Cloud Gateway,基于 WebFlux 响应式,作为统一入口,负责路由转发、鉴权、限流、跨域。
远程调用:OpenFeign 做声明式 HTTP 调用,负载均衡使用 Spring‑Cloud‑LoadBalancer,替代旧的 Ribbon。
容错防护:Sentinel 做流量防护,限流、熔断降级、热点参数限流,规则持久化到 Nacos,防止服务雪崩。
分布式事务:Seata AT 模式,业务无侵入解决微服务之间数据一致性问题。
可观测体系三支柱
Metrics 指标:Micrometer 采集指标,Prometheus Pull 模式拉取 /actuator/prometheus,Grafana 做大盘可视化,Alertmanager 实现告警推送。
Tracing 链路追踪:SpringBoot3 废弃 Sleuth,使用 Micrometer‑Tracing,底层 Brave 实现,Span 上报 Zipkin,全链路 TraceId 透传定位慢调用。
Logging 日志:日志打印 traceId、spanId,Loki 做日志聚合,三者依靠 traceId 关联排查问题。
请求流程:前端请求打到 Gateway 网关 → Gateway 路由转发,LoadBalancer 从 Nacos 选取实例 → Feign 调用业务微服务;服务之间 Seata XID、Tracing traceId 自动通过 Http Header 透传。

20.

看门狗锁 API 上相关参数(影响看门狗是否开启)

参数说明看门狗状态
lock.lock()无参加锁✅ 开启看门狗自动续期
lock.tryLock(long waitTime, TimeUnit unit)只填等待时间,不填 leaseTime✅ 开启看门狗
lock.lock(long leaseTime, TimeUnit unit)指定 leaseTime❌ 看门狗禁用,leaseTime 到期锁释放
lock.tryLock(long waitTime, long leaseTime, TimeUnit unit)指定 leaseTime❌ 看门狗禁用

核心对比表

特性lock()tryLock(waitTime, unit)
阻塞行为无限阻塞拿不到锁就一直等,永不超时有限阻塞最多等待 waitTime,超时直接返回 false
返回值void,成功拿到锁才返回;拿不到一直卡着boolean:true = 拿到;false = 超时没拿到
中断响应等待锁时不响应中断线程 interrupt,不会抛异常;拿到锁之后才会标记中断状态等待锁期间可以响应中断被 interrupt 直接抛出InterruptedException
使用场景必须拿到锁才能继续,不允许失败不希望无限卡死,有兜底降级逻辑(防止死锁、服务雪崩)

平时生产的隔离级别是什么,RC 还是 RR?

线上生产用的是RC(Read Committed,读已提交)。

项目中防重放是什么

Ex:

5 分钟时间戳防重放是:请求中携带发送时间requestTime,服务端收到请求后,比较它和服务器当前时间。

如果相差太久,就认为这可能是一份被截获后重新发送的旧请求,直接拒绝。

// 防重放校验
long diffMinutes = Math.abs(System.currentTimeMillis() - time) / (1000 * 60);
if (diffMinutes > REQUEST_EXPIRE_MINUTES) {
log.info("防重放检查不通过");
return Response.fail(HttpStatus.UNAUTHORIZED, "请求已过期");
}
log.info("完成防重放检查");

什么是“重放攻击”

假设业务方在 12:00 发送了一个合法请求:

{ "businessLineCode": "LINE_A", "receiptTypeCode": "INVOICE", "fileId": "F001", "requestTime": 1789224000000 }

攻击者即使无法解密或修改它,也可能截获整个 RSA 密文,然后在 13:00 原样再发送一次:

第一次:合法业务方发送 第二次:攻击者原样复制并重新发送
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 2:24:11

【零基础学AI】第 1 章课后练习与答案

第 1 章课后练习与答案 先独立完成&#xff0c;再向下核对答案。归类时可以写多个层级&#xff0c;例如“人工智能、机器学习、深度学习”。题目没有提供实现细节时&#xff0c;只写能够确定的类别。&#x1f308; 关于《AI 零基础 36 讲》课后训练 &#x1f4da; 与正式讲解配…

作者头像 李华
网站建设 2026/10/5 2:24:07

Go语言高并发TCP代理反向代理连接池复用实战

title: “Go语言高并发TCP代理反向代理连接池复用实战” date: 2026-06-08 tags: [Go, TCP代理, 反向代理, 连接池, 高并发, net.Conn] categories: Go高并发编程 Go语言高并发TCP代理反向代理连接池复用实战导语TCP 代理是中间件开发中最常见的网络编程场景之一&#xff1a;AP…

作者头像 李华
网站建设 2026/10/5 2:23:03

STM32F103 CAN通信标准库实战:从初始化到双机收发与故障排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华