session更換存儲,實如今多臺服務器共享

場景

web服務器有多臺,每臺服務器都會存貯本身的session,session沒法在多臺服務器共享。因此就須要更換session的存貯空間,存貯在一個共用的空間。一般爲了讀寫速度,咱們會選擇存貯在內存服務上,如redis、mysql的memory存貯引擎等,本文以reddis存貯貫串上下文。php

session共享

nginx ip_hash 或者 url_hash

ip_hash

...
upstream backserver {
    ip_hash;
    server 192.168.0.14:88;
    server 192.168.0.15:80;
}
...

採用ip_hash指令解決這個問題,若是客戶已經訪問了某個服務器,當用戶再次訪問時,會將該請求經過哈希算法,自動定位到該服務器。
每一個請求按訪問ip的hash結果分配,這樣每一個訪客固定訪問一個後端服務器,解決session的問題。mysql

url_hash

按訪問url的hash結果來分配請求,使每一個url定向到同一個(對應的)後端服務器,後端服務器爲緩存時比較有效。nginx

...
upstream backserver {
    server squid1:3128;
    server squid2:3128;
    hash $request_uri;
    hash_method crc32;
}
...

php.ini修改配置

session.save_handler = redis
session.save_path = "tcp://127.0.0.1:6379"

修改完以後,重啓 php-fpm。git

經過 ini_set() 函數設置

ini_set("session.save_handler", "redis");
ini_set("session.save_path", "tcp://127.0.0.1:6379");

session_set_save_handler()函數設置

主流框架都選擇用此類方法覆蓋session的open、close、read、write、gc機制,容易擴展。我寫了個redis的session存貯類,僅供參考:github

<?php
//namespace openyii\framework;

class CSessionRedis //extends CSession
{
    protected $redisInstance;   //redis操做實例
    public $keyPrefix;          //鍵前綴
    public $lifeTime;           //生命週期

    public function __construct( $keyPrefix,$lifeTime=3600 )
    {
        $this->lifeTime = $lifeTime;
        $this->keyPrefix = $keyPrefix;
        session_set_save_handler(
            array($this, 'open'),
            array($this, 'close'),
            array($this, 'read'),
            array($this, 'write'),
            array($this, 'destroy'),
            array($this, 'gc')
        );
        if ($this->keyPrefix === null) {
            $this->keyPrefix = substr(md5(base::$app->id), 0, 5);
        }
        session_start();            //以全局變量形式保存在一個session中而且會生成一個惟一的session_id,
    }

    function open($savePath, $sessionName)
    {
        //todo $redis redis的操做實例要本身寫下

          $this->redisInstance = $redis;
//        $this->redisInstance = base::$app->redis;
        return true;
    }

    function close()
    {
        return true;
    }

    function read($id)
    {
        if( !$this->redisInstance->exists($this->calculateKey($id)) ){
            $this->redisInstance->set( $this->calculateKey($id),'' );
        }
        return $this->redisInstance->get( $this->calculateKey($id) );
    }

    function write($id, $value)
    {
        return $this->redisInstance->set($this->calculateKey($id), $value, $this->lifeTime)?true:false;
    }

    function destroy($id)
    {
        $this->redisInstance->delete($this->calculateKey($id));
        if( !$this->redisInstance->exists($this->calculateKey($id)) ){
            return false;
        }
        return true;
    }

    function gc($lifetime)
    {
        //不作實現,redis自己有回收機制

        return true;
    }

    /**
     * 加密key
     * @param $id
     * @return string
     */
    protected function calculateKey($id)
    {
        return $this->keyPrefix . md5(json_encode([__CLASS__, $id]));
    }

}

具體細節能夠看我寫的php框架代碼,若是想本身實現,上面的代碼改改就能夠實現的。web

思考

若是使用mysql如何存貯?

mysql使用memory存貯引擎,設計數據表(sessionid、sessionValue 、過時時間),重寫session_set_save_handler方法。redis

若是redis是哨兵 master-slave模式,session如何共享?

沒想清楚,諮詢了老大,作法比較坑,php.ini修改配置指定redis master ip,有個守護進程的腳本檢測哨兵,發生故障哨兵切換,檢測到發郵件通知‘人’去手動修改php.ini master的ip,感受匪夷所思啊!算法

個人思路是:哨兵模式會有三個redis服務ip端口,客戶端鏈接獲取每一個redis服務的info信息,若是info裏包含master role,則將此master redis服務操做實例交給上面的代碼,實現寫操做。sql

後面本身動手實現了吧,可行的,代碼地址貼下,結合代碼及說明看看,但願能幫助你們理解:shell

哨兵主從模式(redis服務ip可直接訪問)

哨兵主從模式(redis服務ip不可直接訪問,ip映射訪問)

相關文章
相關標籤/搜索