为什么要用session共享?

当我们的网站用户访问并发性越来越高时,我们就会用到反代理和服务器集群
但是就会出现一个问题  
当用户访问时用户登录时存储的session是存放在服务器的文件中,
但其他服务器没有该用户的session登录信息,那么在理论上用户还得登录一次
当然我们不会允许这种情况发生。
对比啦几种解决方案,决定使用如下解决方案的原理demo:
<?php
#设置session自定义储存
    session_set_save_handler(
        'open',
        'close',
        'read',
        'write',
        'destroy',
        'gc'
    );

    /**
 * 连接redis
 * @return Redis
 */
    function redisInit(){
         $redis_obj=new Redis();
         $redis_obj->open('127.0.0.1','6379');
            return $redis_obj;
    }


    /**
 * open回调的工作方式类似于类中的构造函数,并在打开会话时执行。这是在会话自动启动或使用session_start()
 * 手动启动时执行的第一个回调函数。返回值表示TRUE成功,FALSE表示失败。
 */
    #开启session的执行方法
    function open(){
        var_dump(__METHOD__);
        echo "<hr>";
        return true;
    }

    /**
 * close回调的工作方式类似于类中的析构函数,并在调用会话写回调后执行。调用session_write_close()
 * 时也会调用它 。返回值应该是TRUE成功,否则FALSE就是失败。
 * @return bool
 */
    #关闭session的执行方法
    function close(){
        var_dump(__METHOD__);
        echo "<hr>";
        return  true;
    }

    /**
     * #该read如果没有数据读取回调必须始终返回会话编码(连载)字符串,或空字符串。
     *#当会话启动或调用session_start()时,PHP将在内部调用此回调。在调用此回调之前,PHP将调用该open回调。
     *#此回调返回的值必须与最初传递给write回调存储的完全相同的序列化格式。返回的值将由PHP自动反序列化,
     *#并用于填充$ _SESSION超全局变量。
     *#虽然数据看起来与serialize()相似,但是请注意,session.serialize_handler ini设置中指定的格式是不同的。
     * @param $sid
     * @return bool|string
     */
    #读取session的执行方法  必须返回序列化的内容
    function read($sid){
        var_dump(__METHOD__);
//        return serialize([]);
        echo "<hr>";
        $redis_key='redis_session:'.$sid;
        $redis=redisInit();
        $redis_data=$redis->get($redis_key);
//        var_dump($redis_data);
        if(empty($redis_data)){
         return serialize([]);
        }else{
            #每次访问是更新过期时间
            $redis->expire($redis_key,1400);
            return $redis_data;
        }
    }

    /**
     * #write(string $sessionId, string $data)
     * #write当需要保存和关闭会话时, 将调用回调。此回调收到当前会话ID的序列化版本$ _SESSION超全局变量。
     *#在session.serialize_handler ini设置中指定了PHP内部使用的序列化方法。
     *#传递给该回调的序列化会话数据应根据传递的会话ID存储。检索此数据时,read回调必须返回最初传递给write回调的确切值。
     *#当PHP关闭时调用此回调,或者在调用session_write_close()时显式 调用。请注意,执行此函数后,PHP将在内部执行close回调。
     * @param $sid
     * @param $value
     * @return bool
     */
    #写入redis数据的执行方法
   function write($sid,$value){
       var_dump(__METHOD__);
       echo "<hr>";
//       var_dump(func_get_args());
       $redis=redisInit();
       if($redis->set('redis_session:'.$sid,$value)){
           $redis->expire('redis_session:'.$sid,1440);
             return true;
       }else{
           return false;
       }
   }

    /**
     * #destroy($sessionId)
     *#当使用session_destroy()或 session_regenerate_id()
     *(销毁参数设置为)销毁会话时,将执行此回调TRUE。返回值应该是TRUE成功,否则FALSE就是失败。
     * @param $sid
     * @return bool
    */
    #销毁sessio时执行的方法
    function destroy($sid){

        var_dump(__METHOD__);
        echo "<hr>";
        $redis_key='redis_session:'.$sid;
        $redis=redisInit();
        if($redis->del($redis_key)){
            return true;
        }else{
            return false;
        }
    }

    /**
 *#gc($lifetime)
 *#PHP定期在内部调用垃圾回收器回调,以清除旧的会话数据。频率由session.gc_probability和session.gc_divisor控制 。
 *# 可以在session.gc_maxlifetime中设置传递给此回调的有效期值。返回值应该是TRUE成功,否则FALSE就是失败。
 * @return bool
 */
    function gc(){
        var_dump(__METHOD__);
        return true;
    }

#开启session
session_start();
//$_SESSION['user_name']='scl';
//session_destroy();
//echo "<hr>";
//var_dump($_SESSION);
demo 实现原理:
当我们存取session时调用会调用 open--> read-->write-->close。
当我们销毁session值时 open-->read-->distroy-->close。
gc方法   会有1000/1 的概率  清理session过期文件,
执行成功返回true 否则换回false。

该demo使用第一种原型:

function session_set_save_handler ($open, $close, $read, $write, $destroy, $gc) {}

另一种原型 SessionHandlerInterface 链接如下: