1|0背景
目前开发过程中,按照公司规范,需要依赖框架中的缓存组件。不得不说,做组件的大牛对CRUD操作的封装,连接池、缓存路由、缓存安全性的管控都处理的无可挑剔。但是有一个小问题,该组件没有对分布式锁做实现,那就要想办法依靠缓存组件自己去实现一个分布式锁了。
什么,为啥要自己实现?有现成的开源组件直接拿过来用不就行了,比如Spring-Integration-Redis提供RedisLockRegistry,Redisson,不比自己去实现快的多。那我得声明一下,本人也不喜欢重复造轮子。具体原因呢,首先是项目中的缓存组件是不能替换的,连接池还可能没有办法复用,其次就是如果对开源组件实现原理不熟悉,那么出了问题,维护起来又需要更多成本。
先说一下当前需要分布式锁的两个场景,一个是微信端access_token刷新(分布式锁可以保证access_token只刷新一次,刷新完成之后放入缓存,其他请求直接从缓存读取);一个是分布式部署的定时任务(分布式锁可以保证同一时刻只有一个节点的定时任务执行)。
2|0什么是分布式锁
在单机部署的情况下,要想保证特定业务在顺序执行,通过JDK提供的synchronized关键字、Semaphore、ReentrantLock,或者我们也可以基于AQS定制化锁。单机部署的情况下,锁是在多线程之间共享的,但是分布式部署的情况下,锁是多进程之间共享的。那么分布式锁要保证锁资源的唯一性,可以在多进程之间共享。
3|0分布式锁特性
- 保证同一个方法在某一时刻只能在一台机器里一个进程中一个线程执行;
- 要保证是可重入锁(避免死锁);
- 要保证获取锁和释放锁的高可用;
4|0分布式锁实现方案对比
- Mysql:一般项目都会用到缓存,不可能都用数据库,强依赖数据库不现实。虽然实现乐观锁和悲观锁很简单,但是性能不佳。
- Redis:首先集群可以提高可用性,其次借助Redis实现分布式锁也很简单,另外有很多框架已经帮我们实现好了,直接拿来用就可以了,很方便。同时定期失效的机制可以解决因网络抖动锁删除失败的问题,所以我比较倾向Redis实现。
- Zookeeper:和Mysql一样,不可能为了用分布式锁而去新增并维护一套Zookeeper集群,其次实现起来还是比较复杂的,实现不好的话还会引起“羊群效应”。如果不是原有系统就依赖Zookeeper,同时压力不大的情况下,一般不使用Zookeeper实现分布式锁。
5|0分布式锁考虑要点
- 锁释放(finally);
- 锁超时设置;
- 锁刷新(定时任务,每2/3的锁生命周期执行);
- 如果锁超时了,防止删除其他线程的锁(其他线程会拿到锁),考虑 value值用线程id标识,当前线程释放锁的时候要判断是否为当前线程的线程id;
- 可重入;
6|0Redis分布式锁
6|1RedisLockRegistry
RedisLockRegistry是spring-integration-redis中提供redis分布式锁实现类。主要