问题重现

在我们的项目中,有一个商店购买的页面,流程是这样的:  选择道具  ----->  点击购买   -------> 获得道具 ------> 扣除银币

如果玩家在一秒钟并发的请求这个URL,  如果他有100个银币,只可以买10个道具,由于并发的问题,他还可以买100个道具,到时候,他的银币的数量可能扣除为负数。这样,对游戏来说,是非常严重的问题。

并发的原理是这样的: 当玩家第一次购买道具的的时候,读取玩家的余额,如果大于道具的价格,系统把道具给玩家,然后在扣除玩家的余额。在我我把道具给了玩家过程中,由于大量的请求过来,还没有来得及把玩家的钱扣除,所以,导致了玩家买到了更多的东西

问题所在

如果扣“除银币”在“获得道具”之前完成,就基本上可以避免了这种问题,但是,由于我们道具的特殊设定,不能先扣除银币,所以不能这样做

这样,就只能避免玩家重复的提交同一个买道具的URL,产生了大量的并发。方法是,URL上面加一个formHash,每次都生成一个新的formHash.那么就防止了页面多次提交了。

 

formHash的生成代码

formHash保存在memcache中

    // 生成新的 formHash
    public function refreshFormHash($formName)
    {
        // 生成随机码
        $formHash = md5('VoyageMobile:' . date('md') . ':' . mt_rand() . ':' . $formName);

        $cacheObj = Com_Cache::getInstance('Memcache');
        $cacheKey = 'formHash:' . $formName;

        // 将 formHash 写入到 Memcache 中
        $cacheObj->set($cacheKey, $formHash);

        // 传出到模板中
        $this->assign('formHash', $formHash);

        return $formHash;
    }

  

formHash的验证

    /**
     * 验证 formHash 是否正确
     *
     * @param string $formName
     * @param mixed $formHashGet
     * @param bool $clear 是否取完即清除
     * @return bool
     */
    public function validFormHash($formName, $formHashGet = null, $clear = true)
    {
      // 页面中传过来的formHash if ($formHashGet === null) { $formHashGet = $this->getx('formHash'); } $cacheObj = Com_Cache::getInstance('Memcache'); $cacheKey = 'formHash:' . $formName; $formHash = $cacheObj->get($cacheKey); // 取完即清除(formHash只能用一次) $clear && $cacheObj->delete($cacheKey); return $formHash == $formHashGet ? true : false; }

  

formHash的使用

在道具购买的首页中,系统生成一个formHash.

public function shopIndex()
{
  // 生成道具的列表 

  // 生成formHash 
  $this->refreshFormHash();  
    
}

  

formHash的验证

在购买道具的函数中,必须验证formHash,而且,必须生成新的formHash,并分配到页面中

    /**
     * 购买提交
     */
    public function purchaseAction()
    {
        // 验证请求合法性
        if (! $this->validFormHash('bmItem')) {
            $this->vRedirect('/main/?err=Invalid_Request_FormHash');
        }

        // 重新刷新 formHash
       $this->refreshFormHash('bmItem');

        //  购买流程
}

  

 

   注意问题

   某些页面是ajax请求,不刷新页面,这时候把新生成的formHash 动态的在页面中生成。

 

posted on 2013-06-25 14:51  孙力  阅读(7434)  评论(0编辑  收藏  举报