Связь между сервисами: внутренние адреса и сервисы без публичного адреса.

Как сервисы одного приложения обращаются друг к другу по внутреннему адресу, переменные RIVERVO_<NAME>_URL, сервисы без публичного адреса, несколько экземпляров, а также тайм-ауты и повторы — с примерами для Node.js, Laravel и Python.

5 МИН ЧТЕНИЯ

Сервисы одного приложения обращаются друг к другу напрямую, без выхода в интернет. Сайт может вызывать свой API, фоновый процесс — внутренний сервис, и этот трафик бесплатный: он не учитывается как трафик.

В примерах ниже два веб-сервиса: web — сайт, который открывают посетители, в режиме Публичный адрес, и api, к которому обращаются только web и другие сервисы, в режиме Только внутренний.

Внутренний адрес

У каждого веб-сервиса — с публичным адресом или только внутреннего — есть Внутренний адрес вида http://<service-name>:<port>, например http://api:8080. Он показан на странице сервиса и в Обзор приложения, с кнопкой копирования и подсказкой «Другие сервисы этого приложения обращаются к нему по этому адресу — без интернета и без оплаты трафика.»

  • Порт — 8080 для сервиса, собранного из репозитория, или Порт, который вы указали для сервиса из готового образа.
  • Этот адрес доступен только сервисам того же приложения. Из интернета и из других приложений он не открывается.
  • Адрес начинается с http://. Соединение не выходит за пределы приложения, поэтому настраивать сертификат не нужно.

Переменные с адресами

Копировать адреса в код не нужно. Каждый сервис с кодом — веб-сервисы, фоновые процессы и задачи по расписанию — получает переменную с внутренним адресом каждого другого веб-сервиса приложения:

RIVERVO_<NAME>_URL=http://<name>:<port>

NAME — имя сервиса заглавными буквами, дефисы заменяются на _:

СервисПеременная
apiRIVERVO_API_URL=http://api:8080
admin-apiRIVERVO_ADMIN_API_URL=http://admin-api:8080
webRIVERVO_WEB_URL=http://web:8080
search (из готового образа, порт 7700)RIVERVO_SEARCH_URL=http://search:7700
  • Переменную с собственным адресом сервис не получает.
  • У баз данных и Valkey остаётся своя RIVERVO_<NAME>_URL с адресом подключения. Имена сервисов внутри приложения уникальны, поэтому переменные никогда не совпадают.
  • Переменные показаны на вкладке Переменные среди переменных, которые Задаются автоматически. Как и с любой автоматической переменной, ваша собственная (или общая) переменная с тем же именем заменяет её — см. Переменные окружения.
  • Переменные доходят до сервиса со следующим развёртыванием. Когда вы добавляете новый веб-сервис или переименовываете сервис, остальные сервисы получают новую или изменённую переменную при своём следующем развёртывании. Разверните их заново, чтобы они её получили, например командой rivervo deploy <app> --no-build — текущая версия запускается заново с новыми переменными, без новой сборки.

Сервисы «Только внутренний»

Веб-сервису, к которому обращаются только другие сервисы приложения, — API за вашим сайтом, административной части, поисковому движку — не обязательно быть в интернете. Переведите его в режим Только внутренний:

Публичный адресТолько внутренний
Адрес в интернете https://<name>.rivervo.appданет
Собственные доменыданет
Превью ветокданет
Внутренний адрес для других сервисовдада
Несколько экземпляров и автоматическое масштабирование, журнал, мониторинг, проверки работоспособностидада

Только внутренний сервис работает как любой другой; обратиться к нему могут только другие сервисы того же приложения.

Выбор при создании сервиса

При создании веб-сервиса — в том числе первого сервиса нового приложения, из репозитория, шаблона или готового образа — выберите Публичный адрес или Только внутренний. Заранее ничего не выбрано, поэтому решение всегда за вами. У фоновых процессов, задач по расписанию и баз данных публичного адреса не бывает, поэтому для них выбора нет.

Сервисы, созданные до появления этого выбора, сохраняют публичный адрес.

Переключение позже

