探秘分布式解决方案: 分布式限流——Redis版分布式信号量原理 (附RedisTemplate具体实现代码)...

关于限流这种机制呢也算是老生常谈了, 毕竟在业务开发中实在是很多地方都会用到。比如第三方接口调用限制、并发访问数控制等…

而具体的限流算法在单机中很容易就可以实现, 在java的世界里既有开源库Guava的RateLimiter, 也有JUC中自带的 Semaphore、BlockingQueue等。拿来随手就可以使用。

那么在分布式场景中, 限流就变得不是那么容易了。 在这种环境上想实现限流本质上是在实现一种多个进程之间的协同工作机制。 必须得依靠一个可靠的协调中心才行,这一般都会选一种中间件来实现。

而 Redis 其实就是一个适合这种场景的中间件,既快,又有强大的数据结构对各种限流算法提供支持。那么我这里就来基于Redis实现一种简单粗暴又好用的限流方案—— 信号量(Semaphore)

关于信号量

这里引用一段维基百科的定义

信号量 (英语: semaphore )又称为 信号标 ,是一个同步对象,用于保持在0至指定最大值之间的一个计数值。当线程完成一次对该 semaphore 对象的等待( wait )时,该计数值减一;当线程完成一次对 semaphore 对象的释放( release )时,计数值加一。当计数值为0,则线程等待该 semaphore 对象不再能成功直至该 semaphore 对象变成 signaled 状态。 semaphore 对象的计数值大于0,为 signaled 状态;计数值等于0,为 nonsignaled 状态.

其实白话说起来很简单,信号量就是可以被 多个线程同时持有 的 一种同步对象,比如我设置一个值为5的计数信号量,那么现在有十个线程来获取他就只会有五个可以成功,剩下那五个则获取失败。

所以说如果有个计数信号量定义的值是1,那么他其实就等同于 mutex (互斥锁)

实现的基本思路

既然知道信号量本质是一种锁,那么对于信号量需要拥有的效果自然就有了思路

  • 拥有获取、释放的机制
  • 需要知道是哪个客户端获取到了信号量
  • 获取到信号量之后, 不能因为客户端的崩溃导致无法释放

对于Redis来说,可以使用 ZSet 来实现这些效果。ZSet 是不可重复的有序集合, 内部每个元素都拥有一个属于自己的分数 (score)

那么我们可以将 ZSet 中一条数据视为客户端获取到的信号量, key就是客户端的唯一标识, score 可以设置为 客户端获取信号量的时间

这样就能够实现上面所说的几种机制

  • 在ZSet中插入数据即为获取。 删除即为释放。
  • 利用ZSet的有序特性, 可以根据 score 的排名 来判断是否成功获取到了信号量
  • 因为 score 存的是客户端获取到信号量的时间, 所以可以约定一个过期时间来对死掉的客户端获取到的信号量进行清除

值得注意的是, 在 Redis 上实现信号量,如果客户端持有信号量之后由于处理时间太久导致没在规定的超时时间内释放的话, 那么这个持有 信号量延时机制 , 是需要自己实现的 (守护线程定时更新等) 因为Redis他本身没有提供这种功能的实现, 所以只能自己动手了。 不过像这种需求在使用到分布式信号量的场景中多数不怎么会出现, 所以也可以不管。

使用 Redis 构建分布式信号量的实现细节

知道了需求和对应的实现思路后, 那么可以来决定一下具体的实现细节

这里就需要对 Redis 的命令有一定的了解, 不过就算不了解也没关系, 反正就这么几个命令。

可行的具体流程
  1. 获取系统当前时间, 因为集合的分数储存的是时间毫秒值, 所以可以通过 ZREMRANGEBYSCORE 清理掉过期的信号量
  2. 使用 ZADD 向集合中添加代表自身信号量的元素, 分数为当前时间
  3. 通过 ZRANK 得到当前客户端在集合中的排名, 如果在许可证数量的范围内 (即不大于信号量最大持有数量) 即视为成功获取信号量
  4. 如果不在范围内, 比如信号量设置的最大许可数为 5, 自己在集合中的排名是5 (
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,让我来回答你的问题。你想了解System.Threading序列的第一篇文章,它从Thread的线程单元状态ApartmentState说起。 Thread类在.NET Framework中是一个非常重要的类,它提供了一种在程序中创建和管理线程的方式。在使用Thread类之前,我们需要了解线程单元状态(ApartmentState)。线程单元状态是指一个线程所在的单元,它决定了线程在运行时的行为。 在.NET Framework中,线程单元状态有两种:Single Threaded Apartment和Multi-Threaded Apartment。Single Threaded Apartment是指只有一个线程可以进入该单元,它适用于单线程的应用程序。Multi-Threaded Apartment是指多个线程可以进入该单元,它适用于多线程的应用程序。 在Thread类中,我们可以使用ApartmentState属性来设置线程单元状态。默认情况下,线程单元状态是Multi-Threaded Apartment。如果我们想将线程单元状态设置为Single Threaded Apartment,可以使用以下代码: ```csharp Thread t = new Thread(new ThreadStart(TestMethod)); t.SetApartmentState(ApartmentState.STA); t.Start(); ``` 在以上代码中,我们创建了一个新的线程,并将线程单元状态设置为Single Threaded Apartment。然后,我们启动线程并开始执行TestMethod方法。 总之,了解线程单元状态对于使用Thread类来创建和管理线程是非常重要的。在下一篇文章中,我们将继续探讨System.Threading序列的内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值