redis简单秒杀实例(初学者)
时间:2021-08-08
本文章向大家介绍redis简单秒杀实例(初学者),主要包括redis简单秒杀实例(初学者)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
超卖问题(可以使用事务方式(乐观锁)通过对比本事务与数据库版本号进行控制(需要用watch进行监视) 也可以使用LUA脚本(包含在一个Lua脚本里面的redis命令具备原子性))
连接超时问题(可以使用jedis连接池)
库存遗留问题(就是事务经常失败,库存还有很多)(可以使用LUA脚本进行解决(原子性:挨着运行))
使用的是jsp加servlet
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <h1>iPhone 13 Pro !!! 1元秒杀!!! </h1> <form id="msform" action="${pageContext.request.contextPath}/doseckill" enctype="application/x-www-form-urlencoded"> <input type="hidden" id="prodid" name="prodid" value="0101"> <input type="button" id="miaosha_btn" name="seckill_btn" value="秒杀点我"/> </form> </body> <script type="text/javascript" src="${pageContext.request.contextPath}/script/jquery/jquery-3.1.0.js"></script> <script type="text/javascript"> $(function(){ $("#miaosha_btn").click(function(){ var url=$("#msform").attr("action"); $.post(url,$("#msform").serialize(),function(data){ if(data=="false"){ alert("抢光了" ); $("#miaosha_btn").attr("disabled",true); } } ); }) }) </script> </html>
使用连接池,可以配置redis的连接
public class JedisPoolUtil { private static volatile JedisPool jedisPool = null; private JedisPoolUtil() { } public static JedisPool getJedisPoolInstance() { if (null == jedisPool) { synchronized (JedisPoolUtil.class) { if (null == jedisPool) { JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(200); poolConfig.setMaxIdle(32); poolConfig.setMaxWaitMillis(100*1000); poolConfig.setBlockWhenExhausted(true); poolConfig.setTestOnBorrow(true); // ping PONG jedisPool = new JedisPool(poolConfig, "114.116.246.5", 6379, 60000,"Atcomsn1314"); } } } return jedisPool; } public static void release(Jedis jedis) { if(null != jedis){ jedis.close(); } } }
public class SecKill_redis { public static void main(String[] args) { Jedis jedis =new Jedis("192.168.44.168",6379); System.out.println(jedis.ping()); JedisPoolUtil.release(jedis); } //秒杀过程 public static boolean doSecKill(String uid,String prodid) throws IOException { //1 uid和prodid非空判断 if(uid == null || prodid == null) { return false; } //2 连接redis //Jedis jedis = new Jedis("192.168.44.168",6379); //设置密码 //jedis.auth("123456"); //通过连接池得到jedis对象 JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance(); Jedis jedis = jedisPoolInstance.getResource(); //3 拼接key // 3.1 库存key String kcKey = "sk:"+prodid+":qt"; // 3.2 秒杀成功用户key String userKey = "sk:"+prodid+":user"; //监视库存 jedis.watch(kcKey); //4 获取库存,如果库存null,秒杀还没有开始 String kc = jedis.get(kcKey); if(kc == null) { System.out.println("秒杀还没有开始,请等待"); //释放jedis(close) JedisPoolUtil.release(jedis); return false; } // 5 判断用户是否重复秒杀操作(sismember为判断set中是否存在,返回boolean类型) if(jedis.sismember(userKey, uid)) { System.out.println("已经秒杀成功了,不能重复秒杀"); JedisPoolUtil.release(jedis); return false; } //6 判断如果商品数量,库存数量小于1,秒杀结束 if(Integer.parseInt(kc)<=0) { System.out.println("秒杀已经结束了"); JedisPoolUtil.release(jedis); return false; } //7 秒杀过程 //使用事务 Transaction multi = jedis.multi(); //组队操作 multi.decr(kcKey); multi.sadd(userKey,uid); //执行 List<Object> results = multi.exec(); if(results == null || results.size()==0) { System.out.println("秒杀失败了...."); JedisPoolUtil.release(jedis); return false; } //7.1 库存-1 //jedis.decr(kcKey); //7.2 把秒杀成功用户添加清单里面 //jedis.sadd(userKey,uid); System.out.println("秒杀成功了.."); JedisPoolUtil.release(jedis); return true; } }
public class SecKill_redisByScript { private static final org.slf4j.Logger logger =LoggerFactory.getLogger(SecKill_redisByScript.class) ; public static void main(String[] args) { JedisPool jedispool = JedisPoolUtil.getJedisPoolInstance(); Jedis jedis=jedispool.getResource(); System.out.println(jedis.ping()); Set<HostAndPort> set=new HashSet<HostAndPort>(); // doSecKill("201","sk:0101"); } //获取参数KEYS[i] static String secKillScript ="local userid=KEYS[1];\r\n" + "local prodid=KEYS[2];\r\n" + //..prodid..类似拼接字符串 "local qtkey='sk:'..prodid..\":qt\";\r\n" + "local usersKey='sk:'..prodid..\":usr\";\r\n" + "local userExists=redis.call(\"sismember\",usersKey,userid);\r\n" + //判断是否存在该用户,即已买过 "if tonumber(userExists)==1 then \r\n" + " return 2;\r\n" + "end\r\n" + "local num= redis.call(\"get\" ,qtkey);\r\n" + //判断是否还有库存 "if tonumber(num)<=0 then \r\n" + " return 0;\r\n" + "else \r\n" + " redis.call(\"decr\",qtkey);\r\n" + " redis.call(\"sadd\",usersKey,userid);\r\n" + "end\r\n" + "return 1" ; static String secKillScript2 = "local userExists=redis.call(\"sismember\",\"{sk}:0101:usr\",userid);\r\n" + " return 1"; public static boolean doSecKill(String uid,String prodid) throws IOException { JedisPool jedispool = JedisPoolUtil.getJedisPoolInstance(); Jedis jedis=jedispool.getResource(); //将脚本转换为函数 String sha1= jedis.scriptLoad(secKillScript); //给脚本函数传入参数,调用函数 Object result= jedis.evalsha(sha1, 2, uid,prodid); String reString=String.valueOf(result); if ("0".equals( reString ) ) { System.err.println("已抢空!!"); }else if("1".equals( reString ) ) { System.out.println("抢购成功!!!!"); }else if("2".equals( reString ) ) { System.err.println("该用户已抢过!!"); }else{ System.err.println("抢购异常!!"); } jedis.close(); return true; } }
ubuntu中安装apache ab命令进行简单压力测试
1.安裝ab命令
sudo apt-get install apache2-utils
ab命令需要参数,先编写postfile
vi postfile
编写以下内容
prodid=0101&
按下esc 输入:wq保存退出
ab命令用于压力测试(并发测试)(可以通过花生壳http转发获取公网ip)(http://4q2321204730s7.zicp.vip/Seckill/doseckill为网页使用的post方法)
-n 请求数 -c 请求数中的并发请求数 -T使用post,put请求需设置content-type
ab -n 100 -c 10 -p ~/postfile -T application/x-www-form-urlencoded http://4q2321204730s7.zicp.vip/Seckill/doseckill
原文地址:https://www.cnblogs.com/easyjava/p/15113367.html
- 支持Ajax跨域访问ASP.NET Web Api 2(Cors)的简单示例教程演示
- android 股票K线图
- github搭建个人网站
- Android:一个高效的UI才是一个拉风的UI
- 什么是ORM?为什么用ORM?浅析ORM的使用及利弊
- .NET[C#]中实现实体对象深拷贝(克隆/复制)的几种方法
- Android中图片大小和屏幕密度的关系讲解
- C# WINFORM通过委托和事件窗体间(跨窗体)传值(自定义事件参数)--实例详解
- Apache Spark 2.0预览:机器学习模型持久性
- 推荐一个简单、轻量、功能非常强大的C#/ASP.NET定时任务执行管理器组件–FluentScheduler
- 携程Android App的插件化和动态加载框架
- Spring Boot构建RESTful API与单元测试
- Volley解析之表单提交篇
- JAVA中重写equals()方法的同时要重写hashcode()方法
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- PIMPL:休想窥探我的隐私!
- 树莓派基础实验37:pyserial模块通信实验
- 【答疑解惑】为什么你的 Charles 会抓包失败?
- Mybatis-generator 逆向工程 自定义PO,xml,mapper,example
- 高速上云/网络穿透/视频上云网关EasyNTS组网服务登录状态检测优化记录
- 树莓派基础实验38:逻辑分析仪分析PWM、UART信号
- 【终端设备】视频上云/网络穿透EasyNTS云组网硬件终端无法单独修改账号的优化方式
- 测试环境问题排查的那些事儿
- RTSP流媒体协议视频平台EasyNVR和EasyNTS智能云组网同一浏览器运行为什么会导致EasyNTS无法登陆?
- Java:手写线程安全LRU缓存X探究影响命中率的因素
- 视频上云/网络穿透/网络映射服务EasyNTS设备管理为什么会出现无法搜索到设备的情况?
- 快速打造属于你的接口自动化测试框架
- 大数据下的质量体系建设
- PostgreSQL 日志系统 及 设置错误导致磁盘塞满案例
- 六、乘胜追击,将剩下的Git知识点搞定