
Overall
0
Activity
Funds Held
Trading Pairs
0
Registered Location
-
Followers
0
Coinbase为我们的许多服务提供支持的技术之一是快速的键值数据存储Redis 。我们想分享一些我们构建的库,这些库利用了Redis的一些独特功能。
主锁
在Coinbase,确保没有人可以提取比他们有权获得的更多比特币是我们的最高优先事项。为了保证我们的许多系统的正确性,我们大量使用锁定代码的关键部分。例如,如果有人发出发送比特币的请求,我们必须确保多个进程不会同时生成并向网络广播单独的事务。 互斥锁 (mutex)是一个很好理解的并发原语,它可以在线程和进程之间同步对资源的访问。如果您在单个进程中运行多线程应用程序,则可以使用本机互斥实现,但如果您像我们一样在数十台服务器上运行数千个进程,则需要一个分布式锁管理器 (DLM)。 MasterLock是我们对轻量级DLM的渴望以及对使用Redis的许多现有Ruby锁定库的不满意的结果。
使用Redis锁定的基本思想是,如果某个进程想要获得对名为“Account 13”的资源的独占访问权限,则会在Redis中设置密钥“lock:account:13”。只要未事先设置密钥,该进程就可以访问资源并在完成时取消设置密钥。但是,如果已设置密钥,则进程必须等到获取锁的进程取消设置密钥。方便的是,Redis SET命令有一个选项NX,只有当它不存在时才设置密钥。
如果进程无法释放锁定会发生什么?如果您在许多机器上运行许多进程,那么其中至少有一个必然会在某个时刻自发死亡。最简单的解决方案是在所有锁上设置到期时间,如果任何锁保持的持续时间超过该持续时间,则会自动释放。同样,Redis使用SET命令的EX / PX选项可以轻松实现,该命令在提供的密钥上设置到期时间。我们在这种方法中遇到的主要问题是代码的关键部分的执行时间可能变化很大并且难以预测。如果在所有者进程仍然认为它具有锁定时自动释放锁定,那么您最终可能会同时访问受保护资源,这应该永远不会发生 。这就是你赔钱的方式。
另一种方法是与锁定服务建立会话,并确保在会话被删除时执行停止。这是PostgreSQL咨询锁和谷歌内部Chubby服务采用的方法。只要所有者进程处于活动状态,我们就可以通过定期延长锁定到期时间来模拟Redis中的会话,类似于某些会话协议中的keepalive信号。这允许我们保持默认的到期时间短,但保持对锁的控制,即使代码的关键部分运行的时间超过默认的到期时间。这是MasterLock采用的方法。
初始化MasterLock时,它会启动一个后台线程,负责延长锁定生命周期。当另一个线程获得锁定时,它会将其注册到后台线程。只要获取锁的线程处于活动状态且未明确释放锁,后台线程就会定期更新锁上的租约。如果线程或整个进程终止,则允许锁定到期。
Redlock怎么样?
你们当中有些人可能会想“如果Redis出现故障会怎样?”分布式系统中的高可用性锁定是一个众所周知的难题,通常需要部署像ZooKeeper这样的重量级基础架构。 Redis团队提出了一种名为Redlock的算法,该算法使用法定数量的Redis实例注册锁,以提高可用性。如果我们发现自己需要更高可用性的DLM,我们将来可能会在MasterLock中实现这一点。
交通阻塞
我们为Redis提供的另一个用例是速率限制。在整个站点,我们对限制登录尝试,身份验证尝试,API请求等进行评级。速率限制检查必须快速进行,如果用户的请求恰好由不同的服务器处理,则用户不应该限制。
我们选择使用滚动限制来限制各种操作。假设我们希望将特定IP地址的登录尝试次数限制为每小时10次。如果用户在短时间内进行了10次登录尝试,他们现在将无法登录。但是,我们不再要求用户在进行任何更多尝试之前等待一个小时,而是每6分钟允许一次新的尝试。因此,在下一个小时的过程中,剩余的尝试次数将持续补充,直到剩余的尝试次数达到10次。
为实现此目的,TrafficJam需要跟踪每个IP的两条信息:使用的登录尝试次数和上次尝试的时间戳。基于此,TrafficJam可以使用以下等式计算现在剩余的尝试次数。请注意,在客户端代码中配置了最大尝试次数和时间段等限制参数。
remaining = MIN(max,max - used + max *(now - lastUpdate)/ period)
如果每个服务器都试图同时限制同一个动作,我们如何避免检查和设置逻辑中的竞争条件?我们在Redis服务器上运行原子增量操作,而不是检查和设置客户端代码。如果有任何剩余的操作,则操作会增加尝试次数,否则返回false。这样,客户端乐观地尝试增加速率限制,并且如果已经达到限制则拒绝该请求。
使用Redis,客户端可以使用EVAL命令提交要在服务器上同步评估的自定义Lua脚本。 TrafficJam库调用自定义Lua脚本,以便每次检查速率限制时在Redis服务器上以原子方式执行上述增量操作。自定义脚本功能非常强大,MasterLock也使用它来执行安全的锁定版本。
告诉我代码
这些只是我们在Coinbase中使用Redis的两种方式。您可以在GitHub上查看MasterLock和TrafficJam代码,或者在Ruby应用程序中使用gem。
开源我们的分布式工具箱最初发布在The Coinbase Blog on Medium上,人们通过突出显示和回应这个故事来继续对话。