Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Service Client Bundle Laravel Package

deeep/service-client-bundle

View on GitHub
Deep Wiki
Context7

Стратегии Multi-Host

🌐 Язык: English | Русский

Клиент поддерживает несколько хостов для высокой доступности и распределения нагрузки.

Стратегии

Failover

Пробует хосты по порядку, переключается на следующий при ошибке.

<?php
// config/packages/deeep_service_client.php

declare(strict_types=1);

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

return App::config([
    'deeep_service_client' => [
        'services' => [
            'api' => [
                'hosts' => [
                    'strategy' => 'failover',
                    'list' => [
                        ['url' => 'https://primary.example.com'],
                        ['url' => 'https://secondary.example.com'],
                        ['url' => 'https://tertiary.example.com'],
                    ],
                ],
            ],
        ],
    ],
]);

Поведение:

  • Начинает с первого хоста
  • Переключается на следующий при ошибке 5xx или сетевом сбое
  • Ошибки 4xx НЕ вызывают переключение (ошибка клиента)
  • Немедленно возвращает результат при успехе

Лучше всего подходит для: Высокой доступности, аварийного восстановления

Round-Robin

Равномерно распределяет запросы между хостами.

'api' => [
    'hosts' => [
        'strategy' => 'round_robin',
        'health_check' => [
            'enabled' => true,
            'interval' => 30,
            'path' => '/health',
        ],
        'list' => [
            ['url' => 'https://node1.example.com'],
            ['url' => 'https://node2.example.com'],
            ['url' => 'https://node3.example.com'],
        ],
    ],
],

Поведение:

  • Запрос 1 → Хост 1
  • Запрос 2 → Хост 2
  • Запрос 3 → Хост 3
  • Запрос 4 → Хост 1 (по кругу)
  • При сбое переходит к следующему хосту

Лучше всего подходит для: Балансировки нагрузки, горизонтального масштабирования

Parallel

Отправляет запрос на все хосты одновременно.

'api' => [
    'hosts' => [
        'strategy' => 'parallel',
        'mode' => 'first',          // first или all
        'timeout' => 5.0,
        'list' => [
            ['url' => 'https://fast.example.com'],
            ['url' => 'https://slow.example.com'],
        ],
    ],
],

Режимы:

  • first — Возвращает первый успешный ответ, отменяет остальные
  • all — Ждёт все ответы, агрегирует результаты

Лучше всего подходит для: Оптимизации задержки, избыточной верификации

Опции конфигурации

Health Check

'api' => [
    'hosts' => [
        'strategy' => 'round_robin',
        'health_check' => [
            'enabled' => true,
            'interval' => 30,        // Проверка каждые 30 секунд
            'path' => '/health',     // Путь к endpoint здоровья
            'timeout' => 5,          // Таймаут проверки
        ],
        'list' => [
            ['url' => 'https://node1.example.com'],
            ['url' => 'https://node2.example.com'],
        ],
    ],
],

Нездоровые хосты исключаются из ротации до восстановления.

Прокси для каждого хоста

'api' => [
    'hosts' => [
        'strategy' => 'failover',
        'list' => [
            [
                'url' => 'https://api.example.com',
                'proxy' => 'http://proxy1.local:8080',
            ],
            [
                'url' => 'https://api.example.com',
                'proxy' => 'http://proxy2.local:8080',
            ],
        ],
    ],
],

Взвешенное распределение

'api' => [
    'hosts' => [
        'strategy' => 'round_robin',
        'list' => [
            [
                'url' => 'https://powerful.example.com',
                'weight' => 3,         // Получает в 3 раза больше запросов
            ],
            [
                'url' => 'https://basic.example.com',
                'weight' => 1,
            ],
        ],
    ],
],

Создание кастомной стратегии

use Deeep\ServiceClient\Config\HostConfig;
use Deeep\ServiceClient\Contract\HostResultInterface;
use Deeep\ServiceClient\Contract\HostStrategyInterface;
use Deeep\ServiceClient\Contract\RequestInterface;
use Deeep\ServiceClient\Host\HostResult;

final class WeightedRandomStrategy implements HostStrategyInterface
{
    public function getName(): string
    {
        return 'weighted_random';
    }

    public function execute(
        RequestInterface $request,
        array $hosts,
        callable $sender,
    ): HostResultInterface {
        $totalWeight = array_sum(array_map(
            static fn(HostConfig $h) => $h->weight,
            $hosts,
        ));

        $random = random_int(1, $totalWeight);
        $current = 0;

        foreach ($hosts as $host) {
            $current += $host->weight;
            if ($random <= $current) {
                $response = $sender($request, $host);
                return new HostResult($response, $host, []);
            }
        }

        // Fallback к первому хосту
        $response = $sender($request, $hosts[0]);
        return new HostResult($response, $hosts[0], []);
    }
}

Регистрация кастомной стратегии

Стратегия автоматически зарегистрируется через HostStrategyInterface. Или вручную через тег:

<?php
// config/services.php

declare(strict_types=1);

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use App\Host\WeightedRandomStrategy;

return static function (ContainerConfigurator $container): void {
    $services = $container->services();

    $services->set(WeightedRandomStrategy::class)
        ->tag('deeep_service_client.host_strategy');
};

Затем используйте в конфигурации:

'api' => [
    'hosts' => [
        'strategy' => 'weighted_random',
        'list' => [
            [
                'url' => 'https://primary.example.com',
                'weight' => 5,
            ],
            [
                'url' => 'https://secondary.example.com',
                'weight' => 1,
            ],
        ],
    ],
],

Обработка ошибок

Когда все хосты недоступны:

use Deeep\ServiceClient\Exception\AllHostsFailedException;

try {
    $response = $client->send($request);
} catch (AllHostsFailedException $e) {
    $failures = $e->getFailures();
    // [
    //     ['host' => HostConfig, 'response' => ResponseInterface],
    //     ['host' => HostConfig, 'error' => Throwable],
    // ]

    foreach ($failures as $failure) {
        $logger->error('Хост недоступен', [
            'host' => $failure['host']->getBaseUrl(),
            'error' => $failure['error'] ?? $failure['response']->getStatusCode(),
        ]);
    }
}

Лучшие практики

  1. Используйте failover для критичных сервисов — Обеспечивает доступность
  2. Используйте round-robin с health checks — Предотвращает отправку на мёртвые ноды
  3. Устанавливайте разумные таймауты — Не ждите бесконечно медленных хостов
  4. Мониторьте сбои — Логируйте и настраивайте алерты на отказы хостов
  5. Тестируйте сценарии отказа — Симулируйте недоступность хостов на staging
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle