I have 2 web servers running PHP and Nginx... And I have 1 Redis Server. I want the web servers to use the Redis database for user login data. This will allow me to scale out the web servers without forcing logged-in users to re-authenticate if/when they hit a new server.
Both PHP/Nginx servers are using Redis for session handling, and they both point to the same Redis database. When I visit either site in my browser it generates a session token and stores it in Redis (creating two Redis keys total).... But what I want is to create a new session when I visit Site #1, store the session in Redis, and have Site #2 recognize me when I visit because it queries Redis (so only one Redis key total).
All 3 of my servers are in AWS, in the same VPC. I'm using internal IP addresses for communication b/w the web servers and Redis (so there is some amount of trust b/w the machines).
I'm obviously missing something here, and it might be the load balancer (which I will implement). But I was attempting to develop a Redis-sharing design with the minimal number of components. Also, I'm not entirely sure the load balancer helps w/ this issue.
Here are the relevant portions of code:
PHP/Nginx Servers: (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 Server: (x1)
(3) /etc/redis/redis.conf
bind 0.0.0.0
When I hit the test.php page for both sites, they successfully generate and store their own session in the Redis database. And they correctly increment their respective session token... But I want these web servers to share the Redis session data b/w them, so they "remember" all logged-in users (even if it's the user's first time hitting a given web server).
Thanks!
EDIT: I also attempted to manually query Redis for logged-in user data. This yielded similar results to test.php above. Each website ended up creating and managing its own session token.
PHP/Nginx Servers: (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;
}
Looks like my issue was due to missing a load balancer. I put HAProxy in front of my two web servers, pointed to HAProxy's address in the broswer, and the web servers immediately began sharing the session data stored in Redis. I guess they're both using the HAProxy address. And my browser (Chrome) shows only a single cookie for the HAProxy address (which is what I want)... When I bypass HAProxy and point to the web servers directly in the browser they generate their own session token (which is bad for this scenario).
So here are the relevant code sections (you can disregard what's in my OP):
PHP/Nginx Servers: (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 Server: (x1)
(3) /etc/redis/redis.conf
bind 0.0.0.0
HA Server: (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 Servers: (x2)
/var/www/html/redis-check.php
Ignore this file. The revised test.php file is all that is needed.
Nigel and Saad, thank you for the links! It's always good to get a different POV on the problem.