乡下人产国偷v产偷v自拍,国产午夜片在线观看,婷婷成人亚洲综合国产麻豆,久久综合给合久久狠狠狠9

  • <output id="e9wm2"></output>
    <s id="e9wm2"><nobr id="e9wm2"><ins id="e9wm2"></ins></nobr></s>

    • 分享

      高并發(fā)下的下單功能設計

       xujin3 2017-10-21

      功能需求:設計一個秒殺系統(tǒng)


      初始方案


      商品表設計:熱銷商品提供給用戶秒殺,有初始庫存。


      @Entity
      public class SecKillGoods implements Serializable{
          @Id
          private String id;

          /**
           * 剩余庫存
           */
          private Integer remainNum;

          /**
           * 秒殺商品名稱
           */
          private String goodsName;
      }


      秒殺訂單表設計:記錄秒殺成功的訂單情況


      @Entity
      public class SecKillOrder implements Serializable {
          @Id
          @GenericGenerator(name = 'PKUUID', strategy = 'uuid2')
          @GeneratedValue(generator = 'PKUUID')
          @Column(length = 36)
          private String id;

          //用戶名稱
          private String consumer;

          //秒殺產品編號
          private String goodsId;

          //購買數量
          private Integer num;
      }


      Dao設計:主要就是一個減少庫存方法,其他CRUD使用JPA自帶的方法


      public interface SecKillGoodsDao extends JpaRepository{

          @Query('update SecKillGoods g set g.remainNum = g.remainNum - ?2 where g.id=?1')
          @Modifying(clearAutomatically = true)
          @Transactional
          int reduceStock(String id,Integer remainNum);

      }


      數據初始化以及提供保存訂單的操作:


      @Service
      public class SecKillService {

          @Autowired
          SecKillGoodsDao secKillGoodsDao;

          @Autowired
          SecKillOrderDao secKillOrderDao;

          /**
           * 程序啟動時:
           * 初始化秒殺商品,清空訂單數據
           */
          @PostConstruct
          public void initSecKillEntity(){
              secKillGoodsDao.deleteAll();
              secKillOrderDao.deleteAll();
              SecKillGoods secKillGoods = new SecKillGoods();
              secKillGoods.setId('123456');
              secKillGoods.setGoodsName('秒殺產品');
              secKillGoods.setRemainNum(10);
              secKillGoodsDao.save(secKillGoods);
          }

          /**
           * 購買成功,保存訂單
           * @param consumer
           * @param goodsId
           * @param num
           */
          public void generateOrder(String consumer, String goodsId, Integer num) {
              secKillOrderDao.save(new SecKillOrder(consumer,goodsId,num));
          }
      }


      下面就是controller層的設計


      @Controller
      public class SecKillController {

          @Autowired
          SecKillGoodsDao secKillGoodsDao;
          @Autowired
          SecKillService secKillService;

          /**
           * 普通寫法
           * @param consumer
           * @param goodsId
           * @return
           */
          @RequestMapping('/seckill.html')
          @ResponseBody
          public String SecKill(String consumer,String goodsId,Integer num) throws InterruptedException {
              //查找出用戶要買的商品
              SecKillGoods goods = secKillGoodsDao.findOne(goodsId);
              //如果有這么多庫存
              if(goods.getRemainNum()>=num){
                  //模擬網絡延時
                  Thread.sleep(1000);
                  //先減去庫存
                  secKillGoodsDao.reduceStock(num);
                  //保存訂單
                  secKillService.generateOrder(consumer,goodsId,num);
                  return '購買成功';
              }
              return '購買失敗,庫存不足';
          }

      }


      上面是全部的基礎準備,下面使用一個單元測試方法,模擬高并發(fā)下,很多人來購買同一個熱門商品的情況。


      @Controller
      public class SecKillSimulationOpController {

          final String takeOrderUrl = 'http://127.0.0.1:8080/seckill.html';

          /**
           * 模擬并發(fā)下單
           */
          @RequestMapping('/simulationCocurrentTakeOrder')
          @ResponseBody
          public String simulationCocurrentTakeOrder() {
              //httpClient工廠
              final SimpleClientHttpRequestFactory httpRequestFactory = new SimpleClientHttpRequestFactory();
              //開50個線程模擬并發(fā)秒殺下單
              for (int i = 0; i < 50;="" i++)="">
                  //購買人姓名
                  final String consumerName = 'consumer' + i;
                  new Thread(new Runnable() {
                      @Override
                      public void run() {
                          ClientHttpRequest request = null;
                          try {
                              URI uri = new URI(takeOrderUrl + '?consumer=consumer' + consumerName + '&goodsId=123456&num=1');
                              request = httpRequestFactory.createRequest(uri, HttpMethod.POST);
                              InputStream body = request.execute().getBody();
                              BufferedReader br = new BufferedReader(new InputStreamReader(body));
                              String line = '';
                              String result = '';
                              while ((line = br.readLine()) != null) {
                                  result += line;//獲得頁面內容或返回內容
                              }
                              System.out.println(consumerName+':'+result);
                          } catch (Exception e) {
                              e.printStackTrace();
                          }
                      }
                  }).start();
              }
              return 'simulationCocurrentTakeOrder';
          }

      }


      訪問localhost:8080/simulationCocurrentTakeOrder,就可以測試了


      預期情況:因為我們只對秒殺商品(123456)初始化了10件,理想情況當然是庫存減少到0,訂單表也只有10條記錄。


      實際情況:訂單表記錄



      商品表記錄




      下面分析一下為啥會出現超庫存的情況: 


      因為多個請求訪問,僅僅是使用dao查詢了一次數據庫有沒有庫存,但是比較惡劣的情況是很多人都查到了有庫存,這個時候因為程序處理的延遲,沒有及時的減少庫存,那就出現了臟讀。如何在設計上避免呢?最笨的方法是對SecKillControllerseckill方法做同步,每次只有一個人能下單。但是太影響性能了,下單變成了同步操作。


       @RequestMapping('/seckill.html')
       @ResponseBody
       public synchronized String SecKill


      改進方案


      根據多線程編程的規(guī)范,提倡對共享資源加鎖,在最有可能出現并發(fā)爭搶的情況下加同步塊的思想。應該同一時刻只有一個線程去減少庫存。但是這里給出一個最好的方案,就是利用Oracle,Mysql的行級鎖–同一時間只有一個線程能夠操作同一行記錄,對SecKillGoodsDao進行改造:


      public interface SecKillGoodsDao extends JpaRepository{

          @Query('update SecKillGoods g set g.remainNum = g.remainNum - ?2 where g.id=?1 and g.remainNum>0')
          @Modifying(clearAutomatically = true)
          @Transactional
          int reduceStock(String id,Integer remainNum);

      }


      僅僅是加了一個and,卻造成了很大的改變,返回int值代表的是影響的行數,對應到controller做出相應的判斷。


      @RequestMapping('/seckill.html')
          @ResponseBody
          public String SecKill(String consumer,String goodsId,Integer num) throws InterruptedException {
              //查找出用戶要買的商品
              SecKillGoods goods = secKillGoodsDao.findOne(goodsId);
              //如果有這么多庫存
              if(goods.getRemainNum()>=num){
                  //模擬網絡延時
                  Thread.sleep(1000);
                  if(goods.getRemainNum()>0) {
                      //先減去庫存
                      int i = secKillGoodsDao.reduceStock(goodsId, num);
                      if(i!=0) {
                          //保存訂單
                          secKillService.generateOrder(consumer, goodsId, num);
                          return '購買成功';
                      }else{
                          return '購買失敗,庫存不足';
                      }
                  }else {
                      return '購買失敗,庫存不足';
                  }
              }
              return '購買失敗,庫存不足';
          }


      在看看運行情況 



      訂單表:



      在高并發(fā)問題下的秒殺情況,即使存在網絡延時,也得到了保障。


      出處:http://blog.csdn.net/u013815546/article/details/53928912


        本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
        轉藏 分享 獻花(0

        0條評論

        發(fā)表

        請遵守用戶 評論公約

        類似文章 更多