springboot项目:Redis分布式锁的使用(模拟秒杀系统)

模拟秒杀系统:
第一步:编写Service
package com.payease.service; /**
* liuxiaoming
* 2017-12-14
*/
public interface SecKillService {
/**
* 查询秒杀活动特价商品的信息
*
* @param productId
* @return
*/
String querySecKillProductInfo(String productId); /**
* 模拟不同用户秒杀同一商品的请求
*
* @param productId
* @return
*/
void orderProductMockDiffUser(String productId);
}
第二步:编写Redis加锁解锁工具类
package com.payease.service; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils; /**
* liuxiaoming
* 2017-12-14
*/
@Component
@Slf4j
public class RedisLock { @Autowired
private StringRedisTemplate stringRedisTemplate; /**
* 加锁
* @param key
* @param value 当前时间+超时时间
* @return
*/
public boolean lock(String key, String value) {
if (stringRedisTemplate.opsForValue().setIfAbsent(key, value)) {
return true;
}
//currentValue=A 这两个线程的value都是B 其中一个线程拿到锁
String currentValue = stringRedisTemplate.opsForValue().get(key);
//如果锁过期
if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) {
//获取上一个锁的时间
String oldValue = stringRedisTemplate.opsForValue().getAndSet(key, value);
if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
return true;
}
}
return false;
} /**
* 解锁
* @param key
* @param value
* @return
*/
public void unlock(String key, String value) {
String currentVaule = stringRedisTemplate.opsForValue().get(key);
try {
if (!StringUtils.isEmpty(currentVaule) && currentVaule.equals(value)) {
stringRedisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Exception e) {
log.error("【redis分布式锁】解锁异常,{}" , e);
} }
}
第三步:编写Service实现类
package com.payease.service.impl; import com.payease.exception.SellException;
import com.payease.service.RedisLock;
import com.payease.service.SecKillService;
import com.payease.utils.KeyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import java.util.HashMap;
import java.util.Map; /**
* liuxiaoming
* 1027-12-14
*/
@Service
public class SecKillServiceImpl implements SecKillService { @Autowired
private RedisLock redisLock; private static final int TIMOUT = 10 * 1000; //超时时间 10秒 /**
* 中秋活动 秒杀月饼 限量100000
*/
static Map<String, Integer> products;
static Map<String, Integer> stock;
static Map<String, String> orders; static {
products = new HashMap<>();
stock = new HashMap<>();
orders = new HashMap<>();
products.put("abc123456", 10000);
stock.put("abc123456", 10000);
} private String queryMap(String productId) {
return "中秋活动,月饼特价,限量份"
+ products.get(productId)
+ " 还剩:" + stock.get(productId) + " 份"
+ " 该商品成功下单用户数目:"
+ orders.size() + " 人";
} @Override
public String querySecKillProductInfo(String productId) {
return queryMap(productId);
} /**
* 描述逻辑
*
* @param productId
*/
@Override
public void orderProductMockDiffUser(String productId) { //加锁
long time = System.currentTimeMillis() + TIMOUT;
if (!redisLock.lock(productId, String.valueOf(time))) {
throw new SellException(110, "没抢到,换个姿势再试一遍呀");
}
//1. 查询该商品库存,为0则活动结束。
int stockNum = stock.get(productId);
if (stockNum == 0) {
//库存不足
throw new SellException(100, "活动已经结束,请留意下次活动");
} else { orders.put(KeyUtil.getUniqueKey(), productId);
stockNum = stockNum - 1;
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
stock.put(productId, stockNum);
} //解锁
redisLock.unlock(productId, String.valueOf(time));
} /*@Override
public synchronized void orderProductMockDiffUser(String productId) {
int stockNum = stock.get(productId);
if (stockNum == 0) {
//库存不足
throw new SellException(100, "活动已经结束,请留意下次活动");
} else { orders.put(KeyUtil.genUniqueKey(), productId);
stockNum = stockNum - 1;
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
stock.put(productId, stockNum);
} }*/
}
第四步:编写controller
package com.payease.controller; import com.payease.service.SecKillService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; @RestController
@RequestMapping("/skill")
@Slf4j
public class SecKillController { @Autowired
private SecKillService secKillService; /**
* 查询秒杀活动特价商品的信息
* @param productId
* @return
*/
@GetMapping("/query/{productId}")
public String query(@PathVariable String productId)throws Exception
{
return secKillService.querySecKillProductInfo(productId);
} /**
* 秒杀,没有抢到获得"哎呦喂,xxxxx",抢到了会返回剩余的库存量
* @param productId
* @return
* @throws Exception
*/
@GetMapping("/order/{productId}")
public String skill(@PathVariable String productId)throws Exception
{
log.info("@skill request, productId:" + productId);
secKillService.orderProductMockDiffUser(productId);
return secKillService.querySecKillProductInfo(productId);
}
}
第五步:启动项目 查看浏览器 进行压测
1.查看秒杀情况 http://127.0.0.1:8080/sell/skill/query/abc123456
2. 在Mac终端输入命令: ab -n 500 -c 100 http://127.0.0.1:8080/sell/skill/order/abc123456
进行压测

