Skip to content

[FEATURE] Criar Extensão RoadRunner Runtime para PivotPHP #4

Description

@code-cfernandes

📋 Descrição

Desenvolver extensão para integrar PivotPHP com RoadRunner, um application server de alta performance escrito em Go. Esta implementação oferecerá recursos enterprise-grade como workers supervisionados, plugins extensíveis e integração com ecossistema Go.

🎯 Objetivo

Implementar um runtime RoadRunner que oferece:

  • Workers PHP supervisionados pelo Go
  • Sistema de plugins (jobs, KV, broadcast, metrics)
  • Balanceamento de carga inteligente
  • Health checks e circuit breakers
  • Integração com ferramentas DevOps

📦 Entregáveis

1. Novo Repositório

  • Nome: pivotphp-runtime-roadrunner
  • Namespace: Pivot\Runtime\RoadRunner
  • Integração: spiral/roadrunner-php

2. Componentes Principais

RoadRunnerAdapter.php

namespace Pivot\Runtime\RoadRunner;

use Spiral\RoadRunner\Worker;
use Spiral\RoadRunner\Http\PSR7Worker;
use Pivot\Core\Application;
use Nyholm\Psr7\Factory\Psr17Factory;

class RoadRunnerAdapter implements RuntimeAdapterInterface
{
    private PSR7Worker $worker;
    private Application $app;
    private WorkerManager $manager;
    
    public function serve(Application $app, array $config = []): void
    {
        $this->app = $app;
        $this->initializeWorker();
        $this->runWorkerLoop();
    }
    
    private function initializeWorker(): void
    {
        $worker = Worker::create();
        $factory = new Psr17Factory();
        
        $this->worker = new PSR7Worker($worker, $factory, $factory, $factory);
        $this->manager = new WorkerManager($worker);
    }
    
    private function runWorkerLoop(): void
    {
        while ($request = $this->worker->waitRequest()) {
            try {
                // Reset application state
                $this->manager->beforeRequest();
                
                // Process request
                $response = $this->processRequest($request);
                
                // Send response
                $this->worker->respond($response);
                
                // Cleanup
                $this->manager->afterRequest();
                
                // Check if worker should restart
                if ($this->manager->shouldRestart()) {
                    break;
                }
            } catch (\Throwable $e) {
                $this->worker->getWorker()->error((string)$e);
            }
        }
    }
}

WorkerManager.php

namespace Pivot\Runtime\RoadRunner\Worker;

class WorkerManager
{
    private int $requestCount = 0;
    private float $startTime;
    private array $metrics = [];
    
    public function beforeRequest(): void
    {
        $this->requestCount++;
        $this->captureMemoryUsage();
        $this->resetGlobalState();
    }
    
    public function afterRequest(): void
    {
        $this->collectGarbage();
        $this->checkMemoryLimits();
        $this->updateMetrics();
    }
    
    public function shouldRestart(): bool
    {
        // Restart after X requests
        if ($this->requestCount >= config('roadrunner.max_requests', 10000)) {
            return true;
        }
        
        // Restart on memory threshold
        if (memory_get_usage(true) > config('roadrunner.memory_limit', 128 * 1024 * 1024)) {
            return true;
        }
        
        // Restart after TTL
        if (time() - $this->startTime > config('roadrunner.ttl', 3600)) {
            return true;
        }
        
        return false;
    }
}

3. Plugin Integrations

Jobs Plugin

namespace Pivot\Runtime\RoadRunner\Plugins;

use Spiral\RoadRunner\Jobs\JobsInterface;
use Spiral\RoadRunner\Jobs\Queue;

class JobsPlugin
{
    private JobsInterface $jobs;
    
    public function dispatch(string $queue, array $payload): string
    {
        $queue = $this->jobs->connect($queue);
        
        $task = $queue->create(
            class: PivotJobHandler::class,
            payload: $payload,
            options: [
                'delay' => 0,
                'priority' => 10,
            ]
        );
        
        return $queue->dispatch($task);
    }
    
    public function consume(string $queue, callable $handler): void
    {
        $consumer = $this->jobs->consume($queue);
        
        while ($task = $consumer->waitTask()) {
            try {
                $result = $handler($task->payload);
                $task->complete($result);
            } catch (\Throwable $e) {
                $task->fail($e);
            }
        }
    }
}

KV (Key-Value) Plugin

namespace Pivot\Runtime\RoadRunner\Plugins;

use Spiral\RoadRunner\KeyValue\StorageInterface;

class KVPlugin
{
    private StorageInterface $storage;
    
    public function set(string $key, mixed $value, int $ttl = 0): void
    {
        $this->storage->set($key, serialize($value), $ttl);
    }
    
    public function get(string $key): mixed
    {
        $value = $this->storage->get($key);
        return $value ? unserialize($value) : null;
    }
    
    public function mget(array $keys): array
    {
        return array_map('unserialize', $this->storage->mGet($keys));
    }
}

4. Configuration

.rr.yaml

version: "3"

