Redis与Redisson
什么是Redis?
Remote Dictionary Server(Redis)远程字典服务器是完全开源免费的,用C语言编写的,遵守BSD开源协议,是一个高性能的(key/value)分布式内存数据库,基于内存运行,并支持持久化的NoSQL数据库,它也通常被称为数据结构服务器,因为值(value)可以是字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
与传统数据库不同的是 Redis 的数据是存在内存中的,所以存写速度非常快,因此 Redis被广泛应用于缓存方向。Redis为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。
Rediis 与其他 key - value 缓存产品有以下几个特点:
性能极高 – Redis读的速度是11W次/s,写的速度是81K次/s
支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
丰富的数据类型,Redis不仅仅支持简单的key-value类型的数据,同时还提供Strings, Lists, Hashes, Sets 及 Ordered Sets 等数据结构的存储。
支持数据的备份,即master-slave模式的数据备份。 Redis使用场景,查询出的数据保存到Redis中,下次查询的时候直接从Redis中拿到数据。不用和数据库进行交互。
什么是Redisson
Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap,SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque,Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe,Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。 关于Redisson项目的详细介绍可以在官方网站找到。
每个Redis服务实例都能管理多达1TB的内存。
Spring Boot整合Redisson
添加依赖
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.5.0</version>
</dependency>
修改application.properties配置
因为是简单的demo,所以简单配置即可
server.port=8082
redisson.address=redis://192.168.1.106:6379
添加配置类
RedisConfig.java
@Configuration
public class RedisConfig {
@Value("${redisson.address}")
private String addressUrl;
@Bean
public RedissonClient getRedisson() throws Exception{
RedissonClient redisson = null;
Config config = new Config();
config.useSingleServer()
.setAddress(addressUrl);
redisson = Redisson.create(config);
System.out.println(redisson.getConfig().toJSON().toString());
return redisson;
}
}
定义redis操作接口
TestRedisService.java
@Service
public interface TestRedisService {
RLock lock(String lockKey);
RLock lock(String lockKey, long timeout);
RLock lock(String lockKey, TimeUnit unit, long timeout);
boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime);
void unlock(String lockKey);
void unlock(RLock lock);
}
定义操作实现类
TestRedisServiceImpl.java
@Component
public class TestRedisServiceImpl implements TestRedisService {
// RedissonClient已经由配置类生成,这里自动装配即可
@Autowired
private RedissonClient redissonClient;
// lock(), 拿不到lock就不罢休,不然线程就一直block
@Override
public RLock lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
return lock;
}
// leaseTime为加锁时间,单位为秒
@Override
public RLock lock(String lockKey, long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, TimeUnit.SECONDS);
return null;
}
// timeout为加锁时间,时间单位由unit确定
@Override
public RLock lock(String lockKey, TimeUnit unit, long timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
return lock;
}
@Override
public boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
return false;
}
}
@Override
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
@Override
public void unlock(RLock lock) {
lock.unlock();
}
}
测试与验证
定义一个restcontroller,作为请求入口,在控制器中使用100个线程模拟一下获取锁的动作,然后不去释放锁,浏览器请求后查看下控制台输出情况。
@RestController
@RequestMapping("/test")
public class TestRedisController {
@Autowired
private TestRedisService testRedisService;
@GetMapping("/test")
@ResponseBody
public void redissonTest() {
String key = "redisson_key";
for (int i = 0; i < 100; i++) {
Thread t = new Thread(() -> {
try {
System.err.println("=============线程开启============" + Thread.currentThread().getName());
/*testRedisService.lock(key,10L); //直接加锁,获取不到锁则一直等待获取锁
Thread.sleep(100); //获得锁之后可以进行相应的处理
System.err.println("======获得锁后进行相应的操作======"+Thread.
currentThread().getName());
testRedisService.unlock(key); //解锁
System.err.println("============================="+
Thread.currentThread().getName());
*/
boolean isGetLock = testRedisService.tryLock(key, TimeUnit.SECONDS, 5L, 10L); // 尝试获取锁,等待5秒,自己获得锁后一直不解锁则10秒后自动解锁
if (isGetLock) {
System.out.println("线程:" + Thread.currentThread().getName() + ",获取到了锁");
Thread.sleep(100);
// 获得锁之后可以进行相应的处理
System.err.println("======获得锁后进行相应的操作======" + Thread.currentThread().getName());
//distributedLocker.unlock(key);
System.err.println("=============================" + Thread.currentThread().getName());
}
} catch (Exception e) {
e.printStackTrace();
}
});
t.start();
}
}
}
=============线程开启============Thread-209
=============线程开启============Thread-210
=============线程开启============Thread-211
=============线程开启============Thread-212
=============线程开启============Thread-213
线程:Thread-122,获取到了锁
======获得锁后进行相应的操作======Thread-122
=============================Thread-122
122线程获得了锁,然后其他线程在等待状态。
下面将解锁代码注释去掉,查看控制台情况:
=============线程开启============Thread-213
======获得锁后进行相应的操作======Thread-130
=============================Thread-130
线程:Thread-130,获取到了锁
======获得锁后进行相应的操作======Thread-130
=============================Thread-130
======获得锁后进行相应的操作======Thread-171
=============================Thread-171
线程:Thread-171,获取到了锁
======获得锁后进行相应的操作======Thread-171
=============================Thread-171
======获得锁后进行相应的操作======Thread-155
=============================Thread-155
线程:Thread-155,获取到了锁
======获得锁后进行相应的操作======Thread-155
=============================Thread-155
======获得锁后进行相应的操作======Thread-148
=============================Thread-148
线程:Thread-148,获取到了锁
======获得锁后进行相应的操作======Thread-148
=============================Thread-148
可以发现,每次在我们设置时间过后,都会有新的线程来获得锁。