PHP 蓝绿部署会话保持

wen PHP项目 5

本文目录导读:

PHP 蓝绿部署会话保持

  1. 共享会话存储(推荐)
  2. 数据库会话存储
  3. 负载均衡器配置(辅助方案)
  4. 部署切换策略
  5. 完整示例:Laravel/Symfony 配置
  6. 最佳实践建议

在 PHP 蓝绿部署中实现会话保持(Session Stickiness)是一个常见的挑战,因为传统的 PHP 会话默认存储在本地文件系统中,以下是几种实现方案:

共享会话存储(推荐)

Redis 方案

// php.ini 配置
session.save_handler = redis
session.save_path = "tcp://redis-server:6379?auth=password&database=0"

或者使用 PHP 代码初始化:

<?php
// 在应用启动时配置
ini_set('session.save_handler', 'redis');
ini_set('session.save_path', 'tcp://redis-host:6379');
// 使用 Predis 或 phpredis 扩展
// 示例使用 phpredis
$redis = new Redis();
$redis->connect('redis-server', 6379);
session_set_save_handler(
    function($savePath, $sessionName) use ($redis) {
        return true;
    },
    function() {
        return true;
    },
    function($sessionId) use ($redis) {
        return $redis->get("session:$sessionId") ?: '';
    },
    function($sessionId, $data) use ($redis) {
        $redis->set("session:$sessionId", $data, 3600);
        return true;
    },
    function($sessionId) use ($redis) {
        $redis->del("session:$sessionId");
        return true;
    },
    function($maxLifetime) {
        return true;
    }
);
session_start();
?>

Memcached 方案

// php.ini
session.save_handler = memcached
session.save_path = "memcached-server:11211"
// 或者使用 Memcached 类
$memcached = new Memcached();
$memcached->addServer('memcached-server', 11211);
session_start();

数据库会话存储

<?php
class SessionDBHandler {
    private $pdo;
    public function __construct($pdo) {
        $this->pdo = $pdo;
    }
    public function start() {
        session_set_save_handler(
            array($this, 'open'),
            array($this, 'close'),
            array($this, 'read'),
            array($this, 'write'),
            array($this, 'destroy'),
            array($this, 'gc')
        );
        session_start();
    }
    public function open($savePath, $sessionName) {
        return true;
    }
    public function close() {
        return true;
    }
    public function read($sessionId) {
        $stmt = $this->pdo->prepare("SELECT data FROM sessions WHERE id = ?");
        $stmt->execute([$sessionId]);
        return $stmt->fetchColumn() ?: '';
    }
    public function write($sessionId, $data) {
        $stmt = $this->pdo->prepare(
            "INSERT INTO sessions (id, data, last_access) 
             VALUES (?, ?, NOW()) 
             ON DUPLICATE KEY UPDATE data = ?, last_access = NOW()"
        );
        return $stmt->execute([$sessionId, $data, $data]);
    }
    public function destroy($sessionId) {
        $stmt = $this->pdo->prepare("DELETE FROM sessions WHERE id = ?");
        return $stmt->execute([$sessionId]);
    }
    public function gc($maxLifetime) {
        $stmt = $this->pdo->prepare(
            "DELETE FROM sessions WHERE last_access < DATE_SUB(NOW(), INTERVAL ? SECOND)"
        );
        return $stmt->execute([$maxLifetime]);
    }
}
// 使用
$pdo = new PDO('mysql:host=db-server;dbname=app', 'user', 'pass');
$handler = new SessionDBHandler($pdo);
$handler->start();

负载均衡器配置(辅助方案)

Nginx + IP Hash

upstream php_servers {
    ip_hash;
    server blue-server:9000;
    server green-server:9000;
}
server {
    listen 80;
    server_name example.com;
    location / {
        proxy_pass http://php_servers;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

HAProxy Cookie 粘性

backend php_servers
    balance roundrobin
    cookie SERVERID insert indirect nocache
    server blue-server blue:9000 cookie blue check
    server green-server green:9000 cookie green check

部署切换策略

优雅切换方案

#!/bin/bash
# deploy.sh
# 1. 部署到 Green 环境
git pull origin main
rsync -av --exclude='.git' ./ green-server:/var/www/green/
# 2. 切换到 Green(修改负载均衡配置)
# 方式1:修改 Nginx 配置
cp nginx_blue.conf nginx.conf
nginx -s reload
# 方式2:使用 Docker 容器
docker-compose up -d green
docker-compose scale blue=0
# 3. 回滚
# 如果需要,切回 Blue

完整示例:Laravel/Symfony 配置

Laravel .env 配置

SESSION_DRIVER=redis
REDIS_HOST=redis-server
REDIS_PORT=6379
# .env.blue
APP_ENV=blue
APP_URL=https://blue.example.com
# .env.green
APP_ENV=green
APP_URL=https://green.example.com

部署脚本

<?php
// deploy.php
$blueServers = ['blue-1', 'blue-2'];
$greenServers = ['green-1', 'green-2'];
function switchTraffic($activeColor) {
    // 更新负载均衡配置
    $config = "upstream php_servers {\n";
    if ($activeColor === 'green') {
        foreach ($greenServers as $server) {
            $config .= "    server {$server}:9000;\n";
        }
        // 保留部分 blue 用于灰度
        $config .= "    server {$blueServers[0]}:9000 backup;\n";
    } else {
        foreach ($blueServers as $server) {
            $config .= "    server {$server}:9000;\n";
        }
        $config .= "    server {$greenServers[0]}:9000 backup;\n";
    }
    $config .= "}\n";
    file_put_contents('/etc/nginx/sites-available/php_upstream.conf', $config);
    exec('nginx -s reload');
}
// 健康检查
function healthCheck($servers) {
    foreach ($servers as $server) {
        $response = @file_get_contents("http://{$server}/health");
        if ($response !== 'OK') {
            return false;
        }
    }
    return true;
}
// 执行部署
switchTraffic('green');

最佳实践建议

  1. 会话数据序列化
    // 使用自定义会话序列化器
    ini_set('session.serialize_handler', 'php_serialize');

// 存储用户对象 $_SESSION['user'] = serialize($userObject);


2. **会话 ID 管理**
```php
// 更换会话 ID(防止会话固定攻击)
session_regenerate_id(true);
// 配置会话 cookie
session_set_cookie_params([
    'lifetime' => 0,
    'path' => '/',
    'secure' => true,
    'httponly' => true,
    'samesite' => 'Lax'
]);
  1. 监控与日志
    // 记录会话信息用于调试
    error_log("Session ID: " . session_id() . " IP: " . $_SERVER['REMOTE_ADDR']);
  • 首选方案:使用 Redis/数据库等共享存储,这样蓝绿切换时无需担心会话丢失
  • 辅助措施:负载均衡器配置 cookie 粘性或 IP hash 减少会话迁移
  • 部署策略:先部署新版本到空闲环境,测试通过后切换流量,保留回滚能力
  • 监控告警:部署后监控会话成功率、错误率等指标

这样配置后,即使发生蓝绿切换,用户的会话也能保持,不会因为切换到新环境而丢失登录状态。

抱歉,评论功能暂时关闭!