基于SpringBoot使用Redis事务、Redis入门(四)—— 乐观锁尝试构建了一个简单的秒杀demo。
一、逻辑设计
- 发起请求,购买对应的产品及数量
- 接收参数,查询redis对应的key值(产品数量)
- 使用watch监听key
- 开始事务
- key值大于等于购买数量时,命令入队
- 执行事务
二、编码
2.1 Controller
用来接受网页端的请求
@Controller
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
@RequestMapping("/buy")
@ResponseBody
public String buy(@RequestParam("productCode") String productCode, @RequestParam("num") int num){
Long buyNum = productService.buy(productCode,num);
return productCode+"秒杀"+buyNum+"件";
}
}
2.2 Service
处理请求
public interface ProductService {
public Long buy(String productCode,int num);
}
@Service
public class ProductServiceImpl implements ProductService{
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public Long buy(String productCode, int num) {
SessionCallback<Long> sessionCallback = new SessionCallback<Long>() {
@Override
public Long execute(RedisOperations operations) throws DataAccessException {
Long result = new Long(num);
ValueOperations valueOperations = operations.opsForValue();
String productNumStr = (String) valueOperations.get(productCode);
int productNum = Integer.parseInt(productNumStr);
//乐观锁观察产品key
operations.watch(productCode);
//事务开始
operations.multi();
if (productNum>=num){
valueOperations.decrement(productCode,num);
}else{
result = 0l;
}
//事务执行
List exec = operations.exec();
if(exec.size()>0){
System.out.println("产品"+productCode+"秒杀"+num+"件成功!剩余"+(productNum-num)+"件");
}else{
System.out.println("产品"+productCode+"秒杀"+num+"件失败!剩余"+productNum+"件");
}
return result;
}
};
return stringRedisTemplate.execute(sessionCallback);
}
}
三、测试
通过测试工具Jmeter发起并发请求,测试秒杀功能是否满足并发处理,具体Jemter工具的使用看Jemter安装与使用
设置产品的数量10000
1000个线程发起并发请求,每次购买数量1件,如果实现了乐观锁,在事务执行时发现key变化则不执行事务,所以产品剩余的数量应该大于9000件(10000(总数)-1000(线程数)*1(购买数量))
开始进行测试,秒杀成功592件,剩余9408件