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;@Beanpublic 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 {@Resourceprivate 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:业务代码
@Overridepublic 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:并发测试
