本文目录导读:

- 基于配置文件的 Sidecar 管理器
- 配置文件示例
- 带健康检查和自动重启的增强版
- 使用示例
- PHP 配置文件示例 (sidecar-config.php)
- 使用环境变量配置
- 使用 Docker Compose 配合
在 PHP 中实现可配置的 Sidecar 模式,通常有以下几种方案:
基于配置文件的 Sidecar 管理器
<?php
// SidecarConfig.php
class SidecarConfig
{
private array $config;
public function __construct(string $configFile)
{
$this->config = $this->loadConfig($configFile);
}
private function loadConfig(string $file): array
{
if (!file_exists($file)) {
throw new Exception("Config file not found: {$file}");
}
$ext = pathinfo($file, PATHINFO_EXTENSION);
return match ($ext) {
'yaml', 'yml' => yaml_parse_file($file),
'json' => json_decode(file_get_contents($file), true),
'php' => require $file,
default => throw new Exception("Unsupported config format: {$ext}")
};
}
public function getSidecars(): array
{
return $this->config['sidecars'] ?? [];
}
}
// SidecarRunner.php
class SidecarRunner
{
private SidecarConfig $config;
private array $processes = [];
public function __construct(SidecarConfig $config)
{
$this->config = $config;
}
public function startAll(): void
{
foreach ($this->config->getSidecars() as $name => $sidecar) {
$this->start($name, $sidecar);
}
}
private function start(string $name, array $sidecar): void
{
$command = $sidecar['command'] ?? '';
$args = $sidecar['args'] ?? [];
$env = $sidecar['env'] ?? [];
$workingDir = $sidecar['working_dir'] ?? null;
$fullCommand = $command . ' ' . implode(' ', $args);
$process = proc_open(
$fullCommand,
[
0 => ['file', '/dev/null', 'r'],
1 => ['file', $sidecar['log_file'] ?? '/dev/null', 'a'],
2 => ['file', $sidecar['error_log'] ?? '/dev/null', 'a']
],
$pipes,
$workingDir,
$env
);
if (is_resource($process)) {
$this->processes[$name] = $process;
echo "Started sidecar: {$name}\n";
} else {
echo "Failed to start sidecar: {$name}\n";
}
}
public function stopAll(): void
{
foreach ($this->processes as $name => $process) {
proc_terminate($process);
proc_close($process);
echo "Stopped sidecar: {$name}\n";
}
$this->processes = [];
}
public function getStatus(): array
{
$status = [];
foreach ($this->processes as $name => $process) {
$status[$name] = proc_get_status($process);
}
return $status;
}
}
配置文件示例
config.yaml
sidecars:
redis:
command: "redis-server"
args: ["--port", "6379", "--daemonize", "no"]
env:
REDIS_PASSWORD: "secret"
working_dir: "/tmp"
log_file: "/var/log/sidecars/redis.log"
error_log: "/var/log/sidecars/redis-error.log"
timeout: 30
health_check:
command: "redis-cli ping"
interval: 5
retries: 3
nginx:
command: "nginx"
args: ["-g", "daemon off;"]
env: {}
working_dir: "/etc/nginx"
log_file: "/var/log/sidecars/nginx.log"
error_log: "/var/log/sidecars/nginx-error.log"
timeout: 30
health_check:
command: "curl -f http://localhost:8080/health"
interval: 10
retries: 5
带健康检查和自动重启的增强版
<?php
class SidecarSupervisor
{
private array $config;
private array $processes = [];
private array $healthChecks = [];
public function __construct(array $config)
{
$this->config = $config;
}
public function start(): void
{
foreach ($this->config['sidecars'] as $name => $sidecar) {
$this->startProcess($name, $sidecar);
$this->startHealthCheck($name, $sidecar);
}
}
private function startProcess(string $name, array $sidecar): void
{
$command = $this->buildCommand($sidecar['command'], $sidecar['args'] ?? []);
$descriptors = [
0 => ['pipe', 'r'],
1 => ['file', $sidecar['log_file'] ?? '/dev/null', 'a'],
2 => ['file', $sidecar['error_log'] ?? '/dev/null', 'a']
];
$process = proc_open($command, $descriptors, $pipes, $sidecar['working_dir'] ?? null, $sidecar['env'] ?? []);
if (is_resource($process)) {
$this->processes[$name] = [
'process' => $process,
'pipes' => $pipes,
'config' => $sidecar
];
fclose($pipes[0]); // Close stdin
}
}
private function startHealthCheck(string $name, array $sidecar): void
{
if (!isset($sidecar['health_check'])) {
return;
}
$this->healthChecks[$name] = [
'command' => $sidecar['health_check']['command'],
'interval' => $sidecar['health_check']['interval'] ?? 5,
'retries' => $sidecar['health_check']['retries'] ?? 3,
'last_check' => 0,
'failures' => 0
];
}
public function monitor(): void
{
while (true) {
$this->checkAllHealth();
$this->restartFailedProcesses();
sleep(1);
}
}
private function checkAllHealth(): void
{
$now = time();
foreach ($this->healthChecks as $name => &$health) {
if ($now - $health['last_check'] >= $health['interval']) {
$health['last_check'] = $now;
// Execute health check
$result = shell_exec($health['command']);
if ($result === false || empty(trim($result))) {
$health['failures']++;
$this->log("Health check failed for {$name} ({$health['failures']}/{$health['retries']})");
if ($health['failures'] >= $health['retries']) {
$this->restartProcess($name);
$health['failures'] = 0;
}
} else {
$health['failures'] = 0;
}
}
}
}
private function restartProcess(string $name): void
{
$this->log("Restarting sidecar: {$name}");
if (isset($this->processes[$name])) {
proc_terminate($this->processes[$name]['process']);
proc_close($this->processes[$name]['process']);
}
$this->startProcess($name, $this->processes[$name]['config']);
}
private function buildCommand(string $command, array $args): string
{
return $command . ' ' . implode(' ', array_map('escapeshellarg', $args));
}
private function log(string $message): void
{
echo "[" . date('Y-m-d H:i:s') . "]" . " {$message}\n";
}
}
使用示例
<?php
// main.php
require_once 'SidecarConfig.php';
require_once 'SidecarRunner.php';
// 方式1:使用配置文件
$config = new SidecarConfig('config.yaml');
$runner = new SidecarRunner($config);
$runner->startAll();
// 方式2:使用 Supervisor(推荐用于生产环境)
$config = require 'sidecar-config.php'; // PHP 配置文件
$supervisor = new SidecarSupervisor($config);
pcntl_signal(SIGTERM, function($signo) use ($supervisor) {
$supervisor->shutdown();
});
$supervisor->start();
$supervisor->monitor();
PHP 配置文件示例 (sidecar-config.php)
<?php
return [
'sidecars' => [
'mysql' => [
'command' => 'mysqld',
'args' => ['--port=3306', '--socket=/tmp/mysql.sock'],
'env' => [
'MYSQL_ROOT_PASSWORD' => 'root',
'MYSQL_DATABASE' => 'app_db'
],
'working_dir' => '/var/lib/mysql',
'log_file' => '/var/log/mysql/error.log',
'error_log' => '/var/log/mysql/error.log',
'health_check' => [
'command' => 'mysqladmin ping -h localhost',
'interval' => 5,
'retries' => 3
]
],
'redis-cache' => [
'command' => 'redis-server',
'args' => ['/etc/redis/redis.conf'],
'env' => [],
'working_dir' => null,
'log_file' => '/var/log/redis/redis.log',
'error_log' => '/var/log/redis/redis-error.log',
'health_check' => [
'command' => 'redis-cli ping',
'interval' => 10,
'retries' => 5
]
],
'custom-worker' => [
'command' => 'php',
'args' => ['/app/worker.php'],
'env' => [
'QUEUE_CONNECTION' => 'redis',
'CACHE_DRIVER' => 'redis'
],
'working_dir' => '/app',
'log_file' => '/var/log/app/worker.log',
'error_log' => '/var/log/app/worker-error.log',
'health_check' => [
'command' => 'curl -f http://localhost:8080/health',
'interval' => 15,
'retries' => 3
]
]
],
'global_settings' => [
'log_level' => 'debug',
'resource_limits' => [
'memory_limit' => '256M',
'timeout' => 60,
'max_processes' => 10
],
'restart_policy' => [
'max_retries' => 3,
'delay_seconds' => 5
]
]
];
使用环境变量配置
<?php
class EnvConfigSidecar
{
public static function fromEnv(): array
{
$config = [];
// 从环境变量读取 Sidecar 配置
$sidecars = getenv('SIDECARS') ? json_decode(getenv('SIDECARS'), true) : [];
foreach ($sidecars as $name => $sidecar) {
$config['sidecars'][$name] = [
'command' => $sidecar['command'] ?? getenv("SIDECAR_{$name}_COMMAND"),
'args' => $sidecar['args'] ?? [],
'env' => $sidecar['env'] ?? [],
'log_file' => $sidecar['log_file'] ?? '/dev/null',
'health_check' => $sidecar['health_check'] ?? null
];
}
return $config;
}
}
使用 Docker Compose 配合
# docker-compose.yml
version: '3.8'
services:
app:
build: .
environment:
- SIDECARS=${SIDECARS}
volumes:
- ./config:/app/config
command: php /app/main.php
sidecar-proxy:
image: redis:alpine
restart: always
这种配置方式非常灵活,支持多种配置格式(YAML、JSON、PHP 数组),可以轻松集成到 CI/CD 流程中,并且方便在不同环境(开发、测试、生产)之间切换配置。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播