Клиент поддерживает несколько хостов для высокой доступности и распределения нагрузки.
Пробует хосты по порядку, переключается на следующий при ошибке.
<?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'],
],
],
],
],
],
]);
Поведение:
Лучше всего подходит для: Высокой доступности, аварийного восстановления
Равномерно распределяет запросы между хостами.
'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'],
],
],
],
Поведение:
Лучше всего подходит для: Балансировки нагрузки, горизонтального масштабирования
Отправляет запрос на все хосты одновременно.
'api' => [
'hosts' => [
'strategy' => 'parallel',
'mode' => 'first', // first или all
'timeout' => 5.0,
'list' => [
['url' => 'https://fast.example.com'],
['url' => 'https://slow.example.com'],
],
],
],
Режимы:
first — Возвращает первый успешный ответ, отменяет остальныеall — Ждёт все ответы, агрегирует результатыЛучше всего подходит для: Оптимизации задержки, избыточной верификации
'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(),
]);
}
}
How can I help you explore Laravel packages today?