Redis中如何实现分布式信号量?

Redis 可以通过以下方式实现分布式信号量:

  1. 使用 INCR 命令实现简单信号量:
  • 初始化信号量值为最大并发量。
  • 每次请求执行时,使用 INCR 命令将信号量值增加1。如果增加后值大于最大并发量,则拒绝请求。
  • 请求完成时,使用 DECR 命令将信号量值减少1,释放占用的信号量。

例如:

Jedis jedis = new Jedis("localhost");
String semKey = "sem"; 
int maxConcurrency = 10;  // 最大并发量

// 初始化信号量
jedis.set(semKey, String.valueOf(maxConcurrency));

// 请求时获取信号量
String value = jedis.incr(semKey);
if (Integer.parseInt(value) > maxConcurrency) {
  jedis.decr(semKey);  // 释放信号量
  throw new Exception("Too many requests"); 
}

// 请求完成释放信号量
jedis.decr(semKey);  
  1. 使用有序集合实现高性能分布式信号量:
  • 每个并发请求对应一个元素,分数为请求时间戳,通过 ZADD 命令添加到有序集合中。
  • 获取信号量时,使用 ZCARD 检查有序集合元素数量,如果超过最大并发量则拒绝请求。
  • 请求完成时,使用 ZREM 删除对应元素,释放信号量。
  • 这种方法使用有序集合天然排序的特性,避免了使用 INCR/DECR 命令时的锁竞争问题,实现了更高的并发性。

例如:

// 添加请求获取信号量  
String requestId = jedis.zadd(semKey, System.currentTimeMillis(), "request");
long count = jedis.zcard(semKey);
if (count > maxConcurrency) {
  jedis.zrem(semKey, requestId); // 释放信号量
  throw new Exception("Too many requests");
}  

// 请求完成,释放信号量
jedis.zrem(semKey, requestId);