下载安装redisson
整合
maven添加依赖
使用的版本是3.13,因为springboot使用的是2.1.3版本,因此按照官方上的提示,移除redisson-spring-data-22
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-22</artifactId>
</exclusion>
</exclusions>
<version>3.13.1</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-data-21</artifactId>
<version>3.13.1</version>
</dependency>
项目配置
spring:
redis:
redisson:
config: classpath:SentinelConfig-local.yml
SentinelConfig-local.yml文件在项目的resource目录下,具体参数查看redisson依赖包下面的config/SentinelServersConfig.java
#本地开发使用
sentinelServersConfig:
#baseConfig
idleConnectionTimeout: 10000
connectTimeout: 10000
timeout: 3000
retryAttempts: 3
retryInterval: 1500
password: 123456
clientName: null
loadBalancer: !<org.redisson.connection.balancer.RoundRobinLoadBalancer> {}
#BaseMasterSlaveServersConfig
slaveConnectionMinimumIdleSize: 24
slaveConnectionPoolSize: 64
failedSlaveReconnectionInterval: 3000
failedSlaveCheckInterval: 180000
masterConnectionMinimumIdleSize: 24
masterConnectionPoolSize: 64
subscriptionConnectionMinimumIdleSize: 1
subscriptionConnectionPoolSize: 50
dnsMonitoringInterval: 5000
#SentinelServersConfig
database: 8
sentinelAddresses:
- "redis://192.168.1.213:26379"
- "redis://192.168.1.214:26380"
- "redis://192.168.1.214:26381"
masterName: "mymaster"
scanInterval: 1000
threads: 0
# Netty线程池数量,默认值: 当前处理核数量 * 2
nettyThreads: 0
# 编码
codec: !<org.redisson.codec.JsonJacksonCodec> {}
# 传输模式
transportMode : "NIO"
添加工具类
添加DistributeLocker接口,以及它的实现类RedissonDistributeLocker
public interface DistributeLocker {
/**
* 加锁
*
* @param lockKey key
*/
void lock(String lockKey);
/**
* 释放锁
*
* @param lockKey key
*/
void unlock(String lockKey);
/**
* 加锁锁,设置有效期
*
* @param lockKey key
* @param timeout 有效时间,默认时间单位在实现类传入
*/
void lock(String lockKey, int timeout);
/**
* 加锁,设置有效期并指定时间单位
*
* @param lockKey key
* @param timeout 有效时间
* @param unit 时间单位
*/
void lock(String lockKey, int timeout, TimeUnit unit);
/**
* 尝试获取锁,获取到则持有该锁返回true,未获取到立即返回false
*
* @param lockKey
* @return true-获取锁成功 false-获取锁失败
*/
boolean tryLock(String lockKey);
/**
* 尝试获取锁,获取到则持有该锁leaseTime时间.
* 若未获取到,在waitTime时间内一直尝试获取,超过waitTime还未获取到则返回false
*
* @param lockKey key
* @param waitTime 尝试获取时间
* @param leaseTime 锁持有时间
* @param unit 时间单位
* @return true-获取锁成功 false-获取锁失败
*/
boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit);
/**
* 锁是否被任意一个线程锁持有
*
* @param lockKey
* @return true-被锁 false-未被锁
*/
boolean isLocked(String lockKey);
/**
* lock.isHeldByCurrentThread()的作用是查询当前线程是否保持此锁定
* @param lockKey
* @return
*/
boolean isHeldByCurrentThread(String lockKey);
/**
* 添加元素到布隆过滤器中
* @param bloomFilterName
* @param value
*/
void bfAdd(String bloomFilterName, String value);
/**
* 初始化布隆过滤器
* @param bloomFilterName 默认给了expectedInsertions = 1000000L falseProbability = 0.0001
* @return 布隆过滤器名称
*/
RBloomFilter<String> init(String bloomFilterName);
/**
* 初始化布隆过滤器
* @param bloomFilterName 名称
* @param expectedInsertions 预计插入元素总量
* @param falseProbability 精度(误判率)
* @return 布隆过滤器名称
*/
RBloomFilter<String> init(String bloomFilterName, Long expectedInsertions, Double falseProbability);
/**
* 删除布隆过滤器(注意不是删除元素)
* @param bloomFilterName 名称
*/
void bfDelete(String bloomFilterName);
/**
* 判断
* @param bloomFilterName 名称
* @param value false表示则一定不存在,true 则可能存在
* @return
*/
boolean bfContains(String bloomFilterName, String value);
}
public class RedissonDistributeLocker implements DistributeLocker {
private final RedissonClient redissonClient;
public RedissonDistributeLocker(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
@Override
public void lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
}
@Override
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
@Override
public void lock(String lockKey, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, TimeUnit.MILLISECONDS);
}
@Override
public void lock(String lockKey, int timeout, TimeUnit unit) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
}
@Override
public boolean tryLock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
return lock.tryLock();
}
@Override
public boolean tryLock(String lockKey, long waitTime, long leaseTime,
TimeUnit unit) {
RLock lock = redissonClient.getLock(lockKey);
try {
return lock.tryLock(waitTime, leaseTime, unit);
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean isLocked(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
return lock.isLocked();
}
@Override
public boolean isHeldByCurrentThread(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
return lock.isHeldByCurrentThread();
}
@Override
public void bfAdd(String bloomFilterName, String value) {
RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(bloomFilterName);
bloomFilter.add(value);
}
@Override
public RBloomFilter<String> init(String bloomFilterName) {
long expectedInsertions = 1000000L;
double falseProbability = 0.0001;
RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(bloomFilterName);
bloomFilter.tryInit(expectedInsertions, falseProbability);
return bloomFilter;
}
@Override
public RBloomFilter<String> init(String bloomFilterName, Long expectedInsertions, Double falseProbability) {
RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(bloomFilterName);
bloomFilter.tryInit(expectedInsertions, falseProbability);
return bloomFilter;
}
@Override
public void bfDelete(String bloomFilterName) {
RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(bloomFilterName);
bloomFilter.delete();
}
@Override
public boolean bfContains(String bloomFilterName, String value) {
RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter(bloomFilterName);
return bloomFilter.contains(value);
}
}
添加工具类
public class RedissonUtils {
private static DistributeLocker locker;
public static void setLocker(DistributeLocker locker) {
RedissonUtils.locker = locker;
}
public static void lock(String lockKey) {
locker.lock(lockKey);
}
public static void unlock(String lockKey) {
locker.unlock(lockKey);
}
public static void lock(String lockKey, int timeout) {
locker.lock(lockKey, timeout);
}
public static void lock(String lockKey, int timeout, TimeUnit unit) {
locker.lock(lockKey, timeout, unit);
}
public static boolean tryLock(String lockKey) {
return locker.tryLock(lockKey);
}
public static boolean tryLock(String lockKey, long waitTime, long leaseTime,
TimeUnit unit) {
return locker.tryLock(lockKey, waitTime, leaseTime, unit);
}
public static boolean isLocked(String lockKey) {
return locker.isLocked(lockKey);
}
public static boolean isHeldByCurrentThread(String lockKey) {
return locker.isHeldByCurrentThread(lockKey);
}
public static void bfAdd(String bloomFilterName, String value) {
locker.bfAdd(bloomFilterName, value);
}
public static void bfDelete(String bloomFilterName) {
locker.bfDelete(bloomFilterName);
}
public static RBloomFilter<String> init(String bloomFilterName) {
return locker.init(bloomFilterName);
}
public static RBloomFilter<String> init(String bloomFilterName, Long expectedInsertions, Double falseProbability) {
return locker.init(bloomFilterName, expectedInsertions, falseProbability);
}
public static boolean bfContains(String bloomFilterName, String value) {
return locker.bfContains(bloomFilterName, value);
}
}
把工具类注入到spring容器中
@Bean
public RedissonDistributeLocker redissonLocker(RedissonClient redissonClient) {
RedissonDistributeLocker locker = new RedissonDistributeLocker(redissonClient);
RedissonUtils.setLocker(locker);
return locker;
}
测试
布隆过滤器测试
public static void main(String[] args) {
Config config = new Config();
config.useSentinelServers()
.setMasterName("mymaster")
.addSentinelAddress("redis://192.168.1.213:26379", "redis://192.168.1.214:26380", "redis://192.168.1.214:26381")
.setPassword("123456")
.setDatabase(11)
.setRetryInterval(5000)
.setTimeout(10000)
.setConnectTimeout(10000);
RedissonClient redissonClient = Redisson.create(config);
DistributeLocker locker = new RedissonDistributeLocker(redissonClient);
RedissonUtils.setLocker(locker);
String bloomFilterName = "bloomFilter";
RedissonUtils.bfDelete(bloomFilterName);
long st = System.currentTimeMillis();
RedissonUtils.isLocked("123");
log.debug("test connection used time={} ms", System.currentTimeMillis() - st);
long st2 = System.currentTimeMillis();
RedissonUtils.isLocked("123");
log.debug("test connection used time={} ms", System.currentTimeMillis() - st2);
RBloomFilter<String> bloomFilter = RedissonUtils.init(bloomFilterName);
for (int i = 0; i < 1000; i++) {
RedissonUtils.bfAdd(bloomFilterName, bloomFilterName + i);
}
log.debug("insert 1000 data used time={} ms", System.currentTimeMillis() - st);
// log.debug("current bloomFilter count={}", bloomFilter.count());
log.debug("bfContains 1001={} require false", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1001));
log.debug("bfContains 1002={} require false", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1002));
log.debug("bfContains 1003={} require false", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1003));
log.debug("bfContains 1004={} require false", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1004));
log.debug("bfContains 1005={} require false", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1005));
for (int i = 1000; i < 2000; i++) {
RedissonUtils.bfAdd(bloomFilterName, bloomFilterName + i);
}
log.debug("bfContains 1001={} require true", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1001));
log.debug("bfContains 1002={} require true", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1002));
log.debug("bfContains 1003={} require true", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1003));
log.debug("bfContains 1004={} require true", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1004));
log.debug("bfContains 1005={} require true", RedissonUtils.bfContains(bloomFilterName, bloomFilterName + 1005));
}
应用
分布式锁-防重复提交
避免用户同一个操作,因为网络延迟等原因,多次提交到服务端,导致的重复操作。
通过切面方法,在切面内实现,方法进来时,尝试加锁,加锁成功则继续操作,加锁失败则返回操作失败
@Around(value = "pointcut(logAndNoRepeatSubmit)")
public Object around(ProceedingJoinPoint point, LogAndNoRepeatSubmit logAndNoRepeatSubmit) throws Throwable {
String key = LogAspectUtil.getKeyFromRequest();
if (RedissonUtils.tryLock(key)) {
Object result = LogAspectUtil.getObject(point, key);
return result;
} else {
return R.fail("操作太频繁,请稍后再试");
}
}
/**
* 根据用户的uid+用户请求的url路径作为唯一标识
* @return 用户请求唯一标识
*/
public static String getKeyFromRequest() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// Assert.notNull(request, "request can not null");
// 此处可以用token或者JSessionId
// String token = request.getHeader("Authorization");
String path = request.getServletPath();
long uid = JwtUtil.getUid();
return uid + path;
}
public static Object getObject(ProceedingJoinPoint point, String key) throws Throwable {
Object result = null;
try {
result = point.proceed();
} catch (Throwable e) {
log.error("不重复提交日志", e);
throw e;
} finally {
//双重检查释放锁
if (RedissonUtils.isLocked(key)) {
if (RedissonUtils.isHeldByCurrentThread(key)) {
RedissonUtils.unlock(key);
}
}
}
return result;
}
布隆过滤器-防缓存穿透
待实现
思路:
1、通过任务调度器或者在项目启动完成之后,把项目中免权限的表查询的id初始化到布隆过滤器
2、查询进来之前,先通过布隆过滤器判断,是否存在,不存在则直接返回不存在,可能存在才去查找数据库
3、对于后续增长的id,则可以通过同步的方式或者查询数据库成功时,动态添加到过滤器中