2015-03-13 85 views
0

我有一個購物車,我建立了自己的使用會話變量來維護請求狀態的購物車。我有一個增量和減量按鈕,允許用戶增加或減少購物車中產品的數量。這通過Ajax請求發生。購物車類通過在構建時將購物車恢復出會話並在銷燬時將購物車保存回會話中來操作。與php會話和ajax請求的競爭條件

<?php 

class Cart { 

    /** 
    * constructor 
    */ 
    public function __construct(){ 

     //restore cart 
     $this->restore(); 
    } 

    /** 
    * destructor 
    */ 
    public function __destruct(){ 

     //save cart 
     $this->save(); 
    } 

    /** 
    * restore 
    */ 
    public function restore(){ 

     //retrieve session info 
     if(Session::has('cart')){ 

      //get cart 
      $session = Session::get('cart'); 

      //assign session info 
      $this->data = ($session['data']); 
      $this->rates = $session['rates']; 
      $this->lines = $session['lines']; 
     } 
    } 

    /** 
    * save 
    */ 
    public function save(){ 
     Session::put('cart', $this->forSession()); 
    } 

我遇到的問題是具有多個Ajax請求的競爭條件。用戶可以多次點擊該按鈕,發送多個Ajax請求。因此每個請求都會拉動會話的當前狀態,執行操作,然後保存它。問題是以前的交易不一定完成並保存時,它的購物車。我的第一個修正是讓任何後續的Ajax請求取消前一個請求,既減少不必要的(立即覆蓋)請求,也幫助避免這種競爭條件。雖然它似乎有所幫助,但它仍然古怪。所以我的下一個想法就是在源頭上攻擊它,即購物車類本身。我的想法是實施某種類型的「鎖定」,以防止在前一個操作完成之前訪問購物車。這個想法看起來像這樣。

<?php 

/** 
* is cart locked 
*/ 
public function isCartLocked(){ 

    if(Session::get('cartLock') === 1){ 
     sleep(1); 
     $this->isCartLocked(); 
    } 
} 

public function restore(){ 

    Session::put('cartLock', 0); 
    //check if cart is locked 
    $this->isCartLocked(); 

    //lock cart 
    Session::put('cartLock', 1); 

    ... 
} 

public function save(){ 

    //unlock the cart 
    Session::put('cartLock', 0); 

    ... 
} 

現在第一個問題是,我應該做這樣的事情,鎖定?然後,如果是這樣,這是一個體面的方式來處理它?

在我第一次嘗試它之後,我似乎遇到的問題是析構函數不一定總是被調用,這導致我的購物車保持鎖定狀態,並最終導致超時錯誤。

感謝您的幫助!

回答

1

我認爲你實際上想要去掉AJAX請求的函數調用,以便你不關心自己鎖定/解鎖或取消先前的請求 - 只要你確信他們'重新調整數量。

我會推薦this jQuery plugin來限制或反彈JavaScript函數調用(儘管實際上並不需要使用jQuery,但只要通過jQuery名稱空間可用,就可以使用它),假設這就是您提出AJAX請求的方式。

+0

debouncing看起來像是一個很好的解決這個問題。雖然這是我將考慮實現的東西,但我也更喜歡服務器端解決方案,所以我從源頭解決問題,並且不要依賴客戶端解決方案來防止發生這種情況。 – 2015-03-13 19:26:28

0

我不得不說,如果您遇到競爭條件,那麼您在應用程序設計方面存在一些問題,這些問題可能會在一段時間內困擾您的應用程序,因爲會話的併發訪問將被粉碎你的會話存儲比較頻繁

在我看來,最簡單的方法是使用數據庫表,而不是更新字段,而是爲每個用戶事件添加一行(所以增量操作會插入, ,1,並且遞減會插入一行-1)

這樣,您可以簡單地進行求和操作來計數您的計數,並且當用戶chec kout過程完成後,您可以清除購物車表中的整個訂單。

+0

雖然我確信這可以工作,但我個人並不認爲基於數據庫的解決方案適用於像購物車那樣暫時的事情。取決於創建了多少條目,以及用戶基數有多大,所有這些數據庫事務都可能會非常昂貴。感謝您的建議,但我更喜歡基於會話的解決方案。 – 2015-03-13 22:31:27

+0

根據你的會話存儲,你可能會做數據庫寫操作。儘管如果你已經達到了數據庫訪問成爲瓶頸的規模類型,那麼從寫入到會話存儲的所有直接磁盤IO的性能影響也將是顯着的有害的,但我從實際的角度發現了一個臨時表可以是相當在大規模的情況下,您可以考慮在每個負載平衡節點上安裝本地臨時數據庫。 – 2015-03-13 23:18:11

+0

目前我正在使用本地會話,雖然與Laravel,可以很容易地切換到數據庫或餅乾。有趣的想法。我希望有人可以使用基於會話的解決方案, – 2015-03-14 00:55:07