linux 系统:使用yum安装
1、安装ab工具用于测试高并发
命令: yum install httpd-tools
2、ab --help 查看ab命令的参数
3、常用参数说明
-n 请求数量
-c 并发数量
-p postfile 可以将post请求中的参数封装在postfile文件里面,
以key=value&形式保存,注意需要&结尾,使用时,使用-p 文件名(postfile)该文件需要自己创建,该参数的使用需要制定-T,一起使用 -T 文件的类型 'application/x-www-form-urlencoded'
postfile文件: key1=value1&key2=value2&
如下模拟1000个请求10个并发访问某个servlet程序,使用redis存储数据,模拟抢购案例。
[root@localhost bin]# ab -n 100 -c 10 -p /opt/postfile -T 'application/x-www-form-urlencoded' http://192.168.2.150:8080/redis-test3/doSeckill
代码:
@WebServlet("/doseckill")
public class SecKillServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String userid = new Random().nextInt(50000) +"" ;
String prodid =req.getParameter("prodid");
boolean if_success=SecKill_ByRedis.doSecKill(userid,prodid);
// boolean if_success=SecKillByLua.doSecKill(userid,prodid);
resp.getWriter().print(if_success);
}
}
调用业务方法:
public class SecKill_ByRedis {
public static boolean doSecKill(String userid, String prodid) {
//1.生成key
//商品的库存key
String qtKey = "sk:"+prodid+":qt";
//秒杀到的用户数量key
String uidKey = "sk:"+prodid+":usr";
//Jedis jedis = new Jedis("192.168.151.250", 6379);
JedisPool jedisPool = JedisPoolUtils.getInstanceJedisPool();
Jedis jedis = jedisPool.getResource();
//判断这个用户是否已经秒杀到了
if(jedis.sismember(uidKey, userid)){
System.out.println("你已经秒到,不能重复秒杀!");
jedis.close();
return false;
}
//这个位置没有加锁,导致并发时出问题
//增加乐观锁事物
jedis.watch(qtKey);
String qtStr = jedis.get(qtKey);
//判断库存是否足够
if(qtStr == null) {
System.out.println("没有初始化");
jedis.close();
return false;
}else {
int qt= Integer.parseInt(qtStr);
if(qt <= 0 ) {
System.out.println("已经抢光了....");
jedis.close();
return false;
}
}
//组队,之后序马上执行
Transaction transaction = jedis.multi();
//减库存
transaction.decr(qtKey);
//加人
transaction.sadd(uidKey, userid);
//执行队列中的事物
List<Object> exec = transaction.exec();
if(exec==null ||exec.size()==0) {
System.out.println(userid + "秒杀失败....");
jedis.close();
return false;
}
System.out.println(userid + "秒杀成功....");
jedis.close();
return true;
}
}
使用单例模式获取线程池对象
public class JedisPoolUtils {
private static volatile JedisPool jedisPool = null;
private JedisPoolUtils() {
}
public static JedisPool getInstanceJedisPool() {
if(jedisPool == null) {
synchronized (JedisPoolUtils.class) {
if(jedisPool == null) {
JedisPoolConfig poolConfig = new JedisPoolConfig();
//最大的连接数
poolConfig.setMaxTotal(200);
//空闲时的连接数
poolConfig.setMaxIdle(32);
//最大的等待时间
poolConfig.setMaxWaitMillis(1000*1000);
//当没有连接的时间阻塞
poolConfig.setBlockWhenExhausted(true);
//保证获取的连接可以使用
poolConfig.setTestOnBorrow(true);
jedisPool = new JedisPool(poolConfig, "192.168.151.250", 6379);
}
}
}
return jedisPool;
}
}
秒杀不完问题的解决方案:使用lua脚本,Redis版本需要2.6及以上才可以 secndKill.lua脚本文件内容
local userid=KEYS[1];
local prodid=KEYS[2];
local qtkey="sk:"..prodid..":qt";
local usersKey="sk:"..prodid..":usr';
local userExists=redis.call("sismember",usersKey,userid);
if tonumber(userExists)==1 then
return 2;
end
local num= redis.call("get" ,qtkey);
if tonumber(num)<=0 then
return 0;
else
redis.call("decr",qtkey);
redis.call("sadd",usersKey,userid);
end
return 1;
java代码:
public class SecKillByLua {
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 userid, String prodid) {
JedisPool jedisPool = JedisPoolUtils.getInstanceJedisPool();
Jedis jedis = jedisPool.getResource();
String sha1 = jedis.scriptLoad(secKillScript);
Object result = jedis.evalsha(sha1, 2, userid,prodid);
String reString = String.valueOf(result);
if("0".equals(reString)) {
System.out.println("已经抢空了!!");
}else if("1".equals(reString)) {
System.out.println("抢购成功!!");
}else if("2".equals(reString)) {
System.out.println("该用户已经抢过!!!");
}else {
System.out.println("抢购异常!!!");
}
jedis.close();
return true;
}
}
前端页面:
<body>
<h1>iPhoneX !!! 1元秒杀!!!</h1>
<form id="msform" action="${pageContext.request.contextPath}/doseckill">
<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>