- 浏览: 64551 次
文章分类
最新评论
-
小色帝:
我是天才是打发
Jquery实现的Tabs页 -
小色帝:
小色帝 写道1111而温热
Jquery实现的Tabs页 -
小色帝:
1111而温热
Jquery实现的Tabs页
废话不多说,首先分享一个业务场景-抢购。一个典型的高并发问题,所需的最关键字段就是库存,在高并发的情况下每次都去数据库查询显然是不合适的,因此把库存信息存入Redis中,利用redis的锁机制来控制并发访问,是一个不错的解决方案。
首先是一段业务代码:
@Transactional
public void orderProductMockDiffUser(String productId){
//1.查库存
int stockNum = stock.get(productId);
if(stocknum == 0){
throw new SellException(ProductStatusEnum.STOCK_EMPTY);
//这里抛出的异常要是运行时异常,否则无法进行数据回滚,这也是spring中比较基础的
}else{
//2.下单
orders.put(KeyUtil.genUniqueKey(),productId);//生成随机用户id模拟高并发
sotckNum = stockNum-1;
try{
Thread.sleep(100);
} catch (InterruptedExcption e){
e.printStackTrace();
}
stock.put(productId,stockNum);
}
}
这里有一种比较简单的解决方案,就是synchronized关键字。
public synchronized void orderProductMockDiffUser(String productId)
这就是java自带的一种锁机制,简单的对函数加锁和释放锁。但问题是这个实在是太慢了,感兴趣的可以可以写个接口用apache ab压测一下。
ab -n 500 -c 100 http://localhost:8080/xxxxxxx
下面就是redis分布式锁的解决方法。首先要了解两个redis指令
SETNX 和 GETSET,可以在redis中文网上找到详细的介绍。
SETNX就是set if not exist的缩写,如果不存在就返回保存value并返回1,如果存在就返回0。
GETSET其实就是两个指令GET和SET,首先会GET到当前key的值并返回,然后在设置当前Key为要设置Value。
首先我们先新建一个RedisLock类:
@Slf4j
@Component
public class RedisService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/***
* 加锁
* @param key
* @param value 当前时间+超时时间
* @return 锁住返回true
*/
public boolean lock(String key,String value){
if(stringRedisTemplate.opsForValue().setIfAbsent(key,value)){//setNX 返回boolean
return true;
}
//如果锁超时 ***
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){
try {
String currentValue = stringRedisTemplate.opsForValue().get(key);
if(!StringUtils.isEmpty(currentValue)&¤tValue.equals(value)){
stringRedisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Exception e) {
log.error("解锁异常");
}
}
}
这个项目是springboot的项目。首先要加入redis的pom依赖,该类只有两个功能,加锁和解锁,解锁比较简单,就是删除当前key的键值对。我们主要来说一说加锁这个功能。
首先,锁的value值是当前时间加上过期时间的时间戳,Long类型。首先看到用setiFAbsent方法也就是对应的SETNX,在没有线程获得锁的情况下可以直接拿到锁,并返回true也就是加锁,最后没有获得锁的线程会返回false。 最重要的是中间对于锁超时的处理,如果没有这段代码,当秒杀方法发生异常的时候,后续的线程都无法得到锁,也就陷入了一个死锁的情况。我们可以假设CurrentValue为A,并且在执行过程中抛出了异常,这时进入了两个value为B的线程来争夺这个锁,也就是走到了注释*的地方。currentValue==A,这时某一个线程执行到了getAndSet(key,value)函数(某一时刻一定只有一个线程执行这个方法,其他要等待)。这时oldvalue也就是之前的value等于A,在方法执行过后,oldvalue会被设置为当前的value也就是B。这时继续执行,由于oldValue==currentValue所以该线程获取到锁。而另一个线程获取的oldvalue是B,而currentValue是A,所以他就获取不到锁啦。多线程还是有些乱的,需要好好想一想。
接下来就是在业务代码中加锁啦:首要要@Autowired注入刚刚RedisLock类,不要忘记对这个类加一个@Component注解否则无法注入
private static final int TIMEOUT= 10*1000;
@Transactional
public void orderProductMockDiffUser(String productId){
long time = System.currentTimeMillions()+TIMEOUT;
if(!redislock.lock(productId,String.valueOf(time)){
throw new SellException(101,"换个姿势再试试")
}
//1.查库存
int stockNum = stock.get(productId);
if(stocknum == 0){
throw new SellException(ProductStatusEnum.STOCK_EMPTY);
//这里抛出的异常要是运行时异常,否则无法进行数据回滚,这也是spring中比较基础的
}else{
//2.下单
orders.put(KeyUtil.genUniqueKey(),productId);//生成随机用户id模拟高并发
sotckNum = stockNum-1;
try{
Thread.sleep(100);
} catch (InterruptedExcption e){
e.printStackTrace();
}
stock.put(productId,stockNum);
}
redisLock.unlock(productId,String.valueOf(time));
}
大功告成了!比synchronized快了不知道多少倍,再也不会被老板骂了!
首先是一段业务代码:
@Transactional
public void orderProductMockDiffUser(String productId){
//1.查库存
int stockNum = stock.get(productId);
if(stocknum == 0){
throw new SellException(ProductStatusEnum.STOCK_EMPTY);
//这里抛出的异常要是运行时异常,否则无法进行数据回滚,这也是spring中比较基础的
}else{
//2.下单
orders.put(KeyUtil.genUniqueKey(),productId);//生成随机用户id模拟高并发
sotckNum = stockNum-1;
try{
Thread.sleep(100);
} catch (InterruptedExcption e){
e.printStackTrace();
}
stock.put(productId,stockNum);
}
}
这里有一种比较简单的解决方案,就是synchronized关键字。
public synchronized void orderProductMockDiffUser(String productId)
这就是java自带的一种锁机制,简单的对函数加锁和释放锁。但问题是这个实在是太慢了,感兴趣的可以可以写个接口用apache ab压测一下。
ab -n 500 -c 100 http://localhost:8080/xxxxxxx
下面就是redis分布式锁的解决方法。首先要了解两个redis指令
SETNX 和 GETSET,可以在redis中文网上找到详细的介绍。
SETNX就是set if not exist的缩写,如果不存在就返回保存value并返回1,如果存在就返回0。
GETSET其实就是两个指令GET和SET,首先会GET到当前key的值并返回,然后在设置当前Key为要设置Value。
首先我们先新建一个RedisLock类:
@Slf4j
@Component
public class RedisService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/***
* 加锁
* @param key
* @param value 当前时间+超时时间
* @return 锁住返回true
*/
public boolean lock(String key,String value){
if(stringRedisTemplate.opsForValue().setIfAbsent(key,value)){//setNX 返回boolean
return true;
}
//如果锁超时 ***
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){
try {
String currentValue = stringRedisTemplate.opsForValue().get(key);
if(!StringUtils.isEmpty(currentValue)&¤tValue.equals(value)){
stringRedisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Exception e) {
log.error("解锁异常");
}
}
}
这个项目是springboot的项目。首先要加入redis的pom依赖,该类只有两个功能,加锁和解锁,解锁比较简单,就是删除当前key的键值对。我们主要来说一说加锁这个功能。
首先,锁的value值是当前时间加上过期时间的时间戳,Long类型。首先看到用setiFAbsent方法也就是对应的SETNX,在没有线程获得锁的情况下可以直接拿到锁,并返回true也就是加锁,最后没有获得锁的线程会返回false。 最重要的是中间对于锁超时的处理,如果没有这段代码,当秒杀方法发生异常的时候,后续的线程都无法得到锁,也就陷入了一个死锁的情况。我们可以假设CurrentValue为A,并且在执行过程中抛出了异常,这时进入了两个value为B的线程来争夺这个锁,也就是走到了注释*的地方。currentValue==A,这时某一个线程执行到了getAndSet(key,value)函数(某一时刻一定只有一个线程执行这个方法,其他要等待)。这时oldvalue也就是之前的value等于A,在方法执行过后,oldvalue会被设置为当前的value也就是B。这时继续执行,由于oldValue==currentValue所以该线程获取到锁。而另一个线程获取的oldvalue是B,而currentValue是A,所以他就获取不到锁啦。多线程还是有些乱的,需要好好想一想。
接下来就是在业务代码中加锁啦:首要要@Autowired注入刚刚RedisLock类,不要忘记对这个类加一个@Component注解否则无法注入
private static final int TIMEOUT= 10*1000;
@Transactional
public void orderProductMockDiffUser(String productId){
long time = System.currentTimeMillions()+TIMEOUT;
if(!redislock.lock(productId,String.valueOf(time)){
throw new SellException(101,"换个姿势再试试")
}
//1.查库存
int stockNum = stock.get(productId);
if(stocknum == 0){
throw new SellException(ProductStatusEnum.STOCK_EMPTY);
//这里抛出的异常要是运行时异常,否则无法进行数据回滚,这也是spring中比较基础的
}else{
//2.下单
orders.put(KeyUtil.genUniqueKey(),productId);//生成随机用户id模拟高并发
sotckNum = stockNum-1;
try{
Thread.sleep(100);
} catch (InterruptedExcption e){
e.printStackTrace();
}
stock.put(productId,stockNum);
}
redisLock.unlock(productId,String.valueOf(time));
}
大功告成了!比synchronized快了不知道多少倍,再也不会被老板骂了!
发表评论
-
c3p0代码动态切换数据源
2018-08-06 17:54 1033public class PARPDatasource imp ... -
sql 优化开多个线程跑
2018-07-11 09:54 619select /*+ parallel(8) */ * fr ... -
redis 事物限制频率和获得令牌
2018-07-11 02:50 885package com.dongnaoedu.tony.ser ... -
StringWriter 接收异常信息
2018-07-05 18:42 401try { int a=1/0; } catch (E ... -
RedisCacheServiceUtil
2018-06-29 15:06 342package com.paic.icorepnbs.web. ... -
jedis 操作redis
2018-06-26 09:13 332//连接redis ,redis的默认端口是6379 Je ... -
多线程缓存优化思想
2018-06-06 15:33 3261.背景 题库 随机生成N张试卷,每张试卷M个题目,每个题目要 ... -
网络编程之使用HttpClient批量上传文件
2018-05-28 01:12 498网络编程之使用HttpClient批量上传文件(一) 2014 ... -
oracle 正则表达式 替换 ‘’:
2018-05-18 18:04 544select /*+ parallel(8) */ t.na ... -
java web 导出 下拉 excle
2018-05-15 17:03 418@RequestMapping(value = "/ ... -
Java Socket编程
2016-12-28 23:28 304对于Java Socket编程而言,有两个概念,一个是Serv ... -
Java多线程学习(吐血超详细总结)
2016-12-27 17:15 378目录(?)[-] 1.一扩展javalangThread类 2 ... -
JAVA多线程实现的三种方式
2016-12-27 17:16 386Java多线程实现方式主要有三种:继承Thread类、实现Ru ... -
Maven 入门菜鸟教程
2016-12-02 10:02 1136学习maven的使用,看到一篇很实用的入门教程(菜鸟级入门) ... -
Java 读写Properties配置文件
2016-08-22 15:00 363Java 读写Properties配置文件 1.Pro ... -
JAVA前端与后端参数传递方法小结
2016-07-26 10:54 45671,从Action中传值到JSP页面的方法 ①在Actio ... -
苟富贵
2013-02-25 09:43 0额梵蒂冈过v梵蒂冈北方多汇报地方环保 -
Request的getParameter和getAttribute方法的区别
2013-01-22 10:01 605HttpServletRequest.getParameter ... -
Eclipse快捷键小结
2012-12-26 11:26 706Eclipse快捷键小结 Eclipse ... -
JAVA去空格
2012-12-20 16:53 618String ccc=” he l l o , w o r l ...
相关推荐
双十一刚过不久,大家都知道在天猫、京东、苏宁等等电商网站上有很多秒杀活动,例如在某一个时刻抢购一个原价1999现在秒杀价只要999的手机时,会迎来一个用户请求的高峰期,可能会有几十万几百万的并发量,来抢这...
2、Redis锁结合使用场景,需要注意什么?超时问题如何解决 在我们日常的开发中,无不都是使用数据库来进行数据的存储,由于一般的系统任务中通常不会存在高并发的情况,所以这样看起来并没有什么问题,可是一旦涉及...
zookeeper分布式锁 自定义注解 统一封装返回 切面使用 设计模式使用 事物、回滚使用 docker、nginx使用 图片服务器OSS使用 stram、lambda使用 多线程、线程池使用 定时任务使用 短信验证、邮件服务使用 JWT验证TOKEN...
什么是redis? Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库。 它通过提供多种键值数据类型来适应不同场景下的存储需求。...(秒杀、抢购、12306等等) 5. 应用排行榜。 6. 网站访问统计。
2021阿里云开发者社区演示资料课程介绍演示资料,代码,视频走进Redis Redis的整体架构,... Redis的高并发实战:抢购系统IO模型和问题,资源竞争与分布式锁,如何利用Redis高并发原理做抢购系统。 Redis的生态DMS /
主要介绍了PHP商品秒杀问题解决方案,结合实例形式详细分析了php结合mysql与redis实现商品秒杀功能的相关操作技巧及注意事项,需要的朋友可以参考下
注意!要求使用的都是redis3.0以上的版本,...分布式集群架构中的session分离。 聊天室的在线好友列表。 任务队列。(秒杀、抢购、12306等等) 应用排行榜。 网站访问统计。 数据过期处理(可以精确到毫秒) 2.Redis集
秒杀系统是一种高并发的应用场景,主要特点是在极短的时间内,大量用户同时访问系统进行抢购操作...监控和日志:对秒杀系统进行监控,关注系统的吞吐量、响应时间等指标,及时发现并解决问题。同时记录系统日志,......
功能实现的目标1-基于springBoot平台,集成Redis,实现注册、登录、商品展示、商品详情、订单详情以及秒杀抢购功能2-解决缓存穿透、缓存雪崩及缓存击穿等问题3-添加消息队列消峰4-利用JMeter进行压测5-master-slave...
基于springBoot实现的一个分布式电商系统,开发里程: 4月21日 搭建springBoot平台 4月21日 集成mybatis框架 4月21日 集成mycat数据库中间件 4月22日 后台认证集成shiro,采用单库 商品数据处理采用分库分表 商城...
采用分布式锁+RQ 实现抢购秒杀 采用 RabbitMQ 实现数据最终一致性 【备注】 主要针对计算机相关专业的正在做毕设的学生和需要项目实战的Java学习者。 也可作为课程设计、期末大作业。包含:项目源码、项目说明等,该...
本课程是基于大型互联网的真实架构进行讲解,秒杀系统技术架构(Spring+SpringMVC+Mybatis+Dubbo+Druid+Ehcache+Redis+RabbitMQ+Zookeeper+jQuery+ajax),技术涵盖JavaEE技术,分布式服务技术,高并发技术,缓存技术...
分散秒杀系统开发环境JDK1.7,Maven,Mysql,Eclipse,SpringBoot1.5.10,zookeeper3.4.6,kafka_2.11,redis-2.8.4,curator-2.10.0项目介绍SpringBoot开发案例从0到1构建分布式秒杀系统,项目案例基本成型,逐步...
本课由360架构师亲授,以360真实秒杀系统为切入点, 从秒杀的功能入手,分层递进讲解,逐步让大家掌握系统的设计、架构以及优化...加入机器人服务识别,自动完成安全认证 黄牛无法重复、多次下单 保证抢购系统公平稳定
分布式秒杀系统 开发环境 JDK1.7、Maven、Mysql、Eclipse、SpringBoot1.5.10、zookeeper3.4.6、kafka_2.11、redis-2.8.4、curator-2.10.0 启动说明 启动前 请配置 application.properties 中相关redis、zk以及kafka...
该项目是模拟互联网高并发场景实现了一套商城秒杀系统,项目前后端分离,实现的功能包括用户登录、查看商品列表、查看秒杀商品详情、秒杀商品下单、下单结果通过邮件(短信)通知用户、用户超时...在秒杀抢购之前我们用
Spring Boot实战者,微服务或分布式系统架构实战者,秒杀系统和高并发实战者,中间件实战者 你将会学到: 学习如何基于Spring Boot构建秒杀系统或者高并发业务系统,以及构建系统时采用的前后端技术栈 内容简介: 本...
本书围绕秒杀抢购应用场景 ,对当下流行的 Dubbox+Sp ing Boot+ Docker 微服务架构解决方案进行讲解。主要内容包括微服务架构介绍、 Dub box 原理及运用、使用 Spring Boot实现微服务 使用 ActiveMQ Redis 承载高兴...
2.tomcat内嵌的容器优化方案来解决线程瓶颈问题;通过管道优化方案来简化了非keepalive下的网络建联开销; 分布式扩展: 1.负载均衡设计; 2.水平扩展/垂直扩展; 查询优化之多级缓存: 1.多级缓存屏障系统,优化了...