Связь между сервисами: внутренние адреса и сервисы без публичного адреса.
Как сервисы одного приложения обращаются друг к другу по внутреннему адресу, переменные RIVERVO_<NAME>_URL, сервисы без публичного адреса, несколько экземпляров, а также тайм-ауты и повторы — с примерами для Node.js, Laravel и Python.
Сервисы одного приложения обращаются друг к другу напрямую, без выхода в интернет. Сайт может вызывать свой API, фоновый процесс — внутренний сервис, и этот трафик бесплатный: он не учитывается как трафик.
В примерах ниже два веб-сервиса: web — сайт, который открывают посетители, в режиме Публичный адрес, и api, к которому обращаются только web и другие сервисы, в режиме Только внутренний.
Внутренний адрес
У каждого веб-сервиса — с публичным адресом или только внутреннего — есть Внутренний адрес вида http://<service-name>:<port>, например http://api:8080. Он показан на странице сервиса и в Обзор приложения, с кнопкой копирования и подсказкой «Другие сервисы этого приложения обращаются к нему по этому адресу — без интернета и без оплаты трафика.»
- Порт — 8080 для сервиса, собранного из репозитория, или Порт, который вы указали для сервиса из готового образа.
- Этот адрес доступен только сервисам того же приложения. Из интернета и из других приложений он не открывается.
- Адрес начинается с
http://. Соединение не выходит за пределы приложения, поэтому настраивать сертификат не нужно.
Переменные с адресами
Копировать адреса в код не нужно. Каждый сервис с кодом — веб-сервисы, фоновые процессы и задачи по расписанию — получает переменную с внутренним адресом каждого другого веб-сервиса приложения:
RIVERVO_<NAME>_URL=http://<name>:<port>
NAME — имя сервиса заглавными буквами, дефисы заменяются на _:
| Сервис | Переменная |
|---|---|
api | RIVERVO_API_URL=http://api:8080 |
admin-api | RIVERVO_ADMIN_API_URL=http://admin-api:8080 |
web | RIVERVO_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.
О токенах и командной строке — в статье Инструменты разработчика.