config = array_merge($this->getDefaultConfig(), $config); $this->router = new TrafficRouter($this->config); $this->analyzer = new TrafficAnalyzer($this->config); $this->balancer = new TrafficBalancer($this->config); $this->monitor = new TrafficMonitor($this->config); $this->initialize(); } /** * Distribute traffic to services based on strategy. */ public function distributeTraffic(array $instances, array $request, string $strategy = 'weighted'): ?array { if (empty($instances)) { return null; } // Analyze request $requestAnalysis = $this->analyzer->analyzeRequest($request); // Apply routing rules $filteredInstances = $this->applyRoutingRules($instances, $requestAnalysis); if (empty($filteredInstances)) { return null; } // Select instance based on strategy $selectedInstance = $this->selectInstance($filteredInstances, $requestAnalysis, $strategy); if ($selectedInstance) { // Record traffic distribution $this->recordTrafficDistribution($selectedInstance['id'], $requestAnalysis, $strategy); // Update monitoring $this->monitor->recordRequest($selectedInstance['id'], $requestAnalysis); } return $selectedInstance; } /** * Add traffic distribution rule. */ public function addRule(string $name, array $rule): void { $this->validateRule($rule); $this->rules[$name] = array_merge([ 'name' => $name, 'enabled' => true, 'priority' => 100, 'conditions' => [], 'actions' => [], 'weight' => 1, 'created_at' => time(), 'updated_at' => time() ], $rule); // Sort rules by priority uasort($this->rules, function($a, $b) { return $a['priority'] <=> $b['priority']; }); $this->logInfo("Traffic distribution rule added: {$name}"); } /** * Remove traffic distribution rule. */ public function removeRule(string $name): bool { if (!isset($this->rules[$name])) { return false; } unset($this->rules[$name]); $this->logInfo("Traffic distribution rule removed: {$name}"); return true; } /** * Update traffic distribution rule. */ public function updateRule(string $name, array $updates): bool { if (!isset($this->rules[$name])) { return false; } $this->rules[$name] = array_merge($this->rules[$name], $updates); $this->rules[$name]['updated_at'] = time(); $this->logInfo("Traffic distribution rule updated: {$name}"); return true; } /** * Get all rules. */ public function getRules(): array { return $this->rules; } /** * Get rule by name. */ public function getRule(string $name): ?array { return $this->rules[$name] ?? null; } /** * Enable/disable rule. */ public function setRuleEnabled(string $name, bool $enabled): bool { if (!isset($this->rules[$name])) { return false; } $this->rules[$name]['enabled'] = $enabled; $this->rules[$name]['updated_at'] = time(); $this->logInfo("Rule " . ($enabled ? 'enabled' : 'disabled') . ": {$name}"); return true; } /** * Set traffic weights for services. */ public function setTrafficWeights(array $weights): void { $this->balancer->setWeights($weights); $this->logInfo("Traffic weights updated for " . count($weights) . " services"); } /** * Get traffic weights. */ public function getTrafficWeights(): array { return $this->balancer->getWeights(); } /** * Distribute traffic by percentage. */ public function distributeByPercentage(array $instances, array $percentages): ?array { if (empty($instances) || empty($percentages)) { return null; } // Validate percentages sum to 100 $totalPercentage = array_sum($percentages); if (abs($totalPercentage - 100) > 0.01) { throw new \InvalidArgumentException("Percentages must sum to 100, got: {$totalPercentage}"); } // Select instance based on percentage distribution return $this->balancer->selectByPercentage($instances, $percentages); } /** * Distribute traffic by geographic location. */ public function distributeByGeography(array $instances, string $clientLocation): ?array { if (empty($instances)) { return null; } // Find closest instances $closestInstances = $this->findClosestInstances($instances, $clientLocation); if (empty($closestInstances)) { // Fallback to any instance return $instances[0]; } // Select from closest instances using weighted random return $this->balancer->selectWeightedRandom($closestInstances); } /** * Distribute traffic by user segment. */ public function distributeBySegment(array $instances, string $userSegment): ?array { if (empty($instances)) { return null; } // Filter instances by segment $segmentInstances = array_filter($instances, function($instance) use ($userSegment) { $segments = $instance['segments'] ?? []; return in_array($userSegment, $segments) || in_array('all', $segments); }); if (empty($segmentInstances)) { // Fallback to instances without segment restriction $segmentInstances = array_filter($instances, function($instance) { $segments = $instance['segments'] ?? []; return empty($segments) || in_array('all', $segments); }); } if (empty($segmentInstances)) { return $instances[0]; // Ultimate fallback } return $this->balancer->selectRoundRobin(array_values($segmentInstances)); } /** * Distribute traffic by time of day. */ public function distributeByTime(array $instances, \DateTime $dateTime = null): ?array { $dateTime = $dateTime ?? new \DateTime(); $hour = (int) $dateTime->format('H'); if (empty($instances)) { return null; } // Filter instances by time availability $availableInstances = array_filter($instances, function($instance) use ($hour) { $availability = $instance['availability'] ?? []; if (empty($availability)) { return true; // Always available } foreach ($availability as $period) { if ($hour >= $period['start'] && $hour < $period['end']) { return true; } } return false; }); if (empty($availableInstances)) { return $instances[0]; // Fallback } return $this->balancer->selectWeightedRandom(array_values($availableInstances)); } /** * Distribute traffic by load capacity. */ public function distributeByCapacity(array $instances): ?array { if (empty($instances)) { return null; } // Calculate available capacity for each instance $capacityScores = []; foreach ($instances as $instance) { $maxCapacity = $instance['max_capacity'] ?? 100; $currentLoad = $instance['current_load'] ?? 0; $availableCapacity = max(0, $maxCapacity - $currentLoad); $capacityScores[$instance['id']] = $availableCapacity; } // Select instance with most available capacity $maxCapacity = max($capacityScores); $bestInstanceIds = array_keys($capacityScores, $maxCapacity); if (count($bestInstanceIds) === 1) { $bestInstanceId = $bestInstanceIds[0]; } else { // Multiple instances with same capacity, use round-robin $bestInstanceId = $bestInstanceIds[array_rand($bestInstanceIds)]; } foreach ($instances as $instance) { if ($instance['id'] === $bestInstanceId) { return $instance; } } return null; } /** * Get traffic distribution statistics. */ public function getStatistics(): array { $totalRequests = 0; $serviceStats = []; $ruleStats = []; // Calculate service statistics foreach ($this->trafficStats as $serviceId => $stats) { $requests = $stats['total_requests'] ?? 0; $totalRequests += $requests; $serviceStats[$serviceId] = [ 'total_requests' => $requests, 'percentage' => 0, // Will be calculated below 'average_response_time' => $this->calculateAverageResponseTime($serviceId), 'error_rate' => $this->calculateErrorRate($serviceId), 'last_request' => $stats['last_request'] ?? null ]; } // Calculate percentages if ($totalRequests > 0) { foreach ($serviceStats as $serviceId => &$stats) { $stats['percentage'] = ($stats['total_requests'] / $totalRequests) * 100; } } // Calculate rule statistics foreach ($this->rules as $ruleName => $rule) { $ruleStats[$ruleName] = [ 'enabled' => $rule['enabled'], 'priority' => $rule['priority'], 'match_count' => $rule['match_count'] ?? 0, 'last_matched' => $rule['last_matched'] ?? null ]; } return [ 'total_requests' => $totalRequests, 'service_statistics' => $serviceStats, 'rule_statistics' => $ruleStats, 'active_rules' => count(array_filter($this->rules, fn($r) => $r['enabled'])), 'distribution_history' => array_slice($this->distributionHistory, -10) ]; } /** * Get traffic distribution history. */ public function getDistributionHistory(int $limit = 100): array { return array_slice($this->distributionHistory, -$limit); } /** * Get real-time traffic metrics. */ public function getRealTimeMetrics(): array { return [ 'current_rps' => $this->monitor->getCurrentRPS(), 'active_connections' => $this->monitor->getActiveConnections(), 'average_response_time' => $this->monitor->getAverageResponseTime(), 'error_rate' => $this->monitor->getErrorRate(), 'top_services' => $this->monitor->getTopServices(10), 'geographic_distribution' => $this->monitor->getGeographicDistribution(), 'user_segment_distribution' => $this->monitor->getSegmentDistribution() ]; } /** * Optimize traffic distribution. */ public function optimizeDistribution(): array { $recommendations = []; $currentStats = $this->getStatistics(); // Analyze service performance foreach ($currentStats['service_statistics'] as $serviceId => $stats) { if ($stats['error_rate'] > 5) { $recommendations[] = [ 'type' => 'high_error_rate', 'service_id' => $serviceId, 'message' => "Service {$serviceId} has high error rate: {$stats['error_rate']}%", 'action' => 'consider_reducing_weight_or_failing_over' ]; } if ($stats['average_response_time'] > 1000) { // 1 second $recommendations[] = [ 'type' => 'slow_response', 'service_id' => $serviceId, 'message' => "Service {$serviceId} has slow response time: {$stats['average_response_time']}ms", 'action' => 'consider_reducing_weight' ]; } } // Analyze rule effectiveness foreach ($currentStats['rule_statistics'] as $ruleName => $stats) { if ($stats['enabled'] && $stats['match_count'] === 0) { $recommendations[] = [ 'type' => 'unused_rule', 'rule_name' => $ruleName, 'message' => "Rule {$ruleName} is enabled but never matches", 'action' => 'review_or_disable_rule' ]; } } // Suggest weight adjustments $weightSuggestions = $this->suggestWeightAdjustments(); $recommendations = array_merge($recommendations, $weightSuggestions); return $recommendations; } /** * Reset traffic statistics. */ public function resetStatistics(): void { $this->trafficStats = []; $this->distributionHistory = []; foreach ($this->rules as &$rule) { $rule['match_count'] = 0; $rule['last_matched'] = null; } $this->logInfo("Traffic statistics reset"); } /** * Export traffic configuration. */ public function exportConfiguration(string $format = 'json'): string { $data = [ 'rules' => $this->rules, 'traffic_weights' => $this->getTrafficWeights(), 'statistics' => $this->getStatistics(), 'distribution_history' => $this->distributionHistory, 'exported_at' => date('Y-m-d H:i:s'), 'version' => $this->config['version'] ?? '1.0' ]; switch ($format) { case 'json': return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); case 'php': return 'rules as $rule) { if (!$rule['enabled']) { continue; } if ($this->matchesRuleConditions($rule['conditions'], $requestAnalysis)) { $rule['match_count'] = ($rule['match_count'] ?? 0) + 1; $rule['last_matched'] = time(); // Apply rule actions $filteredInstances = $this->applyRuleActions($filteredInstances, $rule['actions']); // If rule has stop_processing flag, break if ($rule['stop_processing'] ?? false) { break; } } } return $filteredInstances; } /** * Check if request matches rule conditions. */ protected function matchesRuleConditions(array $conditions, array $requestAnalysis): bool { foreach ($conditions as $condition) { if (!$this->matchesCondition($condition, $requestAnalysis)) { return false; } } return true; } /** * Check if single condition matches. */ protected function matchesCondition(array $condition, array $requestAnalysis): bool { $field = $condition['field']; $operator = $condition['operator']; $value = $condition['value']; $requestValue = $this->getRequestValue($requestAnalysis, $field); switch ($operator) { case 'equals': return $requestValue === $value; case 'not_equals': return $requestValue !== $value; case 'in': return in_array($requestValue, (array) $value); case 'not_in': return !in_array($requestValue, (array) $value); case 'contains': return is_string($requestValue) && strpos($requestValue, $value) !== false; case 'starts_with': return is_string($requestValue) && strpos($requestValue, $value) === 0; case 'ends_with': return is_string($requestValue) && substr($requestValue, -strlen($value)) === $value; case 'greater_than': return $requestValue > $value; case 'less_than': return $requestValue < $value; case 'between': return $requestValue >= $value[0] && $requestValue <= $value[1]; case 'regex': return preg_match($value, (string) $requestValue) === 1; default: return false; } } /** * Get value from request analysis. */ protected function getRequestValue(array $requestAnalysis, string $field) { $fields = explode('.', $field); $value = $requestAnalysis; foreach ($fields as $f) { if (!is_array($value) || !array_key_exists($f, $value)) { return null; } $value = $value[$f]; } return $value; } /** * Apply rule actions to instances. */ protected function applyRuleActions(array $instances, array $actions): array { $filteredInstances = $instances; foreach ($actions as $action) { switch ($action['type']) { case 'filter': $filteredInstances = $this->filterInstances($filteredInstances, $action['conditions']); break; case 'set_weight': $filteredInstances = $this->setInstanceWeights($filteredInstances, $action['weights']); break; case 'prioritize': $filteredInstances = $this->prioritizeInstances($filteredInstances, $action['criteria']); break; case 'exclude': $filteredInstances = $this->excludeInstances($filteredInstances, $action['instances']); break; } } return $filteredInstances; } /** * Select instance based on strategy. */ protected function selectInstance(array $instances, array $requestAnalysis, string $strategy): ?array { switch ($strategy) { case 'weighted': return $this->balancer->selectWeightedRandom($instances); case 'round_robin': return $this->balancer->selectRoundRobin($instances); case 'least_connections': return $this->balancer->selectLeastConnections($instances); case 'response_time': return $this->balancer->selectByResponseTime($instances); case 'geographic': $location = $requestAnalysis['client']['location'] ?? 'unknown'; return $this->distributeByGeography($instances, $location); case 'segment': $segment = $requestAnalysis['user']['segment'] ?? 'default'; return $this->distributeBySegment($instances, $segment); case 'capacity': return $this->distributeByCapacity($instances); default: return $instances[0]; } } /** * Find closest instances by geography. */ protected function findClosestInstances(array $instances, string $clientLocation): array { // This would use a proper geolocation service in production // For now, return instances with location matching return array_filter($instances, function($instance) use ($clientLocation) { $instanceLocation = $instance['location'] ?? 'unknown'; return $instanceLocation === $clientLocation || $instanceLocation === 'global'; }); } /** * Record traffic distribution. */ protected function recordTrafficDistribution(string $serviceId, array $requestAnalysis, string $strategy): void { // Update service stats if (!isset($this->trafficStats[$serviceId])) { $this->trafficStats[$serviceId] = [ 'total_requests' => 0, 'requests_by_strategy' => [], 'response_times' => [], 'errors' => 0, 'last_request' => null ]; } $this->trafficStats[$serviceId]['total_requests']++; $this->trafficStats[$serviceId]['requests_by_strategy'][$strategy] = ($this->trafficStats[$serviceId]['requests_by_strategy'][$strategy] ?? 0) + 1; $this->trafficStats[$serviceId]['last_request'] = time(); // Add to distribution history $this->distributionHistory[] = [ 'timestamp' => time(), 'service_id' => $serviceId, 'strategy' => $strategy, 'request_analysis' => $requestAnalysis ]; // Limit history size if (count($this->distributionHistory) > 1000) { $this->distributionHistory = array_slice($this->distributionHistory, -1000); } } /** * Calculate average response time for service. */ protected function calculateAverageResponseTime(string $serviceId): float { $stats = $this->trafficStats[$serviceId] ?? []; $responseTimes = $stats['response_times'] ?? []; if (empty($responseTimes)) { return 0.0; } return array_sum($responseTimes) / count($responseTimes); } /** * Calculate error rate for service. */ protected function calculateErrorRate(string $serviceId): float { $stats = $this->trafficStats[$serviceId] ?? []; $totalRequests = $stats['total_requests'] ?? 0; $errors = $stats['errors'] ?? 0; if ($totalRequests === 0) { return 0.0; } return ($errors / $totalRequests) * 100; } /** * Suggest weight adjustments. */ protected function suggestWeightAdjustments(): array { $suggestions = []; $stats = $this->getStatistics(); foreach ($stats['service_statistics'] as $serviceId => $serviceStats) { if ($serviceStats['error_rate'] > 2) { $suggestions[] = [ 'type' => 'weight_adjustment', 'service_id' => $serviceId, 'current_weight' => $this->balancer->getWeight($serviceId), 'suggested_weight' => max(1, (int) ($this->balancer->getWeight($serviceId) * 0.5)), 'reason' => 'High error rate detected' ]; } } return $suggestions; } /** * Validate rule configuration. */ protected function validateRule(array $rule): void { if (empty($rule['conditions'])) { throw new \InvalidArgumentException("Rule must have at least one condition"); } if (empty($rule['actions'])) { throw new \InvalidArgumentException("Rule must have at least one action"); } foreach ($rule['conditions'] as $condition) { if (!isset($condition['field']) || !isset($condition['operator']) || !isset($condition['value'])) { throw new \InvalidArgumentException("Condition must have field, operator, and value"); } } } /** * Initialize traffic distribution strategy. */ protected function initialize(): void { // Load configuration from storage $this->loadConfiguration(); // Start monitoring $this->monitor->start(); $this->logInfo("Traffic distribution strategy initialized"); } /** * Load configuration from storage. */ protected function loadConfiguration(): void { // This would load from persistent storage in production $this->logInfo("Configuration loaded"); } /** * Log info message. */ protected function logInfo(string $message): void { if ($this->config['logging_enabled']) { error_log("[TrafficDistributionStrategy] {$message}"); } } /** * Get default configuration. */ protected function getDefaultConfig(): array { return [ 'default_strategy' => 'weighted', 'enable_monitoring' => true, 'monitoring_interval' => 60, 'history_retention' => 1000, 'logging_enabled' => true, 'optimization_enabled' => true, 'optimization_interval' => 300, 'version' => '1.0' ]; } /** * Get configuration. */ public function getConfig(): array { return $this->config; } /** * Set configuration. */ public function setConfig(array $config): void { $this->config = array_merge($this->config, $config); } /** * Create traffic distribution strategy instance. */ public static function create(array $config = []): self { return new self($config); } /** * Create for high traffic. */ public static function forHighTraffic(): self { return new self([ 'default_strategy' => 'capacity', 'enable_monitoring' => true, 'monitoring_interval' => 30, 'optimization_enabled' => true, 'optimization_interval' => 120 ]); } /** * Create for global distribution. */ public static function forGlobalDistribution(): self { return new self([ 'default_strategy' => 'geographic', 'enable_monitoring' => true, 'logging_enabled' => true ]); } }