簡體   English   中英

PHP & Sessions:有什么辦法可以禁用 PHP 會話鎖定?

[英]PHP & Sessions: Is there any way to disable PHP session locking?

有沒有辦法在使用默認會話處理程序時禁用 PHP 中的會話鎖定?

[編輯:] 或者至少有一種方法可以在調用session_write_close()后重新啟動會話? 如果任何輸出已發送到瀏覽器,則session_start()不起作用。

你不想禁用它......如果你這樣做,你可能會遇到各種奇怪的問題,你在一個窗口登錄,在另一個窗口注銷,然后最終處於不一致的狀態......鎖定是有原因的......

相反,如果您知道不會在該請求中寫入會話,請盡早關閉會話。 啟動它后,即使在調用session_write_close之后,您也可以從它讀取整個請求(除非您重新啟動它,或者做一些其他特殊的事情)。 因此,您要做的是檢查請求以查看它是否是寫入請求,如果不是,則在打開它后立即關閉它。 請注意,如果您稍后嘗試寫入會話(用於驗證碼或 CSRF 保護或其他內容),這可能會產生一些不利影響,因此請謹慎使用...

但是我不會試圖繞過它,而是努力縮短請求長度(以減少鎖爭用),或者為那些根本不需要會話的請求發出 cookieless 請求......

我有一個報告頁面,耗時超過 2 分鍾(超過 80 個 AJAX 請求)。 我通過刪除會話鎖定將其縮短到 30 秒以下。 是的,天堂禁止你刪除文件鎖定,因為那樣你就有了競爭條件。 如果您不了解競態條件並且您不知道這會對您的會話產生什么影響……那么請不要刪除文件鎖定。 但是,如果您知道會話中的數據以及競爭條件的作用,並認為沒有數據會受到競爭條件的不利影響,從而產生錯誤...其他人都這樣做,所以去吧。

MySQL、Redis 和 Memcache

另外,請注意,如果您切換到 MySQL 進行會話管理,恕我直言,您有 99% 的機會不會從讀取到寫入的時間鎖定該行。 因此,使用 MySQL 時,您仍然會遇到相同的競爭條件(如果您決定鎖定該行,則會出現鎖定問題)。

根據我能找到的信息,使用 PHP Redis 的人正在使用一個容易出現競爭條件的非鎖定應用程序......根據以下線程......他們將速度作為他們喜歡這個的原因之一“特征”:

https://github.com/phpredis/phpredis/issues/37

Memcached 直到 3.0.4 版本才支持會話鎖定......所以它也——最初——容易出現競爭條件。

顯然,隨着這些選項的成功,競爭條件並不是程序員面臨的最大問題。

最終問題是

除非您進行文件鎖定,否則所有並發請求將始終受到競爭條件的影響,此時它們不再是並發請求。

關於會話和鎖定與並發和競爭條件的重要事情是了解您的應用程序,了解競爭條件是否會破壞您的應用程序......並設計適合您的應用程序的解決方案。 如果您所做的只是將 userId 存儲在會話中並在所有后續請求中讀取它,那么您可能不需要擔心競爭條件。 如果您存儲了詳細的數據歷史記錄,如果發生亂序或數據丟失,這些歷史記錄將會中斷,然后在讀取和寫入之間鎖定文件,並嘗試在讀取后盡快進行寫入以限制時間量文件被鎖定。

最佳選擇

然而,無會話 API 更適合並發請求。 但是,如果您沒有時間重構為這樣的 API……那么請繼續閱讀。

繼續使用 PHP 會話文件並停止鎖定的權宜之計解決方案

要繼續以默認方式使用 PHP 會話,停止鎖定,並且基本上可以非常快速地解決復雜問題,您可以實現 PHP 網站的 SessionHandler 示例實現。

我在一個每分鍾有數萬個連接的站點的生產環境中運行了以下代碼,我還沒有遇到任何競爭條件問題,但我也不存儲競爭條件可能破壞的數據. 正如我所說,這段代碼得到了一份報告,從超過 2 分鍾縮短到不到 30 秒……並且花了幾分鍾來實現。 無需創建 MySQL 架構,無需安裝 Memcache 或 Redis。

順便說一下,這是 PHP 文檔 ( http://php.net/manual/en/class.sessionhandlerinterface.php ) 中提供的示例實現,它在讀取會話文件時不會鎖定它。

注意正如 Anther 在此評論中指出的那樣,除非您將文件保存到單個服務器,否則這在分布式系統中將不起作用。

<?php
class MySessionHandler implements SessionHandlerInterface
{
    private $savePath;

    public function open($savePath, $sessionName)
    {
        $this->savePath = $savePath;
        if (!is_dir($this->savePath)) {
            mkdir($this->savePath, 0777);
        }

        return true;
    }

    public function close()
    {
        return true;
    }

    public function read($id)
    {
        return (string)@file_get_contents("$this->savePath/sess_$id");
    }

    public function write($id, $data)
    {
        return file_put_contents("$this->savePath/sess_$id", $data) === false ? false : true;
    }

    public function destroy($id)
    {
        $file = "$this->savePath/sess_$id";
        if (file_exists($file)) {
            unlink($file);
        }

        return true;
    }

    public function gc($maxlifetime)
    {
        foreach (glob("$this->savePath/sess_*") as $file) {
            if (filemtime($file) + $maxlifetime < time() && file_exists($file)) {
                unlink($file);
            }
        }

        return true;
    }
}

在 PHP 5.4+ 中,使用它就像在開始會話之前設置處理程序一樣簡單:

$handler = new MySessionHandler();
session_set_save_handler($handler, true);
session_start();

對於較低版本的 PHP,您仍然可以通過函數調用來完成...請參閱 PHP 文檔。

您可以通過在 session_write_close() 之后調用 session_start() 來重新啟動會話。 但是,這將導致多個 SIDS。 我通過在刷新輸出之前從標題中刪除多個 SIDS 來解決此問題。

看這個例子: https : //gist.github.com/CMCDragonkai/6912726#file-nativesession-php-L254

無法從 php 會話中禁用鎖。 這是鎖定的真正噩夢用例。 擺脫會話和/或 php 的唯一方法。 作為臨時解決方案,您可能想要使用 riak 會話處理程序: https : //github.com/zacharyfox/riak-php-sessions它是無鎖的,並且可以正常工作。

最新版本的 memcached 會話處理程序也出於某些瘋狂的原因引入了鎖定,並且無法禁用它。

如果 PHP 即使在調用 session_write_close 之后也沒有異步處理請求,它可能只是 xdebug。 我不知道這是否是你的問題,但我一直被這個問題絆倒並讓自己發瘋,所以我想如果其他人有同樣的問題,我會發布它:)

除了使用session_write_close() 我所知道的都沒有。

這是我在研究會話處理程序時遇到的一個相當古老的問題,但答案是肯定的,這是可能的 - 但不使用默認處理程序(無需深入入侵文件系統以禁用鎖定)。

我遇到了同樣的問題。 需要注意的重要一點是,您確實必須確切了解禁用鎖定的后果!

我的解決方案是會話處理機制的更大擴展的一部分 - 可堆疊會話處理程序,其中包括與默認處理程序兼容的存儲和(可選)非阻塞會話讀取和寫入。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM