目前大多数单体运用系统中,微做事运用系统中以及分布式系统中,Redis作为缓存中间件被广泛利用。Redis被公认的是基于内存的,高效的,支持多种数据构造的缓存框架,Redis官方网站有这样一段话,对其进行了清晰的描述:
Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache, and message broker. Redis provides data structures such as strings, hashes, lists, sets, sorted sets with range queries, bitmaps, hyperloglogs, geospatial indexes, and streams.
从Redis的官方文档描述,Redis不仅仅可以用来作为高效的数据缓存;基于Redis的高等特性,我们可以基于Redis实现行列步队,分布式锁等功能。
(图片来自网络侵删)在单体运用系统中,所有的业务和功能都在一个运用系统中,也相称所有业务和功能都在一个线程中运行,如果须要对某一资源或者某一变量进行加锁操作,我们完备可以基于程序本身利用的措辞的锁机制来实现,比如Java我们可以采取synchronized和Lock的实现类。
在分布式和微做事系统中,不同功能或者不同业务被拆分成独立的运用,并且完成独立的功能。在支配办法上独立的运用分别被支配到不同的机器上,以及采取集群化支配,一个独立的运用被支配为多个副本。比较于单体运用最直不雅观的差异是:不同的功能或者不同的业务是跨进程运行。
那么,在分布式运用系统中,我们如何对某一竞争资源进行加锁操作呢?
答案:当然是我们须要一种分布式锁机制。
实现分布式锁有多种办理方案,常用的办理方案整理如下:
基于Redis对字符串操作命令SETNX实现;基于Zookeeper的临时有序节点实现;由于Redis是单线程的,基于命令SETNX(set if not exists)可以实现锁的机制;
当一个key在Redis中不存在时;返回1(成功);
当一个key在Redis中存在时;返回0(失落败);
127.0.0.1:6379> SETNX abc 1(integer) 1127.0.0.1:6379> SETNX abc 1(integer) 0
在Redis的帮助文档中,对该SETNX命令是这样描述的:
127.0.0.1:6379> help @string ........................................ SETNX key value summary: Set the value of a key, only if the key does not exist since: 1.0.0
下面我们通过一个大略的demo来演示下,基于Redis如何实现分布式锁机制。
新建一个Java工程redis-distributed-lock,maven依赖如下:
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId></dependency>
redisson封装了对Redis的大多数操作命令,相称于Redis的Java客户端。我们只须要做大略的配置,就可以像redis-cli那样操作Redis。当然,这里也可以选择其他的Redis Java客户端,例如Jedis,RedisTemplate等。
定义一个main函数
public class RedisLockDemo { public static void main(String[] args) throws IOException { Config config = new Config(); config.useSingleServer().setAddress("redis://localhost:6379"); config.useSingleServer().setPassword("123456"); RedissonClient redissonClient = Redisson.create(config); RLock rLock = redissonClient.getLock("lock"); for (int i = 0; i < 20; i++) { //开启自线程 new Thread(() -> { //加锁 rLock.lock(); System.out.println(Thread.currentThread().getName() + " 获取锁"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } finally { //开释锁 rLock.unlock(); } }).start(); } }}
在这里我们采取多线程来仿照一个分布式系统,每一个线程代表一个独立的运用系统。我们在同一韶光开启20个线程,这20个线程同时去抢占锁,如果某一线程抢占到锁,则打印“获取到锁”,没有抢占到锁的线程则处于等待状态。当某一线程抢占到锁后,完成操作后开释掉锁,则其他未抢占到锁的线程被唤醒,并且同时再一次去抢占锁。
运行mian函数实行结果如下:
实行结果
从代码层面和运行结果层面,我们可以看到基于Redis实现了分布式锁的机制。redisson这个Java版的Redis客户端封装了对锁的实现细节,详细封装了如下:
所有线程同时抢占锁,以及抢占到锁的线程开释锁;未抢占到锁的线程壅塞等待;抢占到锁的线程开释锁后,唤醒未抢占到锁的线程,并且同时再次抢占锁;参考:
https://redis.io/
demo源码:
https://github.com/bq-xiao/distributed-lock-demo.git
不积跬步,无以至千里;不积小流,无以成江海!