Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

$_SESSION未及时更新 #26

Closed
nameldk opened this issue May 10, 2017 · 3 comments
Closed

$_SESSION未及时更新 #26

nameldk opened this issue May 10, 2017 · 3 comments

Comments

@nameldk
Copy link

nameldk commented May 10, 2017

是在直播间进出房间的场景,用户进入直播间A,我在当前用户的session里记录A $_SESSION['rooms']['A']=1,用户退出A时,我把A从session里unset掉 unset($_SESSION['rooms']['A'])。现在是网页里用过websocket建立连接,用户关闭页面时用户退出A,随即网页关闭,websocket连接断开了。
问题:我会在后端socket断开连接的回调里检测用户是否还有未关闭的房间,有的话会把用户退出房间,可能是 退出房间和socket连接间隔太短了,退出房间时session更新了,但回调时这时session里的A并没清除,从而导致用户会再退出A一次。
Workerman version:3.3.2
GatewayWorker master分支
PHP version:5.6.2
CentOS 7

@walkor
Copy link
Owner

walkor commented May 10, 2017

session的存取是异步的,所以有一点点延迟(大概小于1毫秒左右)。如果请求过快,可能会造成A请求改变了session,但是B请求读到的仍然是旧的session。为了解决这个问题,可以用Gateway::getSession 和 Gateway::setSession 接口来存取session。

但是onClose里无法调用Gateway::getSession 和 Gateway::setSession接口,因为此时链接已经关闭了。
我想这里的解决办法是在内存中添加一个全局数组,里面记录所有client_id的session快照,如果快照本存在,就用最新的快照。

class Events
{
    // ['client_id1'=>session_array, 'client_id2'=>session_array, ...]
    public static $sessions = array();
    public static function onMessage($client_id)
    {
        // 如果内存中有对应client_id的session快照数据则使用内存中最新的数据
        $_SESSION = isset(self::$sessions[$client_id]) ? self::$sessions[$client_id] : $_SESSION;
        // 各种操作$_SESSION
        $_SESSION['xxx'] = xx;
        unset($_SESSION['rooms']['A']);
      
       // 操作完session记得同步一份给self::$sessions作为快照,保证self::$sessions里的数据是最新的
        self::$sessions[$client_id] = $_SESSION;
    }

    public function onClose($client_id) 
    {
         // 如果内存中有对应client_id的session数据则使用内存中最新的数据
        $_SESSION = isset(self::$sessions[$client_id]) ? self::$sessions[$client_id] : $_SESSION;
        // 业务逻辑
        // ....
        // 链接断开了,最后记得删除快照,避免内存泄漏
        unset(self::$sessions[$client_id]);      
    }
}

注意:这个快照要求gateway路由规则是将某个client_id的请求路由给固定worker进程(默认路由就是这样)。
如果你自定义了路由规则需要小心,某个client_id的session数据可能分散在各个worker进程的self::$sessions里,那么这个方法就不会奏效。

@nameldk
Copy link
Author

nameldk commented May 10, 2017

了解了,感谢!

  1. 如果用第三方组件比如redis来存用户的session数据,就不存在延迟问题了吧?
  2. 如果用redis来存储session,性能是不是没有用直接用$_SESSION的好?
  3. client_id 是全局唯一的吧?

@walkor
Copy link
Owner

walkor commented May 10, 2017

1、对
2、性能上一般感觉不到差别
3、对

@nameldk nameldk closed this as completed May 10, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants