常见技术难点及方案

1. 分布式锁

1.1 难点

1.1.1 锁延期

同一时间内不允许多个客户端同时获得锁;

1.1.2 防止死锁

需要确保在任何故障场景下,都不会出现死锁;

1.2.3 可重入

特殊的锁机制,它允许同一个线程多次获取同一个锁而不会被阻塞。

1.2.4 高可用

同时,加锁、释放锁的过程性能开销应尽量低,并保证高可用,避免单点故障。

1.2 技术方案

1.2.1 基于Redis

  • 技术要点

|---------|---------------------------------------------------------------------------------------------------------------|
| 原理 | Redis的SETNX指令:SETNX(set if not exist)指令用于插入一个键值对。如果键已经存在,则返回False,否则插入成功并返回True。 |
| 锁延期 | Redis 实现分布式锁时,为了确保锁的安全性,引入了 watchdog(看门狗)机制。watchdog 的主要作用是定期重置锁的过期时间,从而避免锁意外过期被其他客户端获取。(在客户端启动) |
| 释放锁 | 同时,可以利用EXPIRE指令为键值对设置过期时间,防止锁忘记释放。 |
| 可重入 | Redis分布式锁实现可重入意味着同一个线程或进程可以多次获取同一个锁而不会被阻塞。这通常是通过在客户端维护一个锁计数器来实现的,每次获取锁时计数器加1,每次释放锁时计数器减1,只有当计数器为0时才真正释放锁。 |
| 高可用 | Redis的RedLock算法:尝试从多个相互独立的Redis实例获取锁,只有当从大多数实例上获取了锁,并且获取锁的时间小于锁的过期时间时,才认为锁获取成功。 |

  • 技术实践

1.2.2 基于Zookeeper

  • 技术要点

|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 原理 | Zookeeper分布式锁:利用Zookeeper的顺序临时节点实现分布式锁和等待队列。 客户端尝试在ZooKeeper的一个特定路径下创建一个临时顺序节点。这个节点是临时的,意味着当客户端会话结束时,ZooKeeper会自动删除它。顺序节点则是ZooKeeper会为其分配一个唯一的、单调递增的序列号。 **客户端检查它创建的节点是否是在特定路径下序号最小的节点。如果是,那么它获取到了锁。**如果不是,它就需要等待,通常通过监视序号在它之前的节点的删除事件。 |
| 锁延期 | 为了保持锁,客户端必须保持与ZooKeeper的会话活动。ZooKeeper的会话有一个超时时间,如果在这个时间内客户端没有与ZooKeeper进行任何交互(如心跳),会话将过期,导致所有与该会话关联的临时节点被删除。因此,为了延期锁,客户端需要定期与ZooKeeper交互以重置会话的超时计时器。 |
| 释放锁 | 当客户端完成其任务并准备释放锁时,它只需删除它之前创建的临时节点。由于该节点是临时的,它的删除不会触发其他客户端的监视事件,除非有其他客户端正在等待成为下一个锁持有者。 如果客户端在持有锁期间崩溃或遇到其他问题,ZooKeeper将自动删除其临时节点(因为会话已过期),从而释放锁。其他等待的客户端将能够获取锁。 |
| 可重入 | 本地锁计数器 : 客户端维护一个本地锁计数器。每次成功获取锁时,计数器递增;每次释放锁时,计数器递减。 重入逻辑 : 如果客户端已经持有锁(即本地锁计数器大于0),并且再次尝试获取锁,则只需递增本地锁计数器,而无需与ZooKeeper进行任何额外的交互。 |
| 高可用 | Zookeeper设计的初衷就是为了实现分布式锁服务。 |

  • 技术实践

Apache Curator是一个流行的ZooKeeper客户端库,它提供了高级的分布式锁实现,包括可重入锁。以下是一个使用Curator实现可重入锁的示例:

Curator is a keeper or custodian of a museum or other collection - A ZooKeeper Keeper.

Apache Curator is a Java/JVM client library for Apache ZooKeeper, a distributed coordination service. It includes a high level API framework and utilities to make using Apache ZooKeeper much easier and more reliable. It also includes recipes for common use cases and extensions such as service discovery and a Java 8 asynchronous DSL.

