2018.10.24 今天研究了下搶購秒殺的功能實現
網上查了一大堆 用redis的最多。
主要是通過redis的 watch multi 事務來控制秒殺數量 不超賣。
這里說下自己的感受:
不超賣的話 那就要一個個的來減庫存 這樣的話 效率上會有點問題 這里上下代碼 基本上是再網上抄的 。
我用的是 springboot jedis
我就直接上代碼了
Controller層
package com.bicon.basedemo.controller; import java.util.Arrays; import java.util.List; import java.util.Random; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.annotation.Resource; import org.omg.CORBA.PRIVATE_MEMBER; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; @RestController @RequestMapping("/test") public class test { // @Resource // RedisOperation redisOps; @Resource private JedisPool jedisPool; @RequestMapping("/redis") public void redisTest() { Jedis jedis = jedisPool.getResource(); final String watchkeys = "watchkeys"; ExecutorService executor = Executors.newFixedThreadPool(20); //20個線程池並發數 jedis.set(watchkeys, "10");//設置起始的搶購數 // jedis.del("setsucc", "setfail"); jedis.close(); for (int i = 0; i < 101; i++) {//設置101個人來發起搶購 模擬101個人搶購 executor.execute(new MyRunnable(jedisPool)); } 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 代碼
package com.bicon.basedemo.controller; import java.util.List; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.Transaction; public class MyRunnable implements Runnable{ private JedisPool jedisPool; String userinfo; String watchkeys = "watchkeys"; public MyRunnable(JedisPool jedisPoo){ jedisPool = jedisPoo; }; public void run() { Jedis jedis = jedisPool.getResource(); 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) { System.out.println("重新搶購"); this.run(); return; } else { for(Object succ : list){ String succuserifo ="succ"+succ.toString() +userinfo ; String succinfo="用戶:" + succuserifo + "搶購成功,當前搶購成功人數:" + (1-(valint-10)); System.out.println(succinfo); /* 搶購成功業務邏輯 */ jedis.setnx(succuserifo, succinfo); } } } else { String failuserifo ="kcfail" + userinfo; String failinfo1="用戶:" + failuserifo + "商品被搶購完畢,搶購失敗"; System.out.println(failinfo1); jedis.setnx(failuserifo, failinfo1); return; } } catch (Exception e) { e.printStackTrace(); } finally { jedis.close(); } } }
最后是效果
這段代碼問題其實還是有的:就是沒有按照順來來搶購
其實我覺得有種方法。就是將請求 存入 kafka中
然后取kafka中前面的數據 一直取到搶購的數量(用戶不重復)
這樣不就可以了嗎,不需要考慮超賣問題啥的。純屬自己的感想。
后來看了一個用rabbitMQ 做的 搶購
把請求插入rabbitMQ隊列。然后 消費端訂閱數據 來實現搶購。
2018-11-21 今天在github上看到一個秒殺的項目 還不錯 分享給大家
https://github.com/hfbin/Seckill
這個里面有兩個 分支,第二個分支是支持rabbitmq的。我覺得 做的還不完美。不過很有借鑒意義。