1:pom依赖
<!-- redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.6.5</version>
</dependency>
2:相关配置
2.1 RedissonConfig
package com.ly.cloud.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
@Configuration
public class RedissonConfig {
@Value("${spring.redis.host}")
private String address;
@Value("${spring.redis.port}")
private String port;
@Value("${spring.redis.password}")
private String password;
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer()
.setAddress("redis://" + address + ":"+port)
.setPassword(password)
.setTimeout(10000)
.setRetryAttempts(5)
.setRetryInterval(2000)
.setConnectionPoolSize(64)
.setConnectionMinimumIdleSize(24)
// 保持连接活动
.setKeepAlive(true)
// 发送PING命令的间隔时间;
.setPingConnectionInterval(30000);
return Redisson.create(config);
}
}
2.2 RedissonDistributedLock
package com.ly.cloud.components;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Component
public class RedissonDistributedLock {
@Resource
private RedissonClient redissonClient;
public boolean tryLock(String lockKey, long expireTime, long waitTime) throws InterruptedException {
RLock lock = redissonClient.getLock(lockKey);
return lock.tryLock(waitTime, expireTime, TimeUnit.MILLISECONDS);
}
public void releaseLock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
3:业务代码
@Override
public Boolean insertLectureRegistration(String lectureId) throws InterruptedException {
String loginUserId = LoginUserUtil.getLoginUserId();
// 判断当前 用户是否已经报名该讲座
// if (lectureMessageMapper.getIsRegistrationLectrure(lectureId,loginUserId) > 0) {
// throw new BusinessException("用户:" + loginUserId + "已报名成功,无需第二次报名!");
// }
LectureRegistration registration = new LectureRegistration();
registration.setId(IdUtil.simpleUUID());
registration.setUserId(loginUserId);
registration.setUserName(userMapper.getUserName(loginUserId));
registration.setRegistrationTime(DateTimeUtils.getTimeStamp());
String departmentId = departmentMapper.getDepartmentById(loginUserId);
registration.setRegistrationDepartmentNumber(departmentId);
registration.setRegistrationDepartmentName(departmentMapper.getDepartmentName(departmentId));
registration.setLectureNumber(lectureId);
registration.setDeleted("1");
int total = lectureMessageMapper.getRegistrationPeopleTotal(lectureId);
if (total == 0) {
return lectureMessageMapper.insertLectureRegistration(registration) > 0;
}
String lockKey = lectureId + "stock";
// 只在 Redis 不存在时初始化库存
Boolean stockExists = stringRedisTemplate.hasKey(lockKey);
if (Boolean.FALSE.equals(stockExists)) {
stringRedisTemplate.opsForValue().setIfAbsent(lockKey, String.valueOf(total));
}
boolean locked = redissonDistributedLock.tryLock(lectureId, 1000, 5000);
try {
if (locked) {
stringRedisTemplate.watch(lockKey);
String stockStr = stringRedisTemplate.opsForValue().get(lockKey);
if (stockStr == null) {
throw new BusinessException("讲座报名名额数据异常");
}
int stock = Integer.parseInt(stockStr);
if (stock > 0) {
stringRedisTemplate.setEnableTransactionSupport(true);
stringRedisTemplate.multi();
Long realStock = stringRedisTemplate.opsForValue().increment(lockKey, -1);
List<Object> results = stringRedisTemplate.exec();
if (!CollectionUtils.isEmpty(results)) {
logger.info("讲座报名名额剩余:{}", realStock);
return lectureMessageMapper.insertLectureRegistration(registration) > 0;
} else {
throw new BusinessException("讲座报名名额已用完,无法报名!");
}
} else {
throw new BusinessException("讲座报名名额已用完,无法报名!");
}
}
} catch (Exception e) {
throw new BusinessException("报名失败:", e);
} finally {
if (locked) {
redissonDistributedLock.releaseLock(lectureId);
}
}
return true;
}
4:并发测试