Welcome to Apache Curator | Apache Curator

java 复制代码
import org.apache.curator.framework.CuratorFramework;  
import org.apache.curator.framework.recipes.locks.InterProcessReentrantLock;  
  
public class ZooKeeperReentrantLockExample {  
    private final CuratorFramework client;  
    private final InterProcessReentrantLock lock;  
  
    public ZooKeeperReentrantLockExample(CuratorFramework client, String lockPath) {  
        this.client = client;  
        this.lock = new InterProcessReentrantLock(client, lockPath);  
    }  
    public void acquireLock() throws Exception {  
        lock.acquire(); // 如果当前线程已经持有锁,则重入  
    }  
    public boolean tryAcquireLock(long time, TimeUnit unit) throws Exception {  
        return lock.tryLock(time, unit); // 尝试获取锁,支持重入  
    }  
    public void releaseLock() throws Exception {  
        lock.release(); // 释放锁,如果本地锁计数器减至0,则删除ZooKeeper中的节点  
    }  
}

2. 限流

2.1 难点

2.1.1 突发流量

在实际应用中,流量往往是不稳定的,存在高峰期和低谷期。如何根据流量的变化动态地调整限流策略,是限流算法需要解决的问题。此外,还需要考虑突发流量的情况,如突然出现的大量请求可能导致系统过载。

2.1.2 平滑

计数器限流算法是一种直观的限流方法,它通过累加在特定时间窗口内的请求数量,并在达到设定的阈值时执行限流操作。然而,这种方法存在一个显著的问题,那就是在时间窗口的末期,如果计数器接近或达到限流阈值,那么新到来的请求很可能会触发限流,即使这些请求在实际上并不会对系统造成过载

观察请求量曲线图,计数器这种限流器会容易呈现出锯齿状而不是平滑的曲线。

2.2 方法

2.2.1 计数器

|--------|---------------------------------------------------|
| 原理 | 计数器算法通过记录时间窗口内的请求数量来进行限流。当请求数量超过设定的阈值时,新的请求将被拒绝。 |
| 优点 | 简单直观,易于实现。 |
| 缺点 | 无法应对突发流量,因为一旦达到阈值,后续请求无论多少都会被拒绝。此外,随着流量增长,精度可能下降。 |

2.2.2 基于滑动窗口的计数器

|--------|-------------------------------------------------------------------------------|
| 原理 | 将时间窗口划分为多个小的时间段(格子),每个格子内有一个计数器。随着时间推移,窗口会滑动,抛弃最老的时间段并纳入新的时间段。限流基于整个窗口内的请求总数。 |
| 优点 | 相比于固定窗口计数器算法,滑动窗口能更好地应对突发流量,因为每个时间段都有独立的计数器。 |
| 缺点 | 实现相对复杂,需要维护多个计数器和时间窗口的状态。 |

2.2.3 令牌桶

|--------|-------------------------------------------------------------------|
| 原理 | 令牌桶中存放着一定数量的令牌,每个令牌代表一个请求权限。请求到达时,如果桶中有令牌则取走令牌并允许请求通过;否则请求被拒绝或等待。 |
| 优点 | 能够应对突发流量,因为桶中的令牌可以累积并在需要时快速发放。适用于需要灵活调整限流速率的场景。 |
| 缺点 | 如果令牌发放过快,可能导致系统过载;如果发放过慢,则可能浪费系统资源。 |

2.2.4 漏斗算法

|--------|-----------------------------------------------------|
| 原理 | 想象一个固定容量的桶,请求作为水流进入桶中。如果桶已满,新的请求(水流)会被丢弃或等待直到桶中有空间。 |
| 优点 | 能够平滑突发流量,确保系统的稳定性。对于下游系统来说,流量是恒定的,有助于处理请求。 |
| 缺点 | 可能导致延迟,因为即使系统有能力处理更多请求,漏桶也会限制流量的速率。 |

比较两者的特点,漏桶算法更侧重于强制限制数据的传输速率,确保系统接收的请求速率稳定。而令牌桶算法则在限制平均传输速率的同时,允许一定程度的突发传输,以适应流量变化的实际情况。

