一、为什么负载均衡之后还会出现登录反复失效

原笔记指出,用户登录请求经过负载均衡之后,可能先落到 web01,下一次又落到 web02
如果登录状态只保存在单台 Web 本地,那么用户就会遇到:

  • 一会儿是已登录
  • 一会儿又要求重新登录

这就是典型的会话不一致问题。

原笔记把这个目标总结为:

  • 会话保持
  • 会话共享

本质上就是要让不同 Web 节点读到同一份登录状态。

二、先分清 Cookie 和 Session 的职责

原笔记在进入方案之前,先做了一个概念铺垫。

技术点 共同点 区别
Cookie 都能和用户登录信息有关 保存在客户端浏览器
Session 都能存放用户状态信息 保存在服务端

可以把它简单理解成:

  • Cookie 更像浏览器侧随请求携带的小标识
  • Session 更像服务端真正保存状态的地方

很多应用场景里,两者是配合使用的:

  • 浏览器带着 Cookie 来
  • 服务端根据 Cookie 找到对应的 Session

一旦后端是多节点,Session 就不能再只存在某一台本地机器上。

三、原笔记为什么选择 phpMyAdmin 做会话保持实验

原笔记选择的是 phpMyAdmin,而不是 WordPress。
原因也很清楚:

  • phpMyAdmin 是典型的 PHP 动态应用
  • 登录态明显,容易观察会话问题
  • 不需要额外创建复杂业务数据模型

同时这套实验环境仍沿用前面的多主机架构:

  • web01
  • web02
  • lb
  • db
  • nfs

这样就可以在真实的负载均衡环境里验证会话共享。

四、先把 phpMyAdmin 的数据库账号准备好

原笔记先在数据库主机上创建了一个权限较大的账号:

mysql -uroot -p123456

然后授权:

grant all on *.* to 'phpmyadmin'@'172.16.1.%' identified by '1';

这里的重点不是业务库本身,而是让 phpMyAdmin 这个管理工具能从 Web 节点连接到数据库。

五、两台 Web 上的 phpMyAdmin 要怎么部署

原笔记先在 web01 部署 phpMyAdmin,再复制到 web02

5.1 web01 上的站点配置

站点配置如下:

server {
  listen 80;
  server_name admin.oldboylinux.cn;
  root /app/code/admin;

  error_log /var/log/nginx/admin-error.log notice;
  access_log /var/log/nginx/admin-access.log main;

  location / {
    index index.php;
  }

  location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_buffering on;
    fastcgi_buffers 64 64k;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    include fastcgi_params;
  }
}

然后部署代码:

unzip phpMyAdmin-5.2.1-all-languages.zip
mkdir -p /app/code/admin
chown -R www.www /app/code/admin/
mv phpMyAdmin-5.2.1-all-languages/* /app/code/admin/

再复制配置模板并做必要调整:

cp config.sample.inc.php config.inc.php

原笔记还单独给 /var/lib/php/session 做了授权:

chown -R www.www /var/lib/php/session

5.2 web02 上快速复用 web01 成果

原笔记在 web02 上先创建好目录:

mkdir -p /app/code/admin/
chown -R www.www /app/code/admin/

再从 web01 复制代码目录和 Nginx 配置:

scp -r admin/ 192.168.1.22:`pwd`
scp admin.oldboylinux.cn.conf 192.168.1.22:`pwd`

之后在 web02 上执行:

nginx -t
systemctl reload nginx
chown -R www.www /var/lib/php/session

这样两台 Web 就有了相同的 phpMyAdmin 站点。

六、LB 层如何把 phpMyAdmin 接入集群

原笔记在 LB 上新建 admin.oldboylinux.cn 配置:

upstream admin_pools {
 server 192.168.1.20:80;
 server 192.168.1.22:80;
}

server {
  listen 80;
  server_name admin.oldboylinux.cn;

  error_log /var/log/nginx/admin-error.log notice;
  access_log /var/log/nginx/admin-access.log main;

  location / {
    proxy_pass http://admin_pools;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

从这一刻开始,登录请求就会被轮流打到不同 Web 节点,也正因此会暴露出会话共享问题。

七、Redis 为什么能解决会话共享

原笔记的解决思路是:

  • 不把 Session 放在各自 Web 本地
  • 而是把 Session 统一存到 Redis

这样无论用户下一次被分发到哪台 Web,只要两台机器都连接同一个 Redis,就能读到同一份会话状态。

7.1 先在 DB 主机上部署 Redis

原笔记步骤如下:

yum install -y redis

然后修改 /etc/redis.conf,让它同时监听本地和内网地址:

bind 127.0.0.1 172.16.1.23

最后启动:

systemctl enable --now redis

并检查:

ss -lntup | grep redis

7.2 为什么又新建了一个 php-fpm 端口池

原笔记没有直接修改原来的 www 池,而是新建了 /etc/php-fpm.d/session.conf

[session]
user = www
group = www
listen = 127.0.0.1:9001
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
slowlog = /var/log/php-fpm/www-slow.log
php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_value[session.save_handler] = redis
php_value[session.save_path]    = tcp://172.16.1.23:6379
php_value[soap.wsdl_cache_dir]  = /var/lib/php/wsdlcache

这份配置的关键有两个:

  • 单独监听 127.0.0.1:9001
  • session.save_handler 改成 redis

也就是说,原笔记是把“需要 Redis 会话”的业务,单独放到新的 PHP-FPM 池里处理。

八、为什么 Nginx 里还要把 fastcgi_pass 改到 9001

因为新的会话池监听的是 127.0.0.1:9001,所以两台 Web 上的 phpMyAdmin Nginx 配置都要改成:

fastcgi_pass 127.0.0.1:9001;

原笔记强调,这一步需要在 web01web02 都做,并在修改后执行:

nginx -t
systemctl reload nginx

这样 phpMyAdmin 的 PHP 请求就不再走原来的 9000 池,而是专门走会话存 Redis 的 9001 池。

九、如何确认 Session 已经写进 Redis

完成改造后,原笔记的验证方式是:

1、在浏览器访问 http://admin.oldboylinux.cn/ 2、正常登录 phpMyAdmin 3、到 Redis 里查看键值

示例:

redis-cli
KEYS *

如果能看到类似:

PHPREDIS_SESSION:fa85f7fa9af6ccbaaad545b300495ebd

就说明 PHP Session 已经不在本地文件,而是统一进了 Redis。

十、小结

负载均衡环境下,会话共享的核心不是“尽量让用户总落到同一台机器”,而是“让所有机器都能看到同一份会话数据”。
原笔记通过 phpMyAdmin + Redis + 新建 PHP-FPM 会话池的方式,把这个思路演示得很完整:

  • 前端 LB 负责分发请求
  • 后端双 Web 负责处理应用
  • Redis 负责统一保存 Session

这正是很多 PHP 多节点场景里非常经典的一种会话保持做法。