在多伺服器架構(負載均衡、微服務、多子域名系統)中,使用者請求可能被分發到不同的 PHP 節點。若 Session 儲存在本地檔案系統,使用者在節點 A 登入後,跳轉到節點 B 時就會丟失登入狀態。解決此問題的核心在於:**讓所有節點讀寫同一份 Session 資料**。
NFS (Network File System) 是一種成熟的網路檔案系統協定,透過掛載遠端目錄到本地,讓多台伺服器共享同一組 Session 檔案。本文將詳細說明從 PHP 設定到 NFS 架設的完整流程。
大量 Session 檔案集中在單一目錄會導致 inode 耗盡、目錄查找緩慢。PHP 內建支援分級雜湊目錄:
session.save_path = "2;/tmp/php_sess"
參數解析:
2:分級深度(兩層子目錄)/tmp/php_sess:基礎儲存目錄/tmp/php_sess/a/b/sess_...,每層 16 個十六進位子目錄(0-9, a-f),總共 256×256 = 65,536 個葉子目錄,極大降低單目錄檔案數。PHP 原始碼附贈 mod_files.sh 可自動建立目錄樹。儲存路徑建議:/usr/local/server/php/include/php/ext/session/mod_files.sh
#!/bin/sh
# 用法: mod_files.sh basedir depth [numberofsubdirs]
# 範例: ./mod_files.sh /tmp/php_sess 2 16
if test "$2" = ""; then
echo "usage: $0 basedir depth [numberofsubdirs]"
exit 1
fi
if test "$2" = "0"; then exit 0; fi
hash_chars="0 1 2 3 4 5 6 7 8 9 a b c d e f"
if [ ! -z $3 ]; then
if test "$3" -a "$3" -eq "32"; then
hash_chars="$hash_chars g h i j k l m n o p q r s t u v"
if test "$3" -eq "64"; then
hash_chars="$hash_chars w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z - ,"
fi
fi
fi
for i in $hash_chars; do
newpath="$1/$i"
mkdir $newpath || exit 1
sh $0 $newpath `expr $2 - 1` $3
done
chmod +x mod_files.sh
cd /usr/local/server/php/include/php/ext/session/
./mod_files.sh /tmp/php_sess 2 16
# CentOS/RHEL
yum install nfs-utils rpcbind -y
# Ubuntu/Debian
apt-get install nfs-kernel-server -y
/tmp/php_sess/ 192.168.0.*(rw,sync,no_root_squash,no_subtree_check)
關鍵選項說明:
rw:讀寫權限sync:同步寫入(資料安全優先)no_root_squash:允許客戶端 root 權限寫入(Session 檔案通常由 www-data/apache 建立,視實際情況調整)no_subtree_check:提升效能,減少子目錄檢查systemctl enable rpcbind nfs-server --now
exportfs -rv # 重新匯出
# 安裝客戶端工具
yum install nfs-utils -y # 或 apt-get install nfs-common
# 建立掛載點
mkdir -p /tmp/php_sess
# 測試掛載
mount -t nfs 192.168.0.1:/tmp/php_sess /tmp/php_sess
# 寫入 /etc/fstab 實現開機自動掛載
echo "192.168.0.1:/tmp/php_sess /tmp/php_sess nfs defaults,_netdev 0 0" >> /etc/fstab
# 於任一客戶端建立測試檔案
echo "test" > /tmp/php_sess/test.txt
# 於其他客戶端確認可見
cat /tmp/php_sess/test.txt
確認所有節點 php.ini 皆設定:
session.save_handler = files
session.save_path = "2;/tmp/php_sess"
session.gc_maxlifetime = 1440 # 視需求調整
重啟 PHP-FPM/Apache 後,使用瀏覽器開發者工具觀察 Session ID 是否在不同節點間保持一致。
async 模式:若能容忍極小機率資料遺失,可在 /etc/exports 改用 async 大幅提升寫入效能。actimeo=1 減少屬性快取延遲:mount -o actimeo=1 192.168.0.1:/tmp/php_sess /tmp/php_sessiostat -x 中 await、svctm、NFS 執行緒數 (nfsstat -rc)。| 問題 | 原因 | 對策 |
|---|---|---|
| Session 檔案權限錯誤 | 多節點 UID/GID 不一致 | 統一 Apache/PHP-FPM 運行使用者 UID/GID,或使用 bindfs 映射 |
| NFS 掛載點失聯導致 PHP hang 住 | 預設 hard mount 會無限重試 | 掛載參數加入 soft,timeo=10,retrans=3 快速失敗,並實作健康檢查自動摘除節點 |
| 單點故障 | NFS Server 掛掉全站 Session 失效 | 部署 NFS HA (DRBD+Heartbeat/Corosync) 或改用 Redis/Memcached/Database 方案 |