servicehost

package
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 10, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

Documentation

Overview

Package servicehost — НОСИТЕЛЬ входящего пути: одна точка входа, поднимающая оба слушателя сервиса с одним и тем же контуром работы с владельцем прав.

Что здесь механизм, а что правило

Serve возвращает `error`, а НЕ сервер, и регистрация получает `grpc.ServiceRegistrar` — интерфейс с единственным методом. Поэтому у вызывающего не остаётся объекта, к которому можно приделать своё звено: восьмой сервис, желающий собственный порядок цепочки, не «нарушит правило» — ему не на чем его записать. Это свойство ПОСТРОЕНИЯ.

Порядок звеньев внутри самой функции построением не держится — его держит то, что функция ОДНА и правка её видна как правка контура, а не как правка сервиса. Это честная граница, и выдавать её за невозможность нельзя.

Чем этот пакет отличается от `pkg/servicecontract`

Тот решает, ЧТО МОЖНО ЗАПИСАТЬ, и его отказы — свойства самого дескриптора (О1, О6, О7, О8, О10). Здесь живут отказы, которым нужен СЛУЖИМЫЙ НАБОР RPC и выведенный из него каталог прав: О2, О3, О4, О5, О9. Разделение не косметическое: половину отказов невозможно вычислить, пока не известно, что процесс реально служит.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsGatedMutation

func IsGatedMutation(fullMethod string) bool

IsGatedMutation сообщает, записывает ли этот метод намерение регистрации, — то есть подпадает ли он под загрузочный гейт.

Форма — `/<пакет>.<Служба>/Create`, кроме служб `Internal*`: админ-каталог (типы дисков, регионы, зоны) владельца не заводит и намерения регистрации не порождает, поэтому гейтить его значило бы закрыть админ-путь по причине, к нему не относящейся.

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

Сколько копий этого предиката в дереве СЕГОДНЯ — четыре, а не одна

Прежняя редакция этой шапки говорила «перенесён из трёх одинаковых копий, живших по одной в каждом сервисе», и это описывало НАМЕРЕНИЕ фазы, а не дерево: три копии стоят на месте (`services/{vpc,compute,nlb}/internal/fgaboot`), каждая дословно совпадает с этой. Здесь заведена ЧЕТВЁРТАЯ — канонная, — а три снимаются вместе с переводом своих сервисов на носитель, не раньше: снять их сейчас значило бы оставить сервисы без гейта до самого перевода. Читающий эту строку и решивший, что дублей нет, не пошёл бы их искать — поэтому счёт назван.

Предикат ЭКСПОРТИРОВАН ради второго читателя: сервис вправе спросить у носителя, есть ли у него вообще гейтируемая мутация, — и заявление «гейт мне не нужен» тогда судится тем же кодом, который гейт исполняет, а не своей копией предиката в пробе сервиса.

func Serve

func Serve(ctx context.Context, d servicecontract.Descriptor, public, internal Registrar) error

Serve поднимает ОБА слушателя и обслуживает их до отмены ctx.

Порядок, и он несущий: собрать серверы → зарегистрировать → снять СЛУЖИМЫЙ набор у самих серверов → вывести каталог и карту прав → прогнать отказы старта → и только потом слушать. Ни одно соединение не принимается раньше, чем отработали все десять отказов.

func ServeSurface

func ServeSurface(ctx context.Context, d servicecontract.SurfaceDescriptor) (func() error, error)

ServeSurface поднимает НЕ-gRPC поверхность: привязывает порт СИНХРОННО и возвращает ожидание, которое завершается ТОЛЬКО ПОСЛЕ ТОГО, как порт освобождён.

Почему привязка синхронна, а обслуживание — нет

Отказ привязки и остановка обслуживания — разные события, и вызывающему они нужны в разные моменты. Первое означает «поверхности не будет вовсе» и обязано прийти ДО того, как процесс объявит себя поднявшимся: занятый порт — это ошибка посадки, а не режим работы. Второе приходит на выходе и означает «порт свободен, можно завершаться».

Одна блокирующая функция это различие стирала: вызывающему приходилось звать её в горутине (иначе некому обслуживать gRPC) и читать исход в конце — то есть отказ привязки становился кодом возврата процесса, который до этого успешно проработал сколько угодно. Ровно этот дефект здесь и описан как прежний у плоскости данных реестра, и он воспроизвёлся у трёх сервисов сразу — потому что верное использование зависело от дисциплины вызывающего, а не следовало из сигнатуры.

Теперь неверное использование непредставимо: пока функция не вернулась, порт либо занят нами, либо назван в ошибке.

Профиль той же функции: что здесь то же, а что другое

То же: объявление приносится ДАННЫМИ (servicecontract.SurfaceDescriptor), отказы отработали до первого принятого соединения, вызывающему не остаётся объекта, к которому можно приделать своё. Другое: цепочки звеньев здесь нет — у HTTP-поверхности своя аутентификация внутри обработчика либо объявленное её отсутствие, и подмешивать сюда gRPC-цепочку было бы не унификацией, а прочтением намеренно узкой семантики за общий случай.

Почему возвращается ОЖИДАНИЕ, а не пара «запусти» + «погаси»

Пара возвращала вызывающему обязанность позвать гашение, и обязанность эта разошлась: из шести диагностических слушателей дерева двое гасились контекстом БЕЗ СРОКА (то есть ждали последний скрейп неограниченно), двое — сроком дренажа исполнителей операций, а плоскость данных реестра поднималась вовсе мимо синхронной привязки порта — ошибка занятого адреса там попадала в журнал, и процесс продолжал работать, объявляя себя исправным.

Здесь гашение — не обязанность вызывающего, а СВОЙСТВО функции: слушатель живёт ровно столько, сколько живёт ctx, и пережить его не может, потому что сервер вызывающему не отдан. Слушатель, переживший отмену, держит порт — и следующий старт того же процесса спотыкается о собственный предыдущий.

Возвращённое ожидание обязанностью гасить не является: гасит отмена ctx. Оно отвечает на один вопрос — «порт уже свободен?» — и потому идемпотентно.

Порядок гашения, и почему он именно такой

  1. отмена ctx;
  2. `Shutdown` со СВЕЖИМ контекстом на объявленный срок — свежим потому, что ctx уже отменён, и передать его сюда значило бы отменить гашение в тот же миг, в который оно началось (запросы в полёте оборвались бы);
  3. по истечении срока — `Close`: гашение не бывает бесконечным, и ждать дольше объявленного значит держать порт вопреки объявлению.

Возврат происходит после шага 3, а не после шага 1: вызывающий, дождавшийся этой функции, знает, что порт свободен.

Types

type Registrar

type Registrar func(grpc.ServiceRegistrar)

Registrar приносит ТОЛЬКО обработчики.

Тип не позволяет добавить звено — это и есть фиксированный слот. Аргумент — `grpc.ServiceRegistrar`, интерфейс с единственным методом `RegisterService`; сервера вызывающий не получает ни в каком виде.

Регистратор вызывается РОВНО ОДИН раз на слушатель. «Позовём дважды, он же чистый» контрактом не является: чистоту чужой функции проверить нечем, а побочный эффект во втором вызове проявился бы как удвоение фоновой работы.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL