Springboot开启两个服务,Nginx实现负载均衡,
使用JMeter进行压测一下接口。初始redis中设置socket值为50
package com.example.demo;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author xu
* @date 24/2/2020 下午1:58
*/
@RestController
public class IndexController {
@Autowired
private RedissonClient redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/deduct")
public String deductStock() throws InterruptedException{
synchronized (this) {
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock > 0){
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock",realStock + "");
System.out.println("扣减成功,剩余:"+realStock+"");
} else {
System.out.println("扣减失败,库存不足");
}
}
return "end";
}
}
问题:重复!!!
利用Redis单线程模型进行改进,如果中间逻辑抛异常那么死锁,try-finally,会出现一些问题问题
package com.example.demo;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* @author xu
* @date 24/2/2020 下午1:58
*/
@RestController
public class IndexController {
@Autowired
private RedissonClient redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/deduct")
public String deductStock() throws InterruptedException {
String lockKey = "product01";
//设置线程的唯一标识
String clientId = UUID.randomUUID().toString();
try {
//利用redis的单线程模型,上锁
//Boolean result = stringRedisTemplate.opsForValue().setIfAbsent("lockKey", "xzy");
//防止宕机死锁
//stringRedisTemplate.expire("lockKey",10, TimeUnit.SECONDS);
//以上两行换为一行代码,原子性
Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientId, 10, TimeUnit.SECONDS);
if (!result) {
return "error";//系统繁忙
}
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣减成功,剩余:" + realStock + "");
} else {
System.out.println("扣减失败,库存不足");
}
} finally {
//判断是删除属于自己的锁
if (clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))) {
//释放锁
stringRedisTemplate.delete(lockKey);
}
}
return "end";
}
}
压测结果:
使用redisson锁,三行代码解决问题
package com.example.demo;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* @author xu
* @date 24/2/2020 下午1:58
*/
@RestController
public class IndexController {
@Autowired
private RedissonClient redisson;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/deduct")
public String deductStock() throws InterruptedException {
String lockKey = "product01";
//1.得到锁
RLock redissonLock = redisson.getLock(lockKey);
try {
//2.添加锁过期时间
redissonLock.lock(10,TimeUnit.SECONDS);
int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
if (stock > 0) {
int realStock = stock - 1;
stringRedisTemplate.opsForValue().set("stock", realStock + "");
System.out.println("扣减成功,剩余:" + realStock + "");
} else {
System.out.println("扣减失败,库存不足");
}
} finally {
//3.释放锁
redissonLock.unlock();
}
return "end";
}
}
压测结果(很成熟了)
redisson分布式锁实现原理
又出现问题,redis主从复制,集群,Master挂掉,还没来得及复制怎么办?
快速提升高并发性能,可以分段加锁