一、前提依赖(除去SpringBoot项目基本依赖外):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!-- 配置使用redis启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 引入阿里fastjson2依赖 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.42</version>
</dependency>
<!--junit 测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
二、我这里用到的实体类(Orderinfo ):
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
public class Orderinfo implements Serializable {
private String onum;
private Integer eid;
private BigDecimal price;
private String fromname;
private String fromaddress;
private String fromtel;
private String toname;
private String toaddress;
private String totel;
private String fromcardnum;
private Integer state;
private Date createtime;
private static final long serialVersionUID = 1L;
public String getOnum() {
return onum;
}
public void setOnum(String onum) {
this.onum = onum == null ? null : onum.trim();
}
public Integer getEid() {
return eid;
}
public void setEid(Integer eid) {
this.eid = eid;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public String getFromname() {
return fromname;
}
public void setFromname(String fromname) {
this.fromname = fromname == null ? null : fromname.trim();
}
public String getFromaddress() {
return fromaddress;
}
public void setFromaddress(String fromaddress) {
this.fromaddress = fromaddress == null ? null : fromaddress.trim();
}
public String getFromtel() {
return fromtel;
}
public void setFromtel(String fromtel) {
this.fromtel = fromtel == null ? null : fromtel.trim();
}
public String getToname() {
return toname;
}
public void setToname(String toname) {
this.toname = toname == null ? null : toname.trim();
}
public String getToaddress() {
return toaddress;
}
public void setToaddress(String toaddress) {
this.toaddress = toaddress == null ? null : toaddress.trim();
}
public String getTotel() {
return totel;
}
public void setTotel(String totel) {
this.totel = totel == null ? null : totel.trim();
}
public String getFromcardnum() {
return fromcardnum;
}
public void setFromcardnum(String fromcardnum) {
this.fromcardnum = fromcardnum == null ? null : fromcardnum.trim();
}
public Integer getState() {
return state;
}
public void setState(Integer state) {
this.state = state;
}
public Date getCreatetime() {
return createtime;
}
public void setCreatetime(Date createtime) {
this.createtime = createtime;
}
}
三、场景-抢单
1. 添加RedisLockUtil:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
public class RedisLockUtil {
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* 加锁
* @param key 键
* @param value 当前时间 + 超时时间
* @return 是否拿到锁
*/
public boolean lock(String key, String value) {
if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
return true;
}
String currentValue = redisTemplate.opsForValue().get(key);
//如果锁过期
if (!StringUtils.isEmpty(currentValue)
&& Long.parseLong(currentValue) < System.currentTimeMillis()) {
//设置新值,返回旧值
String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
//是否已被别人抢占 比对currentValue 和oldValue 是否一致 确保未被其他人抢占
return !StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue);
}
return false;
}
/**
* 解锁
*
* @param key 键
* @param value 当前时间 + 超时时间
*/
public void unlock(String key, String value) {
try {
String currentValue = redisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
redisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Exception e) {
System.out.println("redis解锁异常");
}
}
}
2. 新建RedisLockTest类:
import com.alibaba.fastjson2.JSON;
import com.logistics.order.entity.Orderinfo;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.ArrayList;
import java.util.List;
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
@Controller
public class RedisLockTest {
@Autowired
public StringRedisTemplate stringRedisTemplate;
@Autowired
public RedisLockUtil redisLock;
//模拟入库
@Test
public void insertRedis(){
Orderinfo orderInfo1 = new Orderinfo();
orderInfo1.setOnum("1");
orderInfo1.setEid(1);
Orderinfo orderInfo2 = new Orderinfo();
orderInfo2.setOnum("2");
orderInfo2.setEid(2);
Orderinfo orderInfo3 = new Orderinfo();
orderInfo3.setOnum("3");
orderInfo3.setEid(3);
List<Orderinfo> orderinfoList = new ArrayList<Orderinfo>();
orderinfoList.add(orderInfo1);
orderinfoList.add(orderInfo2);
orderinfoList.add(orderInfo3);
orderinfoList.forEach(x -> {
stringRedisTemplate.boundHashOps("order").put(x.getOnum(), JSON.toJSONString(x));
});
System.out.println("入库成功。");
}
//压测
@GetMapping("/getOrder")
public void getOrder(String onum){
//定义redis锁的key
String lockkey = "orderkey";
//定义锁的超时时间 1s
Long ex = 1000L;
String valueTimeout = System.currentTimeMillis()+ex+"";
//判断锁是否加成功
boolean lock = redisLock.lock(lockkey, valueTimeout);
if(lock){
String orderJson = (String)stringRedisTemplate.boundHashOps("order").get(onum);
Orderinfo order = JSON.parseObject(orderJson, Orderinfo.class);
System.out.println("订单:"+order.getOnum() +" 被抢到。");
stringRedisTemplate.boundHashOps("order").delete(onum);
//释放锁
redisLock.unlock(lockkey,valueTimeout);
}
}
}
3. 进入 Redis 的可视化客户端工具内查看添加信息:
4. 模拟抢单:
@GetMapping("/getOrder")
public void getOrder(String onum){
//定义redis锁的key
String lockkey = "orderkey";
//定义锁的超时时间 1s
Long ex = 1000L;
String valueTimeout = System.currentTimeMillis()+ex+"";
//判断锁是否加成功
boolean lock = redisLock.lock(lockkey, valueTimeout);
if(lock){
String orderJson = (String)stringRedisTemplate.boundHashOps("order").get(onum);
Orderinfo order = JSON.parseObject(orderJson, Orderinfo.class);
System.out.println("订单:"+order.getOnum() +" 被抢到。");
stringRedisTemplate.boundHashOps("order").delete(onum);
//释放锁
redisLock.unlock(lockkey,valueTimeout);
}
}
四、Jmeter压测:
1. 创建线程组:
2. 添加 HTTP 请求:
3. 给一个 Linstener 监听的结果树:
4. 模拟每秒 50 个请求:
5. 设置请求及请求参数:
6. 点击 5 图中的绿色小三角启动压测:
Idea控制台:
Redis分布式锁:
分布式锁,是一种思想,它的实现方式有很多。比如,我们将沙滩当做分布式锁的组件,那么它看起来应该是这样的:
加锁
在沙滩上踩一脚,留下自己的脚印,就对应了加锁操作。其他进程或者线程,看到沙滩上已经有脚印,证明锁已被别人持有,则等待。
解锁
把脚印从沙滩上抹去,就是解锁的过程。
锁超时
为了避免死锁,我们可以设置一阵风,在单位时间后刮起,将脚印自动抹去。
分布式锁的实现有很多,比如基于数据库、memcached、Redis、系统文件、zookeeper等。它们的核心的理念跟上面的过程大致相同。
在这里我们通过单节点Redis实现一个简单的分布式锁。
1、加锁
加锁实际上就是在redis中,给Key键设置一个值,如果设置值成功,则表示客户端获得了锁。
2、解锁
解锁的过程就是将Key键删除
Redis分布式锁实现原理:
利用redis在同一时刻操作一个键的值只能有一个进程的特性,如果能设值成功就获取到锁。解锁,就是删除指定的键。为防止死锁可以设置锁超时时间,如果锁超时就释放锁。