Redisson简介
Redisson是一个使用Java编写的开源框架,它提供了一系列分布式数据结构和服务,这些服务能够使Java应用具备分布式应用所需的特性和能力。Redisson的主要目标是将Redis作为Java应用程序的数据存储和缓存层,并且尽可能地利用Redis平台提供的高并发、高可用、高性能、高可扩展性等特点。
Redisson提供的特性包括:
- 完善的Redis客户端功能,支持单节点、集群、哨兵模式的自动切换,
- 提供了完整的Java原生API,支持所有Redis支持的数据结构以及扩展的的分布式数据结构,
- 提供了线程安全的同步/异步Redis操作,支持高性能并发操作,
- 提供了分布式实现的超时等待、信号量、倒计时器、锁、消息队列、异步执行等功能。
Redisson的优势
Redisson使用Java编写,易于集成和使用,同时又具有以下几个优势:
- 支持Java的所有数据结构:除了Redis原生支持的数据结构外,Redisson还提供了Java数组、Java集合、Java映射、原子变量、可重入锁等数据结构的分布式实现。
- 线程安全:Redisson提供了线程安全的同步/异步Redis操作,保证多线程安全。
- 高性能:Redisson底层采用Netty网络框架实现,采用序列化机制提高传输效率,支持高并发、高性能的场景。
- 易于部署和管理:Redisson支持多种模式,如单节点、主从复制、哨兵、Redis Cluster等,易于部署和管理。
Redisson的数据结构
Redisson支持的数据结构包括:
- Redis原生类型:String、List、Set、SortedSet、Hash等。
- Java类型:Java数组、Java集合、Java映射、原子变量等。
- Redisson特定数据类型:
1. 原子变量
Redisson提供了一系列原子变量,包括AtomicLong、AtomicDouble、AtomicBoolean等。原子变量支持单节点和分布式环境下的原子操作,可以实现计数器、状态标记等功能。
2. 分布式锁
Redisson提供了分布式锁的实现,支持公平锁、非公平锁、可重入锁、读写锁等。
3. 分布式队列
Redisson提供了分布式队列的实现,支持阻塞队列、无界队列、延迟队列等。
4. 分布式集合
Redisson提供了分布式集合的实现,支持HashSet、TreeSet、LinkedHashSet等。
5. 分布式映射
Redisson提供了分布式映射的实现,支持HashMap、TreeMap、LinkedHashMap等。
6. 分布式消息
Redisson提供了分布式消息的实现,支持发布订阅模式、点对点模式等。
Redisson的分布式锁
分布式锁是实现分布式并发控制的一种常见方式,通过在不同进程之间共享同一个锁来避免多个并发进程竞争同一资源时出现的数据竞争问题。Redisson提供了完整的分布式锁的实现,包括公平锁、非公平锁、可重入锁、读写锁等。下面我们分别来介绍这几种锁的特点:
1. 公平锁
公平锁是指在多个线程竞争同一个锁时,按照先来先得的原则获取锁。Redisson实现的公平锁是基于Redis的brpoplpush命令实现的,当一个线程获取锁失败时,会将该线程加入到队列的尾部,并在头部等待锁释放。当锁被释放时,将队列头的线程唤醒,让其获取锁,从而保证了先来先得的原则。
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取公平锁对象
RLock fairLock = redisson.getFairLock("my_fair_lock");
// 尝试获取锁,如果获取不到则阻塞等待
fairLock.lock();
try {
// 这里编写需要被锁保护的操作
} finally {
// 释放锁
fairLock.unlock();
}
在上面的示例中,首先获取了 Redisson 实例,然后通过它获取了名为 "my_fair_lock" 的公平锁对象。接下来使用 lock()
方法获取锁,并在需要加锁的代码块中执行操作。在最后使用 unlock()
方法释放锁。
需要注意的是,在 Redisson 中,默认的锁都是公平锁,即所有请求锁的线程会按照请求的顺序依次获取锁,而非公平锁则是无序的,在高并发情况下可能会产生饥饿现象。因此,公平锁通常比非公平锁更可靠,但在某些场景下可能会导致性能问题。
2. 非公平锁
非公平锁是指在多个线程竞争同一个锁时,没有优先级规则,只要锁未被占用,任何线程都可以随机获取锁。Redisson实现的非公平锁是基于Redis的setnx命令实现的,通过setnx命令可以实现原子性的获取锁,但是由于非公平锁没有优先级规则,可能导致某些线程一直获取不到锁,从而产生饥饿现象。
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取非公平锁对象
RLock unfairLock = redisson.getLock("my_unfair_lock");
// 尝试获取锁,如果获取不到则阻塞等待
unfairLock.lock();
try {
// 这里编写需要被锁保护的操作
} finally {
// 释放锁
unfairLock.unlock();
}
需要注意的是,在 Redisson 中,默认的锁都是公平锁,即所有请求锁的线程会按照请求的顺序依次获取锁,而非公平锁则是无序的,在高并发情况下可能会产生饥饿现象。因此,非公平锁需要谨慎使用,需要根据具体场景进行选择。
3. 可重入锁
可重入锁是指同一个线程可以多次获取同一个锁而不会死锁。在分布式环境下,可重入锁需要支持跨进程、跨JVM的可重入。Redisson实现的可重入锁是基于Redis的分布式对象实现的,通过在锁上维护一个线程标识和计数器,来实现同一个线程多次获取锁时计数器增加,释放锁时计数器减少的功能。
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取可重入锁对象
RReentrantLock reentrantLock = redisson.getReentrantLock("my_reentrant_lock");
// 尝试获取锁,如果获取不到则阻塞等待
reentrantLock.lock();
try {
// 这里编写需要被锁保护的操作
// 在可重入锁内可以再次获取锁
reentrantLock.lock();
try {
// 这里编写需要被锁保护的操作
} finally {
reentrantLock.unlock();
}
} finally {
// 释放锁
reentrantLock.unlock();
}
在上面的示例中,首先获取了 Redisson 实例,然后通过它获取了名为 "my_reentrant_lock
" 的可重入锁对象。接下来使用 lock()
方法获取锁,并在需要加锁的代码块中执行操作。在最后使用 unlock()
方法释放锁。
需要注意的是,在 Redisson 中,可重入锁可以多次被同一线程获取,使用计数器记录次数,但需要注意在每次获取锁后必须在 finally 块中释放锁,否则会导致死锁。此外,如果需要在一段时间内自动释放锁(如程序异常等情况),可以使用带有超时时间的 tryLock()
方法或者 lockInterruptibly()
方法。
4. 读写锁
读写锁是指在多线程对同一个资源进行读和写的场景下,通过加锁和解锁来实现共享资源的访问控制。读写锁分为读锁和写锁,当一个线程获取写锁时,其他线程无法获取读锁和写锁,当一个线程获取读锁时,其他线程可以获取读锁,但无法获取写锁。Redisson实现的读写锁是基于Redis CAS命令实现的,通过实现不同的加锁策略来达到读写锁的目的。
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取读写锁对象
RReadWriteLock rwLock = redisson.getReadWriteLock("my_rwlock");
// 写操作加锁
RLock writeLock = rwLock.writeLock();
writeLock.lock();
try {
// 执行写操作
} finally {
writeLock.unlock();
}
// 读操作加锁
RLock readLock = rwLock.readLock();
readLock.lock();
try {
// 执行读操作
} finally {
readLock.unlock();
}
在上面的示例中,首先获取了 Redisson 实例,然后通过它获取了名为 "my_rwlock" 的读写锁对象。接下来分别使用 writeLock()
和 readLock()
方法来获取写锁和读锁,然后在需要加锁的代码块中执行 lock()
方法获取锁,执行完操作后使用 unlock()
方法释放锁。
需要注意的是,在执行写操作时必须先获取写锁,而在执行读操作时则可以获取读锁。这是由于 Redisson 读写锁是弱化了可重入性的,即同一线程无法重复获取锁,因此在执行嵌套的写操作时会发生死锁。另外,在使用 Redisson 读写锁时也需要注意避免出现死锁的情况。
Redisson的分布式队列
分布式队列是指将数据存储到某种队列中,并且可以被多个应用程序或进程同时访问。Redisson提供了阻塞队列、无界队列、延迟队列等多种分布式队列的实现,下面我们分别来介绍这几种队列的特点:
1. 阻塞队列
阻塞队列是指当队列为空或者队列已满时,读和写的线程会被阻塞直到队列可用为止。Redisson实现的阻塞队列是基于Redis的brpoplpush命令实现的,通过在列表尾部插入数据,在列表头部弹出数据的方式来实现阻塞队列的功能。
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取阻塞队列对象
RBlockingQueue queue = redisson.getBlockingQueue("my_blocking_queue");
// 向队列中添加元素,如果队列已满则阻塞等待
queue.put("element");
// 从队列中取出元素,如果队列为空则阻塞等待
Object element = queue.take();
2. 无界队列
无界队列是指队列的大小没有限制,可以一直存储数据。Redisson实现的无界队列是基于Redis的list命令实现的,通过在列表尾部插入数据,在列表头部弹出数据的方式来实现无界队列的功能。
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取无界队列对象
RQueue queue = redisson.getQueue("my_unbounded_queue");
// 向队列中添加元素
queue.offer("element");
// 从队列中取出元素
Object element = queue.poll();
3. 延迟队列
延迟队列是指在队列中存储带有过期时间的数据,在过期时间到达时自动从队列中移除。Redisson实现的延迟队列是基于Redis的zset命令实现的,通过将数据存储到zset中,并设置过期时间作为score,通过定时任务轮询zset来实现延迟队列的功能。
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取延迟队列对象
RDelayedQueue<String> delayedQueue = redisson.getDelayedQueue(redisson.getQueue("my_delayed_queue"), new StringCodec());
// 添加延迟元素到队列中,第二个参数是过期时间,单位为秒
delayedQueue.offer("element", 10, TimeUnit.SECONDS);
// 在过期时间到达后,从队列中取出过期元素
String element = delayedQueue.take();
Redisson的分布式集合
分布式集合是指将数据存储在某种集合中,并且可以被多个应用程序或进程同时访问。Redisson提供了HashSet、TreeSet、LinkedHashSet等分布式集合的实现,下面我们分别来介绍这几种集合的特点:
1. HashSet
HashSet是一种无序、不可重复的集合。Redisson实现的HashSet是基于Redis的hash命令实现的,通过在hash数据结构中存储key-value对来实现HashSet的功能。
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取 Hash 集合对象
RMap<String, Object> map = redisson.getMap("my_hash_set");
// 向集合中添加元素
map.put("key", "value");
// 删除集合中的元素
Object value = map.remove("key");
2. TreeSet
TreeSet是一种有序、不可重复的集合。Redisson实现的TreeSet是基于Redis的zset命令实现的,通过将元素存储到zset中,并按照score进行排序来实现TreeSet的功能。
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取 SortedSet 集合对象
RSortedSet<Integer> set = redisson.getSortedSet("my_tree_set");
// 向集合中添加元素
set.add(1);
set.add(2);
set.add(3);
// 取出排名前三的元素
Collection<Integer> topThree = set.entryRange(0, 2);
3. LinkedHashSet
LinkedHashSet是一种有序、不可重复的集合。Redisson实现的LinkedHashSet是基于Redis的list命令实现的,通过在列表中存储元素,并按照插入顺序进行排序来实现LinkedHashSet的功能。
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取 LinkedSet 集合对象
RList<Object> list = redisson.getList("my_linked_set");
// 向集合中添加元素
list.add("element1");
list.add("element2");
list.add("element3");
// 获取集合中的元素
Object element = list.get(0);
Redisson的调度器
Redisson提供了分布式任务调度的功能,可以支持固定频率执行、延迟执行、定时执行等多种任务调度模式。Redisson的调度器是基于Redis的zset命令实现的,通过将任务存储到zset中,并将执行时间作为score,通过定时任务轮询zset来实现任务调度的功能。
固定频率执行任务
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取任务调度器对象
RScheduledExecutorService executorService = redisson.getExecutorService("my_executor_service");
// 提交周期性任务,任务每隔 5 秒执行一次
RFuture<Void> future = executorService.scheduleAtFixedRateAsync(new Runnable() {
@Override
public void run() {
// 这里编写需要执行的任务逻
}
}, 0, 5, TimeUnit.SECONDS);
Redisson的可靠订阅
Redisson提供了可靠订阅的功能,支持订阅主题、模式匹配、按照参数订阅等模式。Redisson的可靠订阅是基于Redis的pub/sub命令实现的,通过向Redis发送subscribe/psubscribe命令来实现订阅功能,通过向Redis发送unsubscribe/punsubscribe命令来取消订阅。在使用可靠订阅时,Redisson会启动一个线程用于接收订阅的消息,并通过回调函数将消息推送给应用程序。
以上就是Redisson框架的基本特点和使用方式,如果您想了解更多内容或者深入学习Redisson,请参考Redisson的官方文档。
发布消息
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取主题对象
RTopic<String> topic = redisson.getTopic("my_topic", new StringCodec());
// 发布消息
topic.publish("hello world");
订阅消息
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取主题对象
RTopic<String> topic = redisson.getTopic("my_topic", new StringCodec());
// 订阅消息
int listenerId = topic.addListener(new MessageListener<String>() {
@Override
public void onMessage(String channel, String message) {
// 处理收到的消息
}
});
// 取消订阅
topic.removeListener(listenerId);
其他
分布式限流器
基于 Redis 的计数器
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取计数器对象
RAtomicLong counter = redisson.getAtomicLong("my_counter");
// 每次请求时增加计数器的值
counter.incrementAndGet();
// 如果计数器的值超过阈值,则限制请求
if (counter.get() > MAX_REQUESTS_PER_SECOND) {
throw new RateLimitException("Too many requests");
}
基于 Redis 的令牌桶
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取令牌桶对象
RTopic<Set<String>> topic = redisson.getTopic("my_token_bucket", new StringCodec());
// 定义令牌桶容量和令牌生成速率
int capacity = 100;
double rate = 10;
// 启动令牌桶生成器
TokenBucketGenerator generator = new TokenBucketGenerator(topic, capacity, rate);
generator.start();
// 在请求时获取令牌,如果获取不到则限制请求
if (!generator.tryAcquire()) {
throw new RateLimitException("Too many requests");
}
分布式对象存储
上传文件
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取 Bucket 对象
RBucket<byte[]> bucket = redisson.getBucket("my_bucket");
// 读取文件内容
byte[] content = Files.readAllBytes(Paths.get("/path/to/my/file"));
// 上传文件到 Redis
bucket.set(content);
下载文件
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取 Bucket 对象
RBucket<byte[]> bucket = redisson.getBucket("my_bucket");
// 从 Redis 中下载文件
byte[] content = bucket.get();
// 将文件内容写入本地文件
Files.write(Paths.get("/path/to/my/file"), content);
存储对象
// 定义对象类
public class MyClass {
private String name;
private int age;
// 省略 getter 和 setter
}
// 获取 Redisson 实例
RedissonClient redisson = Redisson.create();
// 获取对象存储对象
RMap<String, MyClass> map = redisson.getMap("my_data_store");
// 存储对象到 Redis
MyClass data = new MyClass();
data.setName("John Doe");
data.setAge(30);
map.put("key", data);
// 从 Redis 中读取对象
MyClass result = map.get("key");