前言:大家可以把面试或工作中遇到了经典问题共享一下,我们共同学习成长,金九银十,祝各位都能步步高升!
文档大家可以往下追加,请勿删除他人编写的内容哦。我会把问题的相关资料进行补充,以便各位食用。
请点击打开文档-->
Docs
4-gateway nginx sentinel都可以限流,有什么区别
作用域,限流颗粒度,使用场景
对,主要是对颗粒度的把控
Nginx、Spring Cloud Gateway 和 Sentinel 都可以实现限流,但它们的作用域、限流颗粒度以及使用场景各不相同。以下是它们在这些方面的详细对比:
1. 作用域
-
Nginx 限流:
-
作用于 网络层 或 应用的入口层,通常用于控制进入服务器的流量。
-
通过配置在 Nginx 的反向代理层,Nginx 可以对进入的 HTTP 请求进行限流,适用于整个应用集群的访问控制。
-
限流范围通常包括 IP 地址、请求路径、连接数等。
-
-
Gateway 限流:
-
作用于 应用网关层,属于 API 网关层面的限流,负责转发请求给后端服务。
-
Spring Cloud Gateway 限流通常是基于 路由 和 API 的限流。它可以根据服务的不同 URL 路由来进行限流,从而保护后端服务不被突发流量冲垮。
-
Gateway 的限流还可以细化到具体用户、客户端、IP 等维度。
-
-
Sentinel 限流:
-
作用于 应用内部层面,属于业务逻辑的限流,主要保护具体的业务服务(例如微服务)的稳定性。
-
Sentinel 可以针对服务内部的某个具体方法、某个 API、甚至某个数据库查询等进行限流,防止单个微服务由于突发流量崩溃。
-
还支持基于调用关系的限流,例如 A 服务调用 B 服务时进行限流。
-
2. 限流颗粒度
-
Nginx 限流:
-
Nginx 限流是基于 IP、请求数或连接数等较粗粒度的限流,适用于整个应用或 API 层面的限流控制。
-
Nginx 常用的限流方式:
-
连接限流:限制每个客户端的并发连接数。
-
请求限流:限制每个客户端在一定时间窗口内发送的请求数。
-
-
-
示例:
-
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location / {
limit_req zone=one burst=5 nodelay;
proxy_pass http://backend;
}
}
}
-
该配置每秒钟只允许一个请求,且最多允许5个突发请求。
-
Gateway 限流:
-
Gateway 支持通过 Redis 实现精细化限流,通常基于 路径(Route) 或 请求头(Header) 进行限流。限流规则可以设置为针对不同用户、IP 或 API 进行限流。
-
Gateway 限流的细粒度比 Nginx 细,可以基于用户、应用、IP、路径、请求头等任意维度。
-
-
示例:
@Beanpublic KeyResolver userKeyResolver() { return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()); } @Beanpublic RateLimiter myRateLimiter() {return new RedisRateLimiter(10, 20); // 每秒 10 个请求,最大 20 个突发请求 }
-
这个限流器会对每个 IP 地址应用限流策略。
-
Sentinel 限流:
-
Sentinel 限流的颗粒度更为细腻,支持从 应用内部到 微服务接口 再到 方法级别 的精细化限流。还可以对资源、API、流量入口等进行限流。
-
Sentinel 支持多种限流策略,例如:
-
QPS 限流:限制某个接口的每秒请求数。
-
调用关系限流:限制调用链中具体服务的流量。
-
线程数限流:根据当前并发线程数进行限流。
-
热点参数限流:根据请求中的热点参数(如用户 ID、商品 ID)进行限流。
-
-
-
示例:
// 定义限流规则FlowRule rule = new FlowRule(); rule.setResource("myResource"); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setCount(100); // QPS 限制为 100// 加载规则 FlowRuleManager.loadRules(Collections.singletonList(rule)); // 在代码中保护资源try (Entry entry = SphU.entry("myResource")) {// 被保护的业务逻辑 } catch (BlockException e) {// 处理被限流的逻辑 }
-
这里 Sentinel 保护了
"myResource"
资源,限制其每秒只能处理 100 个请求。
3. 使用场景
-
Nginx 使用场景:
-
适合在应用的入口处进行流量限制,适用于 大规模流量控制,例如防止恶意 IP 的流量攻击。
-
适合对外部 HTTP 请求的速率限制,如 防止 DDoS 攻击、限制特定 IP 访问频率等。
-
在 API 网关前用作 第一道防线,处理基础的流量控制。
-
-
Gateway 使用场景:
-
适合在微服务架构中,作为 API 网关 来对各个服务的流量进行控制。
-
可以基于路由限流,适合保护 特定服务或接口,如在网关处限制每个用户的 API 调用次数或防止恶意调用。
-
可以与 Redis 结合,进行 分布式限流,适合跨服务、跨应用的限流场景。
-
-
Sentinel 使用场景:
-
适合在微服务架构中,对 具体的服务接口 或 内部方法 进行限流,防止单个微服务或资源过载。
-
Sentinel 适合应用内部的 精细化流量控制,例如某个服务的某个接口、热点参数的流量控制,防止服务由于突发请求崩溃。
-
Sentinel 支持 调用链路 和 热点参数 限流,适用于复杂的调用关系或热点资源的保护。
-
总结
工具 | 作用域 | 限流颗粒度 | 使用场景 |
Nginx | 网络层、应用入口 | 基于 IP、请求数、连接数等较粗粒度的限流 | 防止 DDoS 攻击,限制 IP 流量 |
Gateway | API 网关层 | 基于 API 路由、用户、IP 等精细化限流 | 微服务 API 限流,保护特定服务 |
Sentinel | 应用内部,业务逻辑和微服务保护 | 细粒度限流,支持方法级、参数级限流 | 精确控制单个接口、方法、热点参数流量 |
Nginx 适合入口流量控制,Gateway 适合 API 级别的精细化限流,Sentinel 适合内部业务和服务接口的限流保护。
3-redis脑裂如何解决
Redis 脑裂问题(Split-brain)指的是在 Redis 主从架构(主节点和副本节点)中,由于网络分区或故障,导致多个节点误以为自己是主节点并同时接受写操作的情况。脑裂会引起数据不一致的问题,并可能导致数据丢失或冲突。
解决 Redis 脑裂的常见方法
-
使用 Redis Sentinel 的 quorum(仲裁机制)
-
Redis Sentinel 是 Redis 官方提供的高可用解决方案,主要功能包括自动故障转移和主节点的监控。通过 Sentinel 配置,可以在主节点失效后自动提升从节点为主节点,同时防止脑裂。
-
Quorum(仲裁)机制:通过配置
quorum
参数来确保在网络分区时不会因为少数哨兵的错误判断而进行错误的主从切换。 -
配置示例:
-
在
sentinel.conf
中设置: -
sentinel monitor mymaster 127.0.0.1 6379 2
-
其中,
2
表示至少需要 2 个 Sentinel 同意才能进行主从切换操作。通过设置合适的 quorum 值,确保故障转移过程的准确性。
-
-
开启
min-slaves-to-write
和min-slaves-max-lag
参数 -
Redis 提供了防止脑裂的两个重要配置项,
min-slaves-to-write
和min-slaves-max-lag
。这两个配置项通过确保主节点有足够数量的从节点在线并且同步数据延迟在可控范围内,来防止主节点在网络分区时继续执行写操作。-
min-slaves-to-write:确保至少有一定数量的从节点同步后,主节点才能接受写操作。
-
min-slaves-max-lag:控制从节点允许的最大同步延迟时间。
-
-
配置示例:
min-replicas-to-write 1 至少需要 1 个副本节点
min-replicas-max-lag 10 最大允许的延迟是 10 秒
-
如果 Redis 主节点发现没有足够的从节点处于同步状态或从节点的同步延迟超过了 10 秒,它将拒绝写请求,从而避免脑裂情况引发的数据不一致。
-
网络隔离问题的修复
-
Redis 脑裂问题的根源通常是网络分区引起的,因此定期检查和修复网络问题是防止脑裂的基本措施。可以采取以下措施:
-
确保高可用的网络架构,减少网络分区的可能性。
-
使用多个网络路径或备份网络链路,以便在主网络故障时有备用网络。
-
对 Redis 部署进行合理的网络隔离配置,确保网络问题不容易引发脑裂。
-
-
使用 Redis Cluster
-
Redis Cluster 本身具备容错和分片特性,可以在 Redis 集群节点发生网络分区时继续正常工作。
-
在 Redis Cluster 中,当一个主节点和它的副本失去联系时,其他节点可以选举出新的主节点,确保系统正常运行。
-
Redis Cluster 的脑裂防护依赖于更多的节点来参与选举,因此需要设置合理的 quorum 值,确保少数派无法做出主从切换的错误决定。
-
-
结合 Keepalived + VIP 解决脑裂
-
通过
Keepalived
工具,可以为 Redis 实例配置虚拟 IP(VIP),并通过健康检查来控制 VIP 的主节点归属。-
当 Redis 主节点失效时,Keepalived 自动将 VIP 切换到新的主节点,确保只有一个主节点在处理请求,避免脑裂问题。
-
Keepalived 通过 VRRP 协议来管理 VIP 的归属,并与 Redis 的高可用机制结合,有效防止脑裂。
-
预防脑裂的最佳实践
-
合理配置 Sentinel
-
确保足够数量的哨兵实例(推荐 3 个及以上),以避免误判和错误的主从切换。
-
设置适当的
quorum
值,以确保 Sentinel 系统的稳定性。
-
-
使用主从复制中的
min-replicas-to-write
和min-replicas-max-lag
-
配置 Redis 主节点在网络环境不稳定时拒绝写请求,避免脑裂导致的数据不一致。
-
-
优化网络基础设施
-
减少网络分区的可能性,确保 Redis 集群和 Sentinel 之间的网络畅通。
-
配置监控系统及时发现网络分区问题。
-
-
结合其它高可用工具
-
使用像
Keepalived
这样的工具来控制 VIP 和 Redis 实例的高可用,进一步提升系统的稳定性。
-
通过以上措施,可以有效预防和解决 Redis 脑裂问题,确保数据一致性和系统的高可用性。
2-redis和@transational共用遇到过那些问题?
连redis事务冲突都这么了解
在 Spring Boot 中,如果同时使用 Redis 和 @Transactional
注解进行事务管理,可能会遇到一些常见问题,特别是在缓存和数据库事务之间保持一致性时。这种场景下,数据可能被不一致地写入 Redis 和数据库,从而引发问题。以下是一些常见问题和解决方案:
1. 缓存和数据库不一致
当 Redis 缓存和数据库同时参与操作时,常见问题是数据的一致性问题。例如,当数据库事务回滚时,Redis 缓存已经更新,但由于数据库没有成功提交,导致数据不一致。
具体问题:
-
如果数据库的更新操作被回滚,Redis 缓存可能已经提前写入新的值,导致缓存中的数据是错误的,而数据库中的数据仍然是旧的。
-
当数据库更新成功时,Redis 缓存可能还没更新或过期,从而导致读取到错误数据。
解决方案:
-
缓存延迟更新:只在数据库事务成功提交后才更新 Redis 缓存。你可以使用 Spring 事务事件监听器,在事务成功提交之后才进行缓存的更新。
-
@Transactionalpublic void updateData() {// 数据库操作// 更新数据库中的数据// 事务提交成功后更新 Redis// 如果事务回滚,这里不会执行
}
-
使用 Spring 的
TransactionSynchronizationManager
来保证事务完成后更新缓存: -
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {@Overridepublic void afterCommit() {// 事务成功后执行更新 Redis 缓存操作
}
});
-
读写模式一致:确保 Redis 和数据库的更新顺序一致,例如:
-
更新数据库 -> 更新缓存。
-
删除缓存 -> 更新数据库 -> 添加新的缓存。
-
-
Redis 操作不受事务管理控制 Redis 默认是不支持事务回滚的,而
@Transactional
只对数据库操作生效。也就是说,Redis 操作不受数据库事务的回滚或提交控制。如果数据库事务失败或回滚,Redis 中的数据不会自动回滚。
-
具体问题:
-
当事务失败时,数据库的操作可能会回滚,但 Redis 的写入不会回滚,导致 Redis 中缓存的数据和数据库状态不同步。
-
-
解决方案:
-
手动控制 Redis 缓存的更新:在数据库操作成功之后,再进行缓存的更新或清理。这种方式可以避免在事务回滚时缓存的数据不一致。
-
延迟操作 Redis:通过
TransactionSynchronizationManager
来控制 Redis 操作的时机,在事务成功提交后进行缓存的操作。 -
@Transactionalpublic void saveDataWithCacheUpdate(Object data) {
// 数据库操作
myRepository.save(data);
// 事务成功后执行 Redis 操作
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Overridepublic void afterCommit() {
// 在事务提交后更新 Redis 缓存
redisTemplate.opsForValue().set("key", data);
}
});
}
-
-
事务传播行为的影响 如果 Redis 的操作嵌套在数据库事务中,并且 Redis 的操作依赖于数据库的状态,事务传播行为可能导致问题。例如,当事务传播设置为
REQUIRES_NEW
时,Redis 和数据库的操作将使用不同的事务,从而导致不一致。
-
具体问题:
-
如果 Redis 的操作在一个新事务中进行,并且数据库操作回滚,则 Redis 中的数据仍然可能被提交,导致数据库和 Redis 不一致。
-
-
解决方案:
-
避免 Redis 操作在新的事务中进行:确保 Redis 操作和数据库操作使用相同的事务传播行为,以保证一致性。
-
手动控制 Redis 操作的时机:例如,在事务提交成功后再进行缓存的更新或删除。
-
-
超时问题 Redis 和数据库操作的执行时间不同,如果 Redis 操作或数据库操作的耗时超过了事务的超时时间,也可能导致事务异常回滚。
-
具体问题:
-
事务超时可能导致数据库操作回滚,但此时 Redis 的操作已经生效,导致缓存和数据库不一致。
-
Redis 操作比较快,但数据库操作可能会比较耗时,如果事务超时,整体操作将失败。
-
-
解决方案:
-
合理配置事务超时时间:根据具体的业务场景,配置合理的事务超时时间,确保在事务超时时间内完成所有操作。
-
@Transactional(timeout = 30)public void updateData() {
// 数据库操作// Redis 操作
}
-
-
并发问题 在高并发场景下,缓存和数据库的更新操作可能会由于不同线程的竞争,导致数据不一致问题。例如,两个线程同时执行,可能会出现先更新 Redis 然后数据库更新未提交成功的情况。
-
具体问题:
-
多个线程同时操作相同的缓存和数据库,可能会导致 Redis 中的数据和数据库中的数据不一致。
-
-
解决方案:
-
使用分布式锁:在进行 Redis 和数据库的更新时,使用 Redis 的分布式锁机制来确保多个线程不会同时操作相同的数据。
-
乐观锁:可以使用数据库的乐观锁机制,确保数据库中的数据不会被多个事务同时修改,保证最终一致性。
-
总结
当 Redis 和 @Transactional
同时使用时,常见的问题包括缓存与数据库不一致、事务回滚时 Redis 不回滚、事务传播带来的问题以及超时和并发问题。为了解决这些问题,常用的方案包括延迟更新 Redis 缓存、手动控制 Redis 操作的时机、合理配置事务传播行为、以及使用分布式锁和乐观锁来确保数据的一致性。
1-redisson加锁和释放锁,业务没处理完,锁到期了,怎么解决
在分布式系统中,使用 Redisson 进行分布式锁管理时,如果业务逻辑没有执行完,而锁已经到期自动释放,这可能会导致其他线程或进程获得锁,导致并发冲突或数据不一致问题。为了避免这种情况,可以采取以下解决方案:
1. 设置合理的锁过期时间
Redisson 默认会为锁设置一个 TTL(Time To Live,即过期时间),如果在业务逻辑执行完之前锁已经过期并释放,就会导致锁竞争。因此,首先要确保锁的过期时间足够长,能够覆盖业务逻辑的执行时间。
你可以通过 tryLock()
方法来指定锁的最大持有时间:
java
复制代码
RLock lock = redissonClient.getLock("myLock");
boolean isLocked = lock.tryLock(10, TimeUnit.SECONDS); // 10秒过期时间
但是这种方法存在一个问题:如果业务处理的时间是不可预估的,直接指定一个固定的超时时间可能会导致锁到期过早或过晚。
2. 使用 Redisson 的自动续期机制
Redisson 提供了 自动续期 功能,在 Redisson 的 RLock
中,当一个线程成功获取到锁后,默认情况下,Redisson 会启动一个守护线程,这个守护线程会每隔一段时间自动为持有的锁续期,确保在业务执行过程中锁不会意外到期。
工作机制:
-
当你使用
lock()
方法获取锁时,Redisson 会默认设置一个 30 秒的锁过期时间。 -
如果业务逻辑执行时间超过了这个过期时间,Redisson 会自动延长锁的 TTL 时间,直到锁被手动释放或业务处理完毕。
示例代码:
RLock lock = redissonClient.getLock("myLock");
try {// 通过lock()加锁,Redisson会自动续期
lock.lock(); // 业务处理逻辑
} finally {// 业务逻辑处理完毕后手动释放锁
lock.unlock();
}
在这个例子中,锁初始的过期时间是 30 秒,但是 Redisson 的守护线程会自动续期,确保锁在业务逻辑执行期间不会被释放。只有在 unlock()
之后,锁才会真正释放。
3. 手动控制锁续期
如果你想更灵活地控制锁的过期时间,可以手动续期锁。Redisson 提供了 lock.lock(leaseTime, timeUnit)
方法来设置锁的有效期,并且在需要时手动续期。
例如,如果你的业务逻辑需要较长时间处理,可以在业务执行的过程中手动延长锁的生命周期:
RLock lock = redissonClient.getLock("myLock");
try {// 加锁并设置过期时间,比如 60 秒
lock.lock(60, TimeUnit.SECONDS);
// 如果业务处理还没有结束并且锁即将到期,手动续期if (/* 业务处理还没有结束 */) {
lock.lock(60, TimeUnit.SECONDS); // 再续期 60 秒
}
// 业务处理逻辑
} finally {// 业务逻辑处理完毕后手动释放锁
lock.unlock();
}
不过,使用这种方式可能会增加代码的复杂度,因此推荐使用 Redisson 提供的自动续期机制,自动管理锁的过期时间。
4. 锁过期后如何处理异常情况
如果业务逻辑复杂且执行时间不确定,除了合理设置锁的过期时间和利用自动续期机制之外,还可以考虑在锁到期后进行异常处理。
你可以通过以下方式捕获锁释放后的异常情况:
java
复制代码
RLock lock = redissonClient.getLock("myLock");
try {if (lock.tryLock(10, 60, TimeUnit.SECONDS)) { // 尝试获取锁,等待最多10秒,持有时间60秒// 业务逻辑
} else {// 如果获取锁失败,执行相应的处理逻辑throw new IllegalStateException("无法获取锁,任务正在被其他线程执行");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 处理异常
} finally {if (lock.isHeldByCurrentThread()) {
lock.unlock(); // 确保只有持有锁的线程释放锁
}
}
在这里,你可以使用 tryLock()
方法来更好地控制锁的获取和处理逻辑。并且,通过检查锁的持有状态,确保不会出现线程未持有锁却意外释放的情况。
5. 使用 Watchdog(看门狗)机制
Redisson 内置了 "Watchdog" 机制(守护线程),这个机制会每隔 10 秒检查一次锁的持有状态,如果锁还在被持有,它会自动为锁续期,防止锁过期导致问题。
只要你使用的是 Redisson 的 lock()
方法而不是 lock(leaseTime, timeUnit)
,Redisson 就会自动使用 Watchdog 机制来续期。
总结
当 Redisson 加锁时,业务逻辑还未处理完锁就到期了,可能导致并发冲突。解决方案包括:
-
设置合理的锁过期时间,确保业务逻辑能在过期时间内完成。
-
使用 Redisson 的自动续期机制,这是一种最简单和安全的方式,锁会自动续期,直到你手动释放锁。
-
如果自动续期不适用,可以考虑手动续期锁,在需要时延长锁的生命周期。
-
处理锁过期异常,确保系统在锁到期后有适当的措施来处理业务逻辑。
推荐使用 Redisson 提供的自动续期机制,简化锁的管理并确保锁不会意外过期。
算法-----
动态规划还是有点难,
双指针,优先队列,二叉树,回溯这些我都可以默写了,
双指针 经典题目 判断有没有环
我看有的大佬每次动态规划都能给出递推,递归,记忆搜索和选择or 不选回溯
dp 数组去记录空间容易炸,
邻接矩阵 占空间,有向 无向,有权,无权
有向 有负权重的只能floyd
bfs 和 dikstra都能解决80%--
大佬的做题记录,太牛了
End --欢迎进群共同成长-------
重要文档
https://springdoc.cn/spring/core.html#beans-factory-type-determination
实用工具--gpt
https://xinghuo.xfyun.cn/desk
https://www.doubao.com/chat/
https://chatgpt.com/c/66dfbb12-9e1c-8001-9cc9-608cb2983ae5