3.查看浏览器 http://127.0.0.1:8080/sell/skill/query/abc123456

注:

springboot项目:Redis分布式锁的使用(模拟秒杀系统)的更多相关文章
- SpringBoot集成Redis分布式锁以及Redis缓存
https://blog.csdn.net/qq_26525215/article/details/79182687 集成Redis 首先在pom.xml中加入需要的redis依赖和缓存依赖 < ...
- springBoot实现redis分布式锁
参考:https://blog.csdn.net/weixin_44634197/article/details/108308395 .. 使用redis的set命令带NX(not exist)参数实 ...
- springboot+redis分布式锁-模拟抢单
本篇内容主要讲解的是redis分布式锁,这个在各大厂面试几乎都是必备的,下面结合模拟抢单的场景来使用她:本篇不涉及到的redis环境搭建,快速搭建个人测试环境,这里建议使用docker:本篇内容节点如 ...
- 用压测模拟并发、并发处理(synchronized,redis分布式锁)
使用工具:Apache an 测压命令: ab -n 100 -c 100 http://www.baidu.com -n代表模拟100个请求,-c代表模拟100个并发,相当于100个人同时访问 ab ...
- spring-boot 中实现标准 redis 分布式锁
一,前言 redis 现在已经成为系统缓存的必备组件,针对缓存读取更新操作,通常我们希望当缓存过期之后能够只有一个请求去更新缓存,其它请求依然使用旧的数据.这就需要用到锁,因为应用服务多数以集群方式部 ...
- redis分布式锁在springboot中的实现
理论知识 redis分布式锁的实现方案请参考文章 如何优雅地用redis实现分布式锁 本案例简介 以秒杀活动为例子,在多线程高并发的情况下需要保证秒杀业务的线程安全性,确保秒杀记录与所扣库存数 ...
- 项目中用到了Redis分布式锁,了解一下背后的原理
前言 以前在学校做小项目的时候,用到Redis,基本也只是用来当作缓存.现在博主在某金融平台实习,发现Redis在生产中并不只是当作缓存这么简单.在我接触到的项目中,Redis起到了一个分布式锁的作用 ...
- 基于SpringBoot AOP面向切面编程实现Redis分布式锁
基于SpringBoot AOP面向切面编程实现Redis分布式锁 基于SpringBoot AOP面向切面编程实现Redis分布式锁 基于SpringBoot AOP面向切面编程实现Redis分布式 ...
- Redis分布式锁升级版RedLock及SpringBoot实现
分布式锁概览 在多线程的环境下,为了保证一个代码块在同一时间只能由一个线程访问,Java中我们一般可以使用synchronized语法和ReetrantLock去保证,这实际上是本地锁的方式.但是现在 ...
随机推荐
- line-height:150% 和 line-height:1.5
line-height属性的细节与大多数CSS属性不同,line-height支持属性值设置为无单位的数字.有无单位在子元素继承属性时有微妙的不同. 有单位(包括百分比)与无单位之间的区别有单位时,子 ...
- 网格去噪 Mesh Denoising Guided by Patch Normal Co-filtering via Kernel Low-rank Recovery
http://staff.ustc.edu.cn/~lgliu/ 网格去噪 https://blog.csdn.net/lafengxiaoyu/article/details/73656060
- android分屏
上手了Android N Preview,第一个不能错过的新特性就是App分屏的支持.Android7.0原生系统就可以支持两个App横屏并排或者竖屏上下摆放了.第二个新特性就是在Android TV ...
- R12 查询EBS用户相关SQL
--R12查询EBS在线用户SQL SELECT U.USER_NAME, APP.APPLICATION_SHORT_NAME, FAT.APPLICATION_NAME, ...
- Android-SurfaceView生命周期
SurfaceView的生命周期,和 Activity生命周期,Service生命周期,BroadcastReceiver生命周期,等,不一样: 因为SurfaceView显示的是(视频画面,游戏画面 ...
- zabbix监控cpu jumps
cpu监控图形分为三种 cpu jumps cpu突发 包含 context switches per second 进程线程切换 interrupts per second 每秒的中断次数 cpu ...
- NETCore 同步AD域组织和用户
BitAdminCore为没有自主开发框架的小团队,提供快速项目搭建及开发能力. 框架演示:http://bit.bitdao.cn 框架源码:https://github.com/chenyinxi ...
- AppDomain.CurrentDomain.BaseDirectory项目目录相关操作
链接:https://www.cnblogs.com/guolianyu/p/3980971.html 经常用到,每次都百度,所以自己备份一下!
- XHTML与HTML、HTML5的区别
XHTML与HTML最主要的区别 XHTML 元素必须被正确地嵌套. XHTML 元素必须被关闭. XHTML标签名必须用小写字母. XHTML 文档必须拥有根元素. HTML5 HTML5是很有野心 ...
- 在 django模型中封装元组和字典, 字段中使用chioce参数实现数据的一一对应
一.models.py中 class OrderInfo(BaseModel): '''订单模型类''' # 封装一个字典, 便于在视图中取值, 进行比对 PAY_METHODS = { : &quo ...