Сервис → Настройки → Публичный адрес: переключите и подтвердите. Изменить это может только Администратор приложения или владелец аккаунта. Ничего не перезапускается.

  • Перевести в режим «Только внутренний» нельзя, пока у сервиса подключены домены, — сначала удалите их на его вкладке Домены. Превью тоже нужно сначала выключить.
  • При включении публичного адреса сервис получает тот же адрес https://<name>.rivervo.app. Сертификат может появиться примерно через минуту.
  • Добавить домен к только внутреннему сервису нельзя — сначала включите ему публичный адрес.

Вкладка Терминал у только внутреннего сервиса работает так же, как у любого другого.

Асинхронное взаимодействие

Обращения выше ждут ответа. Когда сервис должен передать работу и продолжить своё — фоновые задачи, события, на которые реагируют несколько сервисов, — передавайте сообщения через очередь или брокер сообщений: Valkey, NATS или RabbitMQ. См. События и очереди.

Несколько экземпляров

Если сервис, к которому вы обращаетесь, работает в нескольких экземплярах, его внутреннее имя ведёт ко всем его работающим экземплярам, и соединения распределяются между ними.

Клиент, который держит соединения открытыми и использует их повторно (пул keep-alive-соединений), может продолжать отправлять запросы в один и тот же экземпляр; новые соединения снова распределяются. Это нормально, настраивать ничего не нужно.

Тайм-ауты и повторы

Во время развёртывания или когда автоматическое масштабирование останавливает экземпляр, выполняющийся запрос может завершиться ошибкой. Две привычки делают обращения между сервисами надёжными:

  • Короткий тайм-аут — несколько секунд, чтобы запрос не зависал, пока экземпляр, в который он попал, останавливается.
  • Один-два повтора с короткой паузой — для запросов, которые безопасно повторить: чтение данных через GET. Не повторяйте запросы, которые изменяют данные (платежи, заказы), если ваш API не умеет отличать повторный запрос от нового.

Пример: Node.js

const api = process.env.RIVERVO_API_URL
 
async function getWithRetry(url, attempts = 3) {
  for (let i = 1; ; i++) {
    try {
      const res = await fetch(url, { signal: AbortSignal.timeout(5000) })
      if (res.status < 500 || i === attempts) return res
    } catch (err) {
      if (i === attempts) throw err
    }
    await new Promise((resolve) => setTimeout(resolve, 200 * i))
  }
}
 
const res = await getWithRetry(`${api}/orders`)
const orders = await res.json()

Пример: Laravel

Укажите адрес в config/services.php:

'api' => [
    'url' => env('RIVERVO_API_URL'),
],

И читайте его через config(). В рабочем окружении конфигурация кэшируется, и env(), вызванный вне файлов конфигурации, ничего не возвращает:

use Illuminate\Support\Facades\Http;
 
$orders = Http::timeout(5)
    ->retry(2, 200)
    ->get(config('services.api.url').'/orders')
    ->json();

timeout(5) ждёт не больше 5 секунд; retry(2, 200) делает до 2 попыток с паузой 200 мс между ними.

Пример: Python

import os
import requests
 
API = os.environ["RIVERVO_API_URL"]
 
orders = requests.get(f"{API}/orders", timeout=5).json()

С повторами для GET:

import os
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
 
API = os.environ["RIVERVO_API_URL"]
 
session = requests.Session()
retry = Retry(total=2, backoff_factor=0.2, allowed_methods=["GET"], status_forcelist=[502, 503, 504])
session.mount("http://", HTTPAdapter(max_retries=retry))
 
orders = session.get(f"{API}/orders", timeout=5).json()

Неудачный GET — ошибка соединения или ответ 502, 503 или 504 — повторяется до 2 раз, с короткой паузой, которая каждый раз растёт.

Из командной строки и Rivervo MCP

  • rivervo apps и сведения о сервисе показывают внутренний адрес каждого веб-сервиса и то, есть ли у него публичный адрес.
  • rivervo services set-public <service> on|off включает и выключает публичный адрес. Нужны роль Администратора и токен с областью действия Полный доступ.
  • В Rivervo MCP то же самое делает инструмент set_public_address.

О токенах и командной строке — в статье Инструменты разработчика.

Всё ещё не можете разобраться?

Напишите живому инженеру — медианный ответ менее 3 минут в любое время суток.

Написать в поддержку