在适用场景上,漏斗算法更多地用于保护系统,防止因流量过大而崩溃。而令牌桶算法则更适用于那些需要应对突发流量的场景,比如在秒杀活动中,用户的请求速率不固定,令牌桶算法可以确保系统既能处理稳定的请求流,又能应对突发的请求高峰

2.3 方案

2.3.1 单机限流

2.3.1.1 Guava限流
  • Guava RateLimiter----------限制时间窗口内的凭据速率
    • 平滑突发限流(SmoothBurst)
    • 平滑预热限流(SmoothWarningUp)
    • 主要方法:RateLimiter.create()和limiter.acquire()

2.3.2 分布式限流

2.3.2.1 Ngnix+Lua
2.3.2.2 Redis+Lua

​思路上就很粗暴!比如当前限流为10000QPS/s,直接将当前秒作为key,每一个请求到达的时候都将这个key自增,当一个请求将其自增到10000后,就拒绝访问!具体的实现见张开涛的《亿级流量网络架构核心技术》p75----"分布式限流"。

3. 负载均衡

4. 分布式事务

4.1 难点

4.2 技术方案

4.2.1 两阶段提交(2PC)

|--------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 原理 | 两阶段提交(Two-phase Commit,2PC),通过引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务。 |
| 过程 | 准备: 首先协调者询问参与者事务是否执行成功,参与者发回事务执行结果。 提交或者回滚: 然后如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。(标黄的部分可能会失败) * 第一阶段(prepare):即所有的参与者RM准备执行事务并锁住需要的资源。参与者ready时,向TM报告已准备就绪。 * 第二阶段 (commit/rollback):当事务管理者(TM)确认所有参与者(RM)都ready后,向所有参与者发送commit命令。 |
| 实现 | |
| 优点 | |
| 缺点 | |

4.2.2 补偿事务(TCC)

|--------|---------------------------------------------------------------------------------------------------|
| 原理 | |
| 过程 | |
| 实现 | |
| 优点 | |
| 缺点 | |

5. 幂等

5.1 CAS

5.2 数据库Unique Key

6. 排行榜

6.1 基于Redis sortedset

对于排行榜,Redis的有序集合(Sorted Set)是一个非常适合的数据结构。有序集合中的每个元素都是唯一的,并且关联着一个分数。Redis正是根据分数来为集合中的元素进行从小到大的排序。

6.1.1 存储排行榜数据

使用ZADD命令将用户得分添加到有序集合中。例如:

bash 复制代码
ZADD leaderboard 100 user1  
ZADD leaderboard 150 user2  
ZADD leaderboard 90 user3

6.1.2 获取排行榜数据

  • 获取排行榜的全部数据 :使用ZRANGEZREVRANGE命令来获取排行榜的全部数据。例如,获取得分从高到低的排行榜:
bash 复制代码
ZREVRANGE leaderboard 0 -1 WITHSCORES
  • 获取指定位置的排名 :使用ZREVRANK命令来获取指定用户在排行榜中的位置(从高到低排序):
bash 复制代码
ZREVRANK leaderboard user2
  • 获取指定用户的得分 :使用ZSCORE命令来获取指定用户的得分:
bash 复制代码
ZSCORE leaderboard user2

6.1.3 更新用户得分

bash 复制代码
ZINCRBY leaderboard 10 user2  # 给user2增加10分

6.1.4 移除用户得分

如果需要从排行榜中移除某个用户的得分,可以使用ZREM命令:

bash 复制代码
ZREM leaderboard user2

6.1.5 定时任务与持久化

  • 定时任务:你可以使用Redis的键空间通知(Keyspace Notifications)配合外部程序来监听排行榜的变化,实现定时任务,比如每天重置排行榜等。
  • 持久化:确保Redis配置了合适的持久化策略(如RDB或AOF),以防止数据丢失。

7. 分库分表

8. 加签/验签 or 加密/解密

8.1 加签与验签

8.1.1 加签(签名):私钥加签

  1. 目的:确保数据的完整性和真实性,防止数据在传输过程中被篡改,并确认数据的确由某个特定的发送者发送。
  2. 过程:发送者使用自己的私钥对数据(或数据的哈希值)进行加密,生成一个数字签名。这个签名会附加到原始数据上一起发送。

8.1.2 验签(验证签名):公钥验签

  1. 目的:接收者验证数据的完整性和真实性,确认数据在传输过程中未被篡改,并确认数据确实来自预期的发送者。
  2. 过程:接收者使用发送者的公钥对数字签名进行解密。如果解密成功且得到的数据(或数据的哈希值)与原始数据匹配,那么签名验证通过,说明数据是完整且真实的。

8.2 加密与解密

8.2.1 加密:公钥加密

  1. 目的:保护数据的隐私性,确保只有持有相应私钥的接收者才能读懂数据内容。
  2. 过程:发送者使用接收者的公钥对原始数据进行加密,生成密文。这个密文会发送给接收者。

8.2.2 解密:私钥解密

  1. 目的:接收者使用自己的私钥将密文还原为原始数据,从而读取内容。
  2. 过程:接收者使用自己的私钥对收到的密文进行解密,得到原始数据。

9. 大量数据导入导出

9.1 难点

9.1.1 文件过大

分页查询与导出

  • 对于大数据量的导出,采用分页查询的方式,每次查询并写入一定数量的数据到文件,避免一次性查询全部数据导致内存溢出。
  • 在Spring Boot中,可以使用MyBatis或JPA进行分页查询。

流式处理

  • 使用流式API来处理数据,这样可以减少内存占用,因为数据不是一次性加载到内存中,而是逐步处理。

压缩文件

  • 在导出前对数据进行压缩,如使用ZIP或GZIP格式,以减少文件大小。
  • 前端在接收后解压文件,确保数据的完整性和可读性。

9.1.2 断点重传

分块传输

  • 将大文件分成若干小块,逐块传输。
  • 如果某个块传输失败,只需重新传输该块,而不是整个文件。

记录传输位置

  • 在传输过程中,每个块传输完成后,记录已传输的位置。
  • 如果连接中断,可以从上次记录的位置继续传输。

使用断点续传库

  • 利用现有的断点续传库或框架,如OSS提供的SDK,它们通常已经实现了分块、校验、重传等机制。

9.1.3 进度问题

后端进度跟踪

  • 后端在导出或导入过程中,定期记录并更新进度信息。
  • 可以将进度信息存储在数据库或缓存中,供前端查询。

实时通信

  • 使用WebSocket、SSE或长轮询等技术,建立后端与前端之间的实时通信通道。
  • 后端将进度信息推送给前端,前端实时更新UI展示进度。

API查询进度

  • 前端定期通过API查询特定任务的进度信息。
  • 后端返回最新的进度数据,前端根据数据更新UI。

9.2 技术方案

相关推荐
安科瑞刘鸿鹏4 分钟前
分布式光伏发电系统如何确保电能质量达到并网要求?
服务器·网络·分布式·嵌入式硬件·能源
SteveCode.6 分钟前
高性能微服务架构:Spring Boot 集成 gRPC 实现用户与订单服务即时交互
spring boot·微服务·架构
m0_7286470823 分钟前
Feed流系统重构:架构篇
重构·架构
懒鸟一枚30 分钟前
XXl-SSO分布式单点登录框架
分布式
月夜星辉雪2 小时前
【RabbitMQ 项目】服务端:数据管理模块之消息管理
分布式·rabbitmq
java_heartLake2 小时前
微服务中间件之Nacos
后端·中间件·nacos·架构
_.Switch2 小时前
构建现代应用的Python Serverless架构详解
运维·开发语言·python·云原生·架构·serverless·restful
guitarCC5 小时前
spark Rdd的创建方式
大数据·分布式·spark
Yz98765 小时前
Hadoop里面MapReduce的序列化与Java序列化比较
java·大数据·jvm·hadoop·分布式·mapreduce·big data
不能再留遗憾了7 小时前
RabbitMQ 高级特性——发送方确认
分布式·rabbitmq·ruby