简单版:
index.jsp
<%@ 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>
SecKill_redis
package com.xiaoqiu; import redis.clients.jedis.Jedis; import java.io.IOException; public class SecKill_redis { //秒杀过程 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.174.132", 6379); //3 拼接key // 3.1 库存key String kcKey = "sk:" + prodid + ":qt"; // 3.2 秒杀成功用户key String userKey = "sk:" + prodid + ":user"; //4 获取库存,如果库存null,秒杀还没有开始 String kc = jedis.get(kcKey); if (kc == null) { System.out.println("秒杀还没有开始,请等待"); jedis.close(); return false; } // 5 判断用户是否重复秒杀操作 if (jedis.sismember(userKey, uid)) { System.out.println("已经秒杀成功了,不能重复秒杀"); jedis.close(); return false; } //6 判断如果商品数量,库存数量小于1,秒杀结束 if (Integer.parseInt(kc) <= 0) { System.out.println("秒杀已经结束了"); jedis.close(); return false; } //7 秒杀过程 //7.1 库存-1 jedis.decr(kcKey); //7.2 把秒杀成功用户添加清单里面 jedis.sadd(userKey, uid); System.out.println("秒杀成功了.."); jedis.close(); return true; } }
SecKillServlet
package com.xiaoqiu; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Random; /** * 秒杀案例 */ public class SecKillServlet extends HttpServlet { private static final long serialVersionUID = 1L; public SecKillServlet() { super(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String userid = new Random().nextInt(50000) +"" ; String prodid =request.getParameter("prodid"); boolean isSuccess=SecKill_redis.doSecKill(userid,prodid); response.getWriter().print(isSuccess); } }
测试
使用工具ab模拟测试
CentOS6 默认安装
CentOS7需要手动安装
2.1.联网:yum install httpd-tools
2.2.无网络
(1) 进入cd /run/media/root/CentOS 7 x86_64/Packages(路径跟centos6不同)
(2) 顺序安装
apr-1.4.8-3.el7.x86_64.rpm
apr-util-1.5.2-6.el7.x86_64.rpm
httpd-tools-2.4.6-67.el7.centos.x86_64.rpm
2.3.1.通过ab测试
vim postfile 模拟表单提交参数,以&符号结尾;存放当前目录。
内容:prodid=0101&
ab -n 2000 -c 200 -k -p ~/postfile -T application/x-www-form-urlencoded http://172.56.119.21:8080/Seckill/doseckill
2.3.2.超卖
SecKill_redis
package com.atguigu; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.Transaction; import java.io.IOException; import java.util.List; /** * */ public class SecKill_redis { //秒杀过程 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.174.132", 6379); //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(); return false; } // 5 判断用户是否重复秒杀操作 if (jedis.sismember(userKey, uid)) { System.out.println("已经秒杀成功了,不能重复秒杀"); jedis.close(); return false; } //6 判断如果商品数量,库存数量小于1,秒杀结束 if (Integer.parseInt(kc) <= 0) { System.out.println("秒杀已经结束了"); jedis.close(); 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("秒杀失败了...."); jedis.close(); return false; } System.out.println("秒杀成功了.."); jedis.close(); return true; } }
1.连接有限制
增加-r参数,-r Don’t exit on socket receive errors.
ab -n 2000 -c 200 -r -p ~/postfile -T application/x-www-form-urlencoded http://172.56.119.21:8080/Seckill/doseckill
2.已经秒光,可是还有库存
已经秒光,可是还有库存。原因,就是乐观锁导致很多请求都失败。先点的没秒到,后点的可能秒到了。
3.连接超时,通过连接池解决
4.连接池
节省每次连接redis服务带来的消耗,把连接好的实例反复利用。通过参数管理连接的行为。
JedisPoolUtil
package com.xiaoqiu; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; 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, "192.168.174.132", 6379, 60000 ); } } } return jedisPool; } public static void release(JedisPool jedisPool, Jedis jedis) { if (null != jedis) { jedisPool.returnResource(jedis); } } }
SecKill_redis
//2 连接redis //通过连接池得到jedis对象 JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance(); Jedis jedis = jedisPoolInstance.getResource();
package com.xiaoqiu; import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.slf4j.LoggerFactory; import ch.qos.logback.core.joran.conditional.ElseAction; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.ShardedJedisPool; import redis.clients.jedis.Transaction; 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>(); } static String secKillScript ="local userid=KEYS[1];\r\n" + "local prodid=KEYS[2];\r\n" + "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= .secKillScript; 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; } }
SecKillServlet
public class SecKillServlet extends HttpServlet { private static final long serialVersionUID = 1L; public SecKillServlet() { super(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String userid = new Random().nextInt(50000) +"" ; String prodid =request.getParameter("prodid"); boolean isSuccess= SecKill_redisByScript.doSecKill(userid,prodid); response.getWriter().print(isSuccess); } }