server:
  command: "php worker.php"
  env:
    - APP_ENV: production
    - APP_RUNTIME: roadrunner

http:
  address: 0.0.0.0:8080
  pool:
    num_workers: ${RR_HTTP_WORKERS:-4}
    max_jobs: 500
    allocate_timeout: 60s
    destroy_timeout: 60s
  middleware: ["gzip", "headers", "static"]
  uploads:
    forbid: [".php", ".exe", ".bat"]
    max_size: 100MB
  static:
    dir: "public"
    forbid: [".php", ".htaccess"]
    calculate_etag: true
    weak: true
    cache_duration: 10m

jobs:
  pool:
    num_workers: ${RR_JOBS_WORKERS:-2}
    max_jobs: 100
  pipelines:
    emails:
      driver: memory
      priority: 10
    notifications:
      driver: amqp
      queue: pivot_notifications
      exchange: pivot
      
kv:
  local:
    driver: memory
  redis:
    driver: redis
    config:
      addrs:
        - "localhost:6379"

metrics:
  address: 0.0.0.0:2112
  collect:
    app_metric_*: true

reload:
  enabled: true
  interval: 1s
  patterns: [".php"]
  services:
    http:
      recursive: true
      ignore: ["vendor/"]
      patterns: [".php", ".env"]
      dirs: ["app", "config", "routes"]

logs:
  mode: production
  level: info
  output: stdout
  err_output: stderr

📋 Tasks Detalhadas

Setup e Integração

  • Criar repositório e estrutura
  • Integrar spiral/roadrunner packages
  • Setup worker bootstrap file
  • Configurar .rr.yaml template
  • Docker setup com RoadRunner

Core Implementation

  • RoadRunnerAdapter principal
  • PSR-7 bridge otimizado
  • Worker lifecycle management
  • State reset mechanisms
  • Error handling e recovery

Plugins Integration

  • Jobs plugin para queues
  • KV storage abstraction
  • Broadcast/WebSocket plugin
  • Metrics collection
  • Health checks

Performance & Monitoring

  • Prometheus metrics export
  • Memory profiling
  • Request tracing
  • Performance benchmarks
  • Load balancing strategies

Developer Tools

  • RoadRunner CLI wrapper
  • Development mode
  • Debug panel integration
  • Log aggregation
  • Configuration validation

Production Features

  • Graceful shutdown
  • Zero-downtime reload
  • Circuit breakers
  • Rate limiting
  • SSL/TLS support

Testing

  • Unit tests para adapter
  • Integration tests com RR
  • Plugin functionality tests
  • Load tests (30k+ req/s)
  • Stability tests (72h+)

🔧 Comandos CLI

# Desenvolvimento
./vendor/bin/rr serve -c .rr.dev.yaml

# Produção
./vendor/bin/rr serve

# Workers status
./vendor/bin/rr workers

# Reset workers
./vendor/bin/rr reset

# Métricas
curl http://localhost:2112/metrics

📊 Performance Targets

Métrica Target Notes
Throughput 30,000 req/s Com 4 workers
Latência P50 < 2ms Requests simples
Latência P99 < 8ms Com DB queries
Memory/Worker < 50MB Após 10k requests
CPU Usage < 70% Full load
Startup Time < 100ms Per worker

🚀 Features Únicas

1. Supervisão Go

  • Restart automático de workers
  • Health monitoring nativo
  • Resource limits enforcement

2. Plugin Ecosystem

  • Queue system integrado
  • Cache distribuído
  • WebSocket/SSE support
  • Temporal workflow engine

3. DevOps Ready

  • Kubernetes native
  • Prometheus metrics
  • OpenTelemetry tracing
  • JSON structured logs

4. Performance

  • Zero-allocation hot path
  • Lock-free job distribution
  • NUMA-aware scheduling

📚 Dependências

{
    "require": {
        "php": "^8.1",
        "pivotphp/core": "^1.0",
        "spiral/roadrunner": "^2023.3",
        "spiral/roadrunner-http": "^3.0",
        "spiral/roadrunner-jobs": "^4.0",
        "spiral/roadrunner-kv": "^4.0",
        "nyholm/psr7": "^1.8"
    },
    "require-dev": {
        "spiral/roadrunner-cli": "^2.5",
        "phpunit/phpunit": "^10.0"
    }
}

🗓️ Timeline

  • Semana 1-2: Core adapter implementation
  • Semana 3: Worker management
  • Semana 4-5: Plugins integration
  • Semana 6: Production features
  • Semana 7: Testing e optimization
  • Semana 8: Documentation e release

⚠️ Considerações

Complexidade

  • Requer conhecimento de Go para customizações
  • Configuração mais complexa que outras soluções
  • Debugging através de bridge PHP-Go

Vantagens

  • Performance enterprise-grade
  • Ecosistema rico de plugins
  • Suporte comercial disponível
  • Amplamente usado em produção

🏷️ Labels

  • extension
  • runtime
  • roadrunner
  • enterprise
  • high-performance
  • go-integration

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions