文章目录
概念
redisson是一个简单易用的Redis客户端工具。不仅如此,它还具备分布式锁的功能
准备工作
- 快速整合SSMP
请参考我这篇文章 Spring Boot快速整合Spring MVC和Mybatis-Plus,实现基本的增删改查功能 - 创建一张库存表
CREATE TABLE `t_storage` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`product_id` int(11) NOT NULL COMMENT '商品id',
`quantity` int(11) NOT NULL COMMENT '商品数量',
`flag` tinyint(4) NOT NULL DEFAULT '1' COMMENT '删除标志:0-删除,1-不删除',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='库存表';
- 新增一条测试数据
INSERT INTO `t_storage` (`id`, `product_id`, `quantity`, `flag`, `create_time`, `update_time`) VALUES (1, 1, 1000, 1, '2024-06-01 09:22:56', '2024-06-01 15:17:55');
- 创建实体类
@Data
@TableName("t_storage")
public class Storage implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private Integer productId;
private Integer quantity;
private Integer flag;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
- 创建Mapper
public interface StorageMapper extends BaseMapper<Storage> {
}
- 创建Service
public interface StorageService {
String reduceStorage(Integer productId);
}
@Service
public class StorageServiceImpl implements StorageService {
@Autowired
private StorageMapper storageMapper;
@Override
public String reduceStorage(Integer productId) {
return "库存扣减成功";
}
}
- 创建Controller
@RestController
@RequestMapping("/storage")
public class StorageController {
@Autowired
private StorageService storageService;
@GetMapping("/reduceStorage/{productId}")
public String reduceStorage(@PathVariable Integer productId) {
return storageService.reduceStorage(productId);
}
}
-
下载解压Nginx服务器
Nginx下载地址 -
下载解压Jmeter压测工具
Jmeter下载地址 -
复制Spring Boot服务模拟集群环境
-
配置Nginx
-
打开Nginx的conf/nginx.conf配置文件
-
在http块内新增ip池
upstream webservers{ server ip:port; # 请写你的ip和端口。如127.0.0.1:10000 server ip:port; # 请写你的ip和端口。如127.0.0.1:10001 }
-
在server块替换location配置
location / { proxy_pass http://webservers; index index.html index.htm; }
-
重启Nginx
-
-
配置Jmeter
-
启动Jmeter
双击 bin/jmeter.bat -
添加线程组
-
设置线程数和压测时间
-
添加http请求配置
-
配置Nginx地址和接口地址
-
添加一个结果报告
-
synchronized本地锁演示
- 编写代码
@Override
public String reduceStorage(Integer productId) {
// synchronized 本地锁
synchronized (this) {
Storage storage = storageMapper.selectOne(new LambdaQueryWrapper<Storage>().eq(Storage::getProductId, productId));
if (storage.getQuantity() > 0) {
storage.setQuantity(storage.getQuantity() - 1);
storageMapper.updateById(storage);
} else {
System.out.println("已经没有库存了");
}
}
return "库存扣减成功";
}
- 单体环境(只启动其中一个服务)
Jmeter中开启1000个线程并10秒完成压测,数据库库存变为0。所以说单体环境中,synchronized本地锁保证了线程安全
- 集群环境(启动两个服务)
Jmeter中开启1000个线程并10秒完成压测,数据库库存不再为0。所以说集群环境中,synchronized本地锁不保证线程安全
JUC包的Lock本地锁演示
- 编写代码
private final Lock lock = new ReentrantLock();
@Override
public String reduceStorage(Integer productId) {
// Lock 本地锁
try {
lock.lock();
Storage storage = storageMapper.selectOne(new LambdaQueryWrapper<Storage>().eq(Storage::getProductId, productId));
if (storage.getQuantity() > 0) {
storage.setQuantity(storage.getQuantity() - 1);
storageMapper.updateById(storage);
} else {
System.out.println("已经没有库存了");
}
} finally {
lock.unlock();
}
return "库存扣减成功";
}
- 单体环境(只启动其中一个服务)
Jmeter中开启1000个线程并10秒完成压测,数据库库存变为0。所以说单体环境中,Lock本地锁保证了线程安全
- 集群环境(启动两个服务)
Jmeter中开启1000个线程并10秒完成压测,数据库库存不再为0。所以说集群环境中,Lock本地锁不保证线程安全
Redisson的RLock分布式锁演示
- 启动Redis
关于Redis的使用可以参考我的这篇文章 Docker快速安装Redis并实现Spring Boot整合Redis - 引入maven依赖
<!-- redisson依赖 -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.30.0</version>
</dependency>
- 配置Redisson客户端
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
// 1. Create config object
Config config = new Config();
config.useSingleServer().setAddress("redis://redis_ip:redis_port");
return Redisson.create(config);
}
}
- 编写代码
@Autowired
private RedissonClient redissonClient;
@Override
public String reduceStorage(Integer productId) {
// RLock 分布式琐
RLock rLock = redissonClient.getLock("redissonLock");
try {
rLock.lock();
Storage storage = storageMapper.selectOne(new LambdaQueryWrapper<Storage>().eq(Storage::getProductId, productId));
if (storage.getQuantity() > 0) {
storage.setQuantity(storage.getQuantity() - 1);
storageMapper.updateById(storage);
} else {
System.out.println("已经没有库存了");
}
} finally {
rLock.unlock();
}
return "库存扣减成功";
}
- 单体环境(只启动其中一个服务)
Jmeter中开启1000个线程并10秒完成压测,数据库库存变为0。所以说单体环境中,Redisson分布式锁保证了线程安全
- 集群环境(启动两个服务)
Jmeter中开启1000个线程并10秒完成压测,数据库库存变为0。所以说集群环境中,Redisson分布式锁保证了线程安全
源码地址
git clone https://gitee.com/xiaolin-v-java/redission-demo.git