背景:模拟商城秒杀活动减库存,给定库存300
代码:
@RestController
public class IndexControllerCOPY {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/deduct_stock")
public String deductStock() {
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)
System.out.println("扣减成功,剩余库存:" + realStock);
} else {
System.out.println("扣减失败,库存不足");
}
// }
return "end";
}
}
前提条件:模拟集群部署模式下,对库存操作,所以启动两个Tomcat实例,8080和9080端口。并用nginx做负载均衡,配置见下图;
启动redis,然后再启动两个实例,用jmeter压测,通过访问80端口,让nginx监听去交给对应的服务执行请求,1秒200个请求试下,压测配置如下图:
查看结果:
可以发现在库存重复扣减了,一件商品多卖,出现超卖,显然不正确。这个时候怎么解决呢?有些道友可能会说这是因为没有加锁,所以在8080的服务器里面看到了超卖,好的满足你的需求,我加上锁在测试:
@RestController
public class IndexControllerCOPY {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/deduct_stock")
public String deductStock() {
synchronized (this){
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock")); // jedis.get("stock")
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + ""); // jedis.set(key,value)
System.out.println("扣减成功,剩余库存:" + realStock);
} else {
System.out.println("扣减失败,库存不足");
}
}
return "end";
}
}
可以发现:第198在8080和9080服务都出现了。所以要明白,你的synchronize只是针对线程上加锁,我都是集群模式,不同的进程之间你的锁没有可能给另一个加上。至于8080服务为什么会出现2条197的记录,聪明的你自己揣测下,很简单的(8080内卷,9080天天摸鱼,8080人修改的值,被摸鱼先生覆盖了)。
好了今天比较忙,深入优化放到下次继续,如果有人需要看的话,我就找时间完善。



