设为首页 收藏本站
查看: 787|回复: 0

[经验分享] 基于redis的高并发秒杀的JAVA-DEMO实现!

[复制链接]

尚未签到

发表于 2017-12-21 11:34:38 | 显示全部楼层 |阅读模式
  在Redis的事务中,WATCH命令可用于提供CAS(check-and-set)功能。假设我们通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Null multi-bulk应答以通知调用者事务执行失败。例如,我们再次假设Redis中并未提供incr命令来完成键值的原子性递增,如果要实现该功能,我们只能自行编写相应的代码。其伪码如下:
  val = GET mykey
  val = val + 1
  SET mykey $val
  以上代码只有在单连接的情况下才可以保证执行结果是正确的,因为如果在同一时刻有多个客户端在同时执行该段代码,那么就会出现多线程程序中经常出现的一种错误场景--竞态争用(race condition)。比如,客户端A和B都在同一时刻读取了mykey的原有值,假设该值为10,此后两个客户端又均将该值加一后set回Redis服务器,这样就会导致mykey的结果为11,而不是我们认为的12。为了解决类似的问题,我们需要借助WATCH命令的帮助,见如下代码:
  WATCH mykey
  val = GET mykey
  val = val + 1
  MULTI
  SET mykey $val
  EXEC
  和此前代码不同的是,新代码在获取mykey的值之前先通过WATCH命令监控了该键,此后又将set命令包围在事务中,这样就可以有效的保证每个连接在执行EXEC之前,如果当前连接获取的mykey的值被其它连接的客户端修改,那么当前连接的EXEC命令将执行失败。这样调用者在判断返回值后就可以获悉val是否被重新设置成功。
  根据这样的思路,我们在JAVA下进行实现:
  新建一个项目,首先引入JAVA的redis操作库:Jedis,这里用的是jedis-2.9.0.jar
  新建一个类:MyRedistest.class做线程操作
  

package com.myredistest;  
import java.util.Random;
  
import java.util.concurrent.ExecutorService;
  
import java.util.concurrent.Executors;
  

  
import redis.clients.jedis.Jedis;
  

  
/**
  * redis
  *
  * @author 10255_000
  *
  */
  


  
public>  public static void main(String[] args) {
  final String watchkeys = "watchkeys";
  ExecutorService executor = Executors.newFixedThreadPool(20);  //20个线程池并发数
  

  final Jedis jedis = new Jedis("192.168.56.101", 6379);
  jedis.set(watchkeys, "100");//设置起始的抢购数
  // jedis.del("setsucc", "setfail");
  jedis.close();
  for (int i = 0; i < 1000; i++) {//设置1000个人来发起抢购
  executor.execute(new MyRunnable("user"+getRandomString(6)));
  }
  executor.shutdown();
  }
  

  public static String getRandomString(int length) { //length是随机字符串长度
  String base = "abcdefghijklmnopqrstuvwxyz0123456789";   
  Random random = new Random();   
  StringBuffer sb = new StringBuffer();   
  for (int i = 0; i < length; i++) {   
  int number = random.nextInt(base.length());   
  sb.append(base.charAt(number));   
  }   
  return sb.toString();   
  }  
  
}
  

  

  建一个类:MyRunnable.class 实现Runnable做线程操作:
  

package com.myredistest;  

  
import java.util.List;
  

  
import redis.clients.jedis.Jedis;
  
import redis.clients.jedis.Transaction;
  


  
public>  

  String watchkeys = "watchkeys";// 监视keys
  Jedis jedis = new Jedis("192.168.56.101", 6379);
  String userinfo;
  public MyRunnable() {
  }
  public MyRunnable(String uinfo) {
  this.userinfo=uinfo;
  }
  @Override
  public void run() {
  try {
  jedis.watch(watchkeys);// watchkeys
  

  String val = jedis.get(watchkeys);
  int valint = Integer.valueOf(val);
  if (valint <= 100 && valint>=1) {
  Transaction tx = jedis.multi();// 开启事务
  // tx.incr("watchkeys");
  tx.incrBy("watchkeys", -1);
  

  List<Object> list = tx.exec();// 提交事务,如果此时watchkeys被改动了,则返回null
  if (list == null ||list.size()==0) {
  

  String failuserifo = "fail"+userinfo;
  String failinfo="用户:" + failuserifo + "商品争抢失败,抢购失败";
  System.out.println(failinfo);
  /* 抢购失败业务逻辑 */
  jedis.setnx(failuserifo, failinfo);
  } else {
  for(Object succ : list){
  String succuserifo ="succ"+succ.toString() +userinfo ;
  String succinfo="用户:" + succuserifo + "抢购成功,当前抢购成功人数:"
  + (1-(valint-100));
  System.out.println(succinfo);
  /* 抢购成功业务逻辑 */
  jedis.setnx(succuserifo, succinfo);
  }
  }
  

  } else {
  String failuserifo ="kcfail" +  userinfo;
  String failinfo1="用户:" + failuserifo + "商品被抢购完毕,抢购失败";
  System.out.println(failinfo1);
  jedis.setnx(failuserifo, failinfo1);
  // Thread.sleep(500);
  return;
  }
  

  } catch (Exception e) {
  e.printStackTrace();
  } finally {
  jedis.close();
  }
  

  }
  
}
  

  

  执行MyRedistest ,查看redis中插入的key值

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-426395-1-1.html 上篇帖子: redis源码笔记(一) —— 从redis的启动到command的分发 下篇帖子: [转] Redis 存储List对象
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表