2015-03-13 29 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