多个 PHP Web 服务器为用户会话共享一个 Redis 服务器
Multiple PHP Web Servers to Share a Single Redis Server for User Sessions
我有 2 个 Web 服务器 运行 PHP 和 Nginx... 我有 1 个 Redis 服务器。我希望 Web 服务器将 Redis 数据库用于用户登录数据。这将允许我扩展 Web 服务器,而无需强制登录用户重新验证 if/when 他们访问了新服务器。
两台 PHP/Nginx 服务器都使用 Redis 进行会话处理,并且它们都指向同一个 Redis 数据库。当我在浏览器中访问任一站点时,它会生成一个会话令牌并将其存储在 Redis 中(总共创建两个 Redis 密钥)... 但是我想要的是在访问站点 #1 时创建一个新会话,将会话存储在 Redis 中,并在我访问时让站点 #2 识别我,因为它查询 Redis(因此总共只有一个 Redis 密钥)。
我的所有 3 个服务器都在 AWS 中,在同一个 VPC 中。我正在使用内部 IP 地址进行通信 b/w 网络服务器和 Redis(因此有一定程度的信任 b/w 机器)。
我显然在这里遗漏了一些东西,它可能是负载平衡器(我将实现)。但我试图用最少的组件开发一个 Redis 共享设计。另外,我不完全确定负载平衡器是否有助于解决此问题。
以下是代码的相关部分:
PHP/Nginx 服务器:(x2)
(1) /etc/php/7.2/fpm/php.ini
session.save_handler = redis
session.save_path = "tcp://10.xxx.xx.xxx:6379" // both web servers point to the same Redis IP address
(2) /var/www/html/test.php
<?php
ini_set('session.save_handler', 'redis'); // redundant; including for completeness
ini_set('session.save_path', 'tcp://10.xxx.xx.xxx:6379'); // redundant; including for completeness
session_name('FOOBAR');
session_start();
echo nl2br(
'<pre>' . session_save_path() . '</pre>' . PHP_EOL
);
echo nl2br(
'Running PHP version: ' . phpversion() . PHP_EOL
);
if (!array_key_exists('visit', $_SESSION)) {
$_SESSION['visit'] = 0;
}
$_SESSION['visit']++;
echo nl2br(
'You have been here ' . $_SESSION['visit'] . ' times.'
);
Redis 服务器:(x1)
(3) /etc/redis/redis.conf
bind 0.0.0.0
当我点击两个站点的 test.php 页面时,它们成功生成了自己的会话并将其存储在 Redis 数据库中。他们正确地增加了他们各自的会话令牌......但是我希望这些网络服务器共享 Redis 会话数据 b/w 他们,所以他们 "remember" 所有登录的用户(即使这是用户第一次命中给定的网络服务器)。
谢谢!
编辑: 我还尝试手动查询 Redis 以获取登录用户数据。这产生了与上述 test.php 相似的结果。每个网站最终都创建和管理自己的会话令牌。
PHP/Nginx 服务器:(x2)
(4) /var/www/html/redis-check.php
<?php
$cookie = "PHPREDIS_SESSION:" . $_COOKIE['FOOBAR'];
// Connecting to Redis server on localhost
$redis = new Redis();
$redis->connect('10.xxx.xx.xxx', 6379);
echo "Connection to server sucessfully";
echo '<br />';
// Check if cookie value exists in Redis
$seenuser = $redis->get($cookie);
if( !empty($seenuser) )
{
echo 'Hello, old friend!<br />';
echo '<br />';
echo 'Cookie Value: ' . $cookie;
echo '<br />';
echo 'Session Value: ' . $seenuser;
}
else
{
// This is a new user, so create a new session
ini_set('session.save_handler', 'redis'); // redundant; including for completeness
ini_set('session.save_path', 'tcp://10.xxx.xx.xxx:6379'); // redundant; including for completeness
session_name('FOOBAR');
session_start();
if (!array_key_exists('visit', $_SESSION))
{
$_SESSION['visit'] = 0;
}
$_SESSION['visit']++;
$cookie = "PHPREDIS_SESSION:" . $_COOKIE['FOOBAR'];
$seenuser = $redis->get($cookie);
echo 'New Login<br />';
echo '<br />';
echo 'Cookie Value: ' . $cookie;
echo '<br />';
echo 'Session Value: ' . $seenuser;
}
看来我的问题是因为缺少负载均衡器。我将 HAProxy 放在我的两个 Web 服务器前面,指向 HAProxy 在浏览器中的地址,以及 Web 服务器立即开始共享存储在 Redis 中的会话数据。我猜他们都在使用 HAProxy 地址。我的浏览器 (Chrome) 只显示 HAProxy 地址的单个 cookie(这就是我想要的)...当我绕过 HAProxy 并直接在浏览器中指向 Web 服务器时,它们会生成自己的会话令牌(这对这种情况不利)。
所以这是相关的代码部分 (你可以忽略我OP中的内容):
PHP/Nginx 服务器:(x2)
(1) /etc/php/7.2/fpm/php.ini
session.save_handler = redis
session.save_path = "tcp://10.xxx.xx.xxx:6379" // both web servers point to the same Redis IP address
(2) /var/www/html/test.php
<?php
session_name('FOOBAR');
session_start();
echo 'IP Address: ' . getHostByName(getHostName());
echo '<br />';
echo '*******************************************************************************';
echo '<br />';
if (!array_key_exists('visit', $_SESSION))
{
$_SESSION['visit'] = 0;
echo 'Hello, new friend!';
echo '<br />';
}
else
{
echo 'Glad you are still with us!!';
echo '<br />';
}
$_SESSION['visit']++;
echo 'You have been here ' . $_SESSION['visit'] . ' times.';
Redis 服务器:(x1)
(3) /etc/redis/redis.conf
bind 0.0.0.0
HA 服务器:(x1)
(4) /etc/haproxy/haproxy.cfg
frontend proxy_in
bind *:80
default_backend web_farm
backend web_farm
balance roundrobin
mode http
server web1 10.xxx.xx.111:80 check # web server 1
server web2 10.xxx.xx.222:80 check # web server 2
PHP/Nginx 服务器:(x2)
/var/www/html/redis-check.php
Ignore this file. The revised test.php file is all that is needed.
Nigel 和 Saad,感谢您提供的链接! 总是对这个问题有不同的看法是件好事。
我有 2 个 Web 服务器 运行 PHP 和 Nginx... 我有 1 个 Redis 服务器。我希望 Web 服务器将 Redis 数据库用于用户登录数据。这将允许我扩展 Web 服务器,而无需强制登录用户重新验证 if/when 他们访问了新服务器。
两台 PHP/Nginx 服务器都使用 Redis 进行会话处理,并且它们都指向同一个 Redis 数据库。当我在浏览器中访问任一站点时,它会生成一个会话令牌并将其存储在 Redis 中(总共创建两个 Redis 密钥)... 但是我想要的是在访问站点 #1 时创建一个新会话,将会话存储在 Redis 中,并在我访问时让站点 #2 识别我,因为它查询 Redis(因此总共只有一个 Redis 密钥)。
我的所有 3 个服务器都在 AWS 中,在同一个 VPC 中。我正在使用内部 IP 地址进行通信 b/w 网络服务器和 Redis(因此有一定程度的信任 b/w 机器)。
我显然在这里遗漏了一些东西,它可能是负载平衡器(我将实现)。但我试图用最少的组件开发一个 Redis 共享设计。另外,我不完全确定负载平衡器是否有助于解决此问题。
以下是代码的相关部分:
PHP/Nginx 服务器:(x2)
(1) /etc/php/7.2/fpm/php.ini
session.save_handler = redis
session.save_path = "tcp://10.xxx.xx.xxx:6379" // both web servers point to the same Redis IP address
(2) /var/www/html/test.php
<?php
ini_set('session.save_handler', 'redis'); // redundant; including for completeness
ini_set('session.save_path', 'tcp://10.xxx.xx.xxx:6379'); // redundant; including for completeness
session_name('FOOBAR');
session_start();
echo nl2br(
'<pre>' . session_save_path() . '</pre>' . PHP_EOL
);
echo nl2br(
'Running PHP version: ' . phpversion() . PHP_EOL
);
if (!array_key_exists('visit', $_SESSION)) {
$_SESSION['visit'] = 0;
}
$_SESSION['visit']++;
echo nl2br(
'You have been here ' . $_SESSION['visit'] . ' times.'
);
Redis 服务器:(x1)
(3) /etc/redis/redis.conf
bind 0.0.0.0
当我点击两个站点的 test.php 页面时,它们成功生成了自己的会话并将其存储在 Redis 数据库中。他们正确地增加了他们各自的会话令牌......但是我希望这些网络服务器共享 Redis 会话数据 b/w 他们,所以他们 "remember" 所有登录的用户(即使这是用户第一次命中给定的网络服务器)。
谢谢!
编辑: 我还尝试手动查询 Redis 以获取登录用户数据。这产生了与上述 test.php 相似的结果。每个网站最终都创建和管理自己的会话令牌。
PHP/Nginx 服务器:(x2)
(4) /var/www/html/redis-check.php
<?php
$cookie = "PHPREDIS_SESSION:" . $_COOKIE['FOOBAR'];
// Connecting to Redis server on localhost
$redis = new Redis();
$redis->connect('10.xxx.xx.xxx', 6379);
echo "Connection to server sucessfully";
echo '<br />';
// Check if cookie value exists in Redis
$seenuser = $redis->get($cookie);
if( !empty($seenuser) )
{
echo 'Hello, old friend!<br />';
echo '<br />';
echo 'Cookie Value: ' . $cookie;
echo '<br />';
echo 'Session Value: ' . $seenuser;
}
else
{
// This is a new user, so create a new session
ini_set('session.save_handler', 'redis'); // redundant; including for completeness
ini_set('session.save_path', 'tcp://10.xxx.xx.xxx:6379'); // redundant; including for completeness
session_name('FOOBAR');
session_start();
if (!array_key_exists('visit', $_SESSION))
{
$_SESSION['visit'] = 0;
}
$_SESSION['visit']++;
$cookie = "PHPREDIS_SESSION:" . $_COOKIE['FOOBAR'];
$seenuser = $redis->get($cookie);
echo 'New Login<br />';
echo '<br />';
echo 'Cookie Value: ' . $cookie;
echo '<br />';
echo 'Session Value: ' . $seenuser;
}
看来我的问题是因为缺少负载均衡器。我将 HAProxy 放在我的两个 Web 服务器前面,指向 HAProxy 在浏览器中的地址,以及 Web 服务器立即开始共享存储在 Redis 中的会话数据。我猜他们都在使用 HAProxy 地址。我的浏览器 (Chrome) 只显示 HAProxy 地址的单个 cookie(这就是我想要的)...当我绕过 HAProxy 并直接在浏览器中指向 Web 服务器时,它们会生成自己的会话令牌(这对这种情况不利)。
所以这是相关的代码部分 (你可以忽略我OP中的内容):
PHP/Nginx 服务器:(x2)
(1) /etc/php/7.2/fpm/php.ini
session.save_handler = redis
session.save_path = "tcp://10.xxx.xx.xxx:6379" // both web servers point to the same Redis IP address
(2) /var/www/html/test.php
<?php
session_name('FOOBAR');
session_start();
echo 'IP Address: ' . getHostByName(getHostName());
echo '<br />';
echo '*******************************************************************************';
echo '<br />';
if (!array_key_exists('visit', $_SESSION))
{
$_SESSION['visit'] = 0;
echo 'Hello, new friend!';
echo '<br />';
}
else
{
echo 'Glad you are still with us!!';
echo '<br />';
}
$_SESSION['visit']++;
echo 'You have been here ' . $_SESSION['visit'] . ' times.';
Redis 服务器:(x1)
(3) /etc/redis/redis.conf
bind 0.0.0.0
HA 服务器:(x1)
(4) /etc/haproxy/haproxy.cfg
frontend proxy_in
bind *:80
default_backend web_farm
backend web_farm
balance roundrobin
mode http
server web1 10.xxx.xx.111:80 check # web server 1
server web2 10.xxx.xx.222:80 check # web server 2
PHP/Nginx 服务器:(x2)
/var/www/html/redis-check.php
Ignore this file. The revised test.php file is all that is needed.
Nigel 和 Saad,感谢您提供的链接! 总是对这个问题有不同的看法是件好事。