目录
[TOC]
官方文档
https://github.com/redisson/redisson
https://github.com/redisson/redisson/wiki
配置及依赖
依赖
下面redisson对应的SpringBoot:2.5.6
redisson和SpringBoot版本对应关系查看链接
1 2 3 4 5 6
| <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.16.0</version> </dependency>
|
配置类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import org.redisson.Redisson; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
import java.io.IOException;
@Configuration public class RedissonConfig {
@Bean(destroyMethod = "shutdown") RedissonClient redisson() throws IOException { Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379"); return Redisson.create(config); } }
|
分布式锁
Redisson各锁
官方文档:https://github.com/redisson/redisson/wiki/8.-%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%92%8C%E5%90%8C%E6%AD%A5%E5%99%A8
- 可重入锁(Reentrant Lock)
- 公平锁(Fair Lock)
- 联锁(MultiLock)
- 红锁(RedLock)
读写锁(ReadWriteLock)
- 读写锁保证读一定能读到最新数据。修改期间,写锁式一个排他锁(互斥锁)。读锁是一个共享锁。
- 读 + 读:相当于无锁,并发读
- 写 + 读:阻塞方式
- 写 + 写:有读锁,写也需要等待
- 读 + 写:有读锁,写也需要等待
信号量(Semaphore)
- 可过期性信号量(PermitExpirableSemaphore)
- 闭锁(CountDownLatch)
Redisson实现分布式锁优点
阻塞式等待,默认锁的时间都是30s
加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30s以后自动删除
锁的自动续期,如果业务时间超长,如果在期间锁过期了,会自动给锁上新的30s,不用担心业务时间长,锁自动过期被删除
redisson使用
该部分文档是未使用redisson-spring-boot-starter
,直接试用的redisson
进行操作
可重入锁使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| @Service @Transactional public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {
public Boolean add(UserEntity userEntity) throws InterruptedException { boolean isSuccess = false;
RLock lock = redissonClient.getLock("user-insert");
lock.lock(); try { UserEntity result = this.baseMapper.selectOne(new LambdaQueryWrapper<UserEntity>().eq(userEntity.getNickname() != null, UserEntity::getNickname, userEntity.getNickname())); Thread.sleep(5000); if (result != null) { throw new RuntimeException(userEntity.getNickname() + "已经插入"); } isSuccess = this.save(userEntity); } finally { lock.unlock(); }
return isSuccess; } }
|
读写锁使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
public void writLock() throws InterruptedException { RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("anyRWLock"); RLock rLock = readWriteLock.writeLock(); try { rLock.lock(); redisUtils.deleteObject("readWriteLock"); redisUtils.setCacheObject("readWriteLock", IdUtil.simpleUUID()); Thread.sleep(15000); } finally { rLock.unlock(); } }
public void readLock() throws InterruptedException { RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("anyRWLock"); RLock rLock = readWriteLock.readLock(); rLock.lock(); System.out.println("读写锁获取值:" + redisUtils.getCacheObject("readWriteLock").toString()); }
|
1 2 3 4 5 6 7 8 9
| @Test void writLock() throws InterruptedException { userService.writLock(); }
@Test void readLock() throws InterruptedException { userService.readLock(); }
|
闭锁使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
public void countDownLatchTest() throws InterruptedException { RCountDownLatch latch = redissonClient.getCountDownLatch("anyCountDownLatch"); latch.trySetCount(5); latch.await(); }
public void countDownLatchDown(int i) throws InterruptedException { RCountDownLatch latch = redissonClient.getCountDownLatch("anyCountDownLatch"); for (int j = 0; j < i; j++) { System.out.println(j); latch.countDown(); Thread.sleep(1000); } }
|
1 2 3 4 5 6 7 8 9
| @Test void contextLoads() throws InterruptedException { userService.countDownLatchTest(); }
@Test void countDownLatchDown() throws InterruptedException { userService.countDownLatchDown(5); }
|
信号量使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
public Boolean parking() throws InterruptedException { RSemaphore park = redissonClient.getSemaphore("park"); park.tryAcquire(); }
public String drive() { RSemaphore park = redissonClient.getSemaphore("park"); park.release(); }
|
分布式限流
参考:https://juejin.cn/post/7203364379339931708
依赖
1 2 3 4 5 6 7 8 9 10 11 12
| <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.23.5</version> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
|
自定义异常类
1 2 3 4 5 6 7 8
|
public class RedissonRateLimitException extends RuntimeException { public RedissonRateLimitException(String message) { super(message); } }
|
自定义注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| import java.lang.annotation.*;
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface RedissonRateLimit {
String key();
long timeOut();
long count(); }
|
核心限流切面类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
| package com.xiaofei.redisson.aspect;
import com.xiaofei.redisson.annotation.RedissonRateLimit; import com.xiaofei.redisson.handler.exception.RedissonRateLimitException; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.redisson.api.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.PostConstruct; import javax.servlet.http.HttpServletRequest; import java.util.concurrent.TimeUnit;
@Slf4j @Aspect @Component @AutoConfigureAfter(RedissonClient.class) public class RedissonRateLimitAspect {
@Autowired private RedissonClient redissonClient;
@PostConstruct public void init() { log.info("Redisson的限流器被加载........."); if (redissonClient == null) { log.warn("Spring容器中没有RedissonClient,Redisson限流器将无法使用............"); } }
@Before("@annotation(redissonRateLimit)") public void redissonRateLimitCheck(RedissonRateLimit redissonRateLimit) { if (redissonRateLimit == null) { return; }
if (redissonClient == null) { log.warn("Spring容器中没有RedissonClient,Redisson限流器将无法使用............"); return; }
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (servletRequestAttributes == null) { log.warn("请求不是http,Redisson限流器将无法使用............"); return; }
log.warn("RedissonRateLimit开始工作");
HttpServletRequest httpServletRequest = servletRequestAttributes.getRequest(); log.info("RedissonRateLimit key:{}, timeOut:{}, count:{}, url:{}", redissonRateLimit.key(), redissonRateLimit.timeOut(), redissonRateLimit.count(), httpServletRequest.getRequestURI());
RRateLimiter rateLimiter = getRedissonRateLimiter(redissonRateLimit); boolean result = rateLimiter.tryAcquire();
if (!result) { throw new RedissonRateLimitException("当前访问人数过多,请稍后再试"); } }
private RRateLimiter getRedissonRateLimiter(RedissonRateLimit redissonRateLimit) { long count = redissonRateLimit.count();
long timeOut = redissonRateLimit.timeOut();
RRateLimiter rateLimiter = redissonClient.getRateLimiter(redissonRateLimit.key());
if (!rateLimiter.isExists()) { rateLimiter.trySetRate(RateType.OVERALL, count, timeOut, RateIntervalUnit.SECONDS); return rateLimiter; }
RateLimiterConfig rateLimiterConfig = rateLimiter.getConfig();
Long rateInterval = rateLimiterConfig.getRateInterval();
Long rate = rateLimiterConfig.getRate();
if (TimeUnit.MILLISECONDS.convert(timeOut, TimeUnit.SECONDS) != rateInterval || count != rate) { rateLimiter.delete(); rateLimiter.trySetRate(RateType.OVERALL, count, timeOut, RateIntervalUnit.SECONDS); } return rateLimiter; } }
|
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
@ApiOperation(value = "根据id查询", httpMethod = "GET", response = ResponseUtils.class, produces = "application/json") @ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "自增id"), }) @RedissonRateLimit(key = "userselectById", timeOut = 1, count = 2) @GetMapping("{id}") public ResponseUtils selectById(@PathVariable Long id) { UserEntity item = userService.selectById(id); return ResponseUtils.success(item); }
@ApiOperation(value = "根据id查询", httpMethod = "GET", response = ResponseUtils.class, produces = "application/json") @ApiImplicitParams({ @ApiImplicitParam(name = "id", value = "主键id"), }) @RedissonRateLimit(key = "roleselectById", timeOut = 1, count = 200) @GetMapping("{id}") public ResponseUtils selectById(@PathVariable Integer id) { TbRoleEntity item = tbRoleService.selectById(id); return ResponseUtils.success(item); }
|