在node项目中,咱们常会跑一些定时任务,好比定时发送通知、定时发送邮件等,项目部署的时候,咱们每每是多机多实例部署,这就致使每一个实例都会跑一次一样的任务,因此咱们须要一个分布式事务锁,来保证任务只能跑一次。html
分布式事务锁有多种实现方式,大体分为如下几类node
针对目前的需求,使用数据库锁太过于麻烦,Zookeeper目前生产未使用,而redis项目中恰好有使用,因此咱们采起第二种实现方式git
redis官方推荐了对应的解决方案 Redlock,官方中列出了各个语言的实现,其中有 node 的实现,以下链接github
redLock-node实现redis
库中作了封装,使用起来很是简单数据库
Configuration 配置缓存
var client1 = require('redis').createClient(6379, 'redis1.example.com');
var client2 = require('redis').createClient(6379, 'redis2.example.com');
var client3 = require('redis').createClient(6379, 'redis3.example.com');
var Redlock = require('redlock');
var redlock = new Redlock(
// you should have one client for each independent redis node
// or cluster
[client1, client2, client3],
{
// the expected clock drift; for more details
// see http://redis.io/topics/distlock
driftFactor: 0.01, // time in ms
// the max number of times Redlock will attempt
// to lock a resource before erroring
retryCount: 10,
// the time in ms between attempts
retryDelay: 200, // time in ms
// the max time in ms randomly added to retries
// to improve performance under high contention
// see https://www.awsarchitectureblog.com/2015/03/backoff.html
retryJitter: 200 // time in ms
}
);
复制代码
Locking & Unlocking 锁事务和释放锁bash
// the string identifier for the resource you want to lock
var resource = 'locks:account:322456';
// the maximum amount of time you want the resource locked,
// keeping in mind that you can extend the lock up until
// the point when it expires
var ttl = 1000; // 锁的生存时间,在该时间内,若锁未释放,强行释放
redlock.lock(resource, ttl).then(function(lock) {
// ...do something here...
// unlock your resource when you are done
return lock.unlock()
.catch(function(err) {
// we weren't able to reach redis; your lock will eventually // expire, but you probably want to log this error console.error(err); }); }) 复制代码
经过以上方式,咱们就能够实现分布式事务锁了运维
在测试过程当中,发现事务没有被锁住,一查,发现两台机子的系统时间不一致,有10秒左右的差异(测试伙伴因别的任务手动调整了时间),这就致使时间早的机子先跑了任务,时间慢的机子,在去获取锁的时候,锁早已经释放,因此RedLock 创建在了 Time 是可信的模型上
的。 这里推荐一篇文章 Redis RedLock 完美的分布式锁么? 解释的很是好dom
在别的博客看过一句话
分布式的CAP理论告诉咱们“任何一个分布式系统都没法同时知足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时知足两项。”
优秀但不完美的方案在加上优秀的运维,定可以解决大部分的业务需求。
还有一个关于超时时间ttl
的设定问题,究竟是设定多长时间比较好,若设定过短,在任务还没执行完,锁就释放了,反之,若是设置的时间太长,其余获取锁的线程就可能要平白的多等一段时间,因此这个就要根据具体的业务场景来设定啦
若是redis是使用单台的话,就不必使用 redlock
这个方案,直接使用 setnx
设定一个标志位,就ok了