Enterprise редакция
Metering — глубокий справочник
Пространство имён NextPDF\Enterprise\Metering предоставляет учёт потребления на уровне оркестрации для видимости биллинга и аудита. Публичная поверхность — шесть символов: MeterCollector, MeterEntry, MeteringReporter, MeteringBackendInterface, PrometheusMeteringBackend и PrometheusPushgatewayException. Сборщик буферизует неизменяемые записи в памяти и сбрасывает их пакетами. Репортёр веерно распределяет каждый пакет по одному или нескольким бэкендам с повторными попытками и изоляцией сбоев на каждый бэкенд. Учёт работает с наилучшим усилием и не фатален: сбой бэкенда учёта ухудшает наблюдаемость, но никогда не обработку документов. Этот поток не является авторитетным источником для навязывания квот. Руководство на уровне рабочего процесса см. в Metering.
Доступность и лицензирование
Заголовок раздела «Доступность и лицензирование»Эта возможность поставляется в NextPDF Enterprise (nextpdf/enterprise) и активируется лицензионным конвертом уровня Enterprise. Развёртывание без этого права не загружает классы возможности. Сравнить редакции и получить лицензию.
Учёт — это базовая возможность Enterprise, доступная, как только установлен пакет Enterprise; отдельного флага на каждую функцию нет. В NextPDF Core (Apache-2.0) и NextPDF Pro нет поверхности сборщика, репортёра или бэкенда; контракт поставляется только в nextpdf/enterprise.
Публичная поверхность API
Заголовок раздела «Публичная поверхность API»| Символ | Параметры | Поведение по умолчанию | Возвращает | Выбрасывает или завершается с | Примечания |
|---|---|---|---|---|---|
MeterCollector::__construct | MeteringReporter $reporter, int $bufferSize = 100 | Создаёт сборщик с пустым буфером в памяти | Новый MeterCollector | Не выбрасывает | $bufferSize документирован как positive-int |
MeterCollector::record | string $operation, int $count, string $tenantId, string $licenseId, int $pagesProcessed = 0, float $durationMs = 0.0, array $metadata = [] | Добавляет одну неизменяемую MeterEntry с отметкой текущего времени; автоматически сбрасывает, когда буфер достигает $bufferSize | void | Не выбрасывает; автосброс делегирует репортёру, который никогда не выбрасывает | Метка времени берётся в момент записи |
MeterCollector::flush | — | Передаёт все буферизованные записи репортёру; пустой буфер — no-op | void | Не выбрасывает; сбои бэкендов поглощаются репортёром | Буфер выгружается до передачи; безопасен при повторном входе |
MeterCollector::bufferCount | — | Возвращает число буферизованных записей | int<0, max> | Не выбрасывает | Диагностика и решения по обратному давлению |
MeterCollector::registerShutdownFlush | — | Регистрирует flush() через register_shutdown_function | void | Не выбрасывает | Вызовите один раз при загрузке в развёртываниях PHP-FPM |
MeterEntry::__construct | string $operation, int $count, DateTimeImmutable $timestamp, string $tenantId, string $licenseId, int $pagesProcessed = 0, float $durationMs = 0.0, array $metadata = [] | Сохраняет переданные значения без изменений | Новый MeterEntry | Нет объявленного @throws; PHP вызывает TypeError при несоответствии типов аргументов под strict_types | final readonly; все восемь продвинутых свойств публичны |
MeteringReporter::__construct | list<MeteringBackendInterface> $backends, int $maxRetries = 2, LoggerInterface $logger = new NullLogger() | Проверяет и сохраняет список бэкендов | Новый MeteringReporter | InvalidArgumentException, когда $backends пуст | $maxRetries считает общее число попыток доставки на каждый бэкенд |
MeteringReporter::report | list<MeterEntry> $entries | Доставляет пакет каждому бэкенду независимо, с повторами на каждый бэкенд | void | Не выбрасывает; исчерпанные попытки логируются на уровне ошибки и отбрасывают пакет этого бэкенда | Пустой список — no-op |
MeteringBackendInterface::report | list<MeterEntry> $entries | Доставляет пакет бэкенду | void | RuntimeException, когда бэкенд недостижим | Реализации ДОЛЖНЫ быть идемпотентны (дедупликация по timestamp + operation + tenantId) |
MeteringBackendInterface::isHealthy | — | Зонд достижимости | bool | Нет объявленного @throws | Только диагностика; репортёр не опирается на него |
MeteringBackendInterface::backendName | — | Диагностическое имя бэкенда | non-empty-string | Нет объявленного @throws | Например "prometheus", "billing-api", "null" |
PrometheusMeteringBackend::__construct | ClientInterface $httpClient, RequestFactoryInterface $requestFactory, StreamFactoryInterface $streamFactory, string $pushgatewayUrl, string $jobName = 'nextpdf_metering' | Настраивает push-цель Pushgateway | Новый PrometheusMeteringBackend | Не выбрасывает | Внедряются клиент PSR-18 и фабрики PSR-17 |
PrometheusMeteringBackend::report | list<MeterEntry> $entries | Агрегирует пакет по сериям «операция и арендатор» и отправляет POST с текстом экспозиции на <pushgatewayUrl>/metrics/job/<jobName> | void | PrometheusPushgatewayException при статусе не 2xx или сбое транспорта PSR-18 | Пустой список — no-op |
PrometheusMeteringBackend::isHealthy | — | Зондирует health-эндпойнт Pushgateway; true только при HTTP 200 | bool | Не выбрасывает; любой сбой возвращает false | GET-зонд только на чтение |
PrometheusMeteringBackend::backendName | — | Возвращает "prometheus" | non-empty-string | Не выбрасывает | Константа |
PrometheusPushgatewayException | — | Сигнализирует о неудачной доставке в Pushgateway | — | Является выбрасываемым | final; расширяет RuntimeException |
public function __construct( private readonly MeteringReporter $reporter, private readonly int $bufferSize = 100,) {}
public function record( string $operation, int $count, string $tenantId, string $licenseId, int $pagesProcessed = 0, float $durationMs = 0.0, array $metadata = [],): void
public function flush(): void
public function bufferCount(): int
public function registerShutdownFlush(): voidpublic function __construct( public string $operation, public int $count, public DateTimeImmutable $timestamp, public string $tenantId, public string $licenseId, public int $pagesProcessed = 0, public float $durationMs = 0.0, public array $metadata = [],) {}public function report(array $entries): void;
public function isHealthy(): bool;
public function backendName(): string;public function __construct( array $backends, private readonly int $maxRetries = 2, private readonly LoggerInterface $logger = new NullLogger(),)
public function report(array $entries): voidpublic function __construct( private readonly ClientInterface $httpClient, private readonly RequestFactoryInterface $requestFactory, private readonly StreamFactoryInterface $streamFactory, private readonly string $pushgatewayUrl, private readonly string $jobName = self::DEFAULT_JOB_NAME,) {}final class PrometheusPushgatewayException extends RuntimeException {}Публичные readonly-свойства MeterEntry
| Свойство | Тип | Значение |
|---|---|---|
$operation | non-empty-string | Тип операции, например "parse", "compress", "embed", "rag_query" |
$count | positive-int | Число потреблённых единиц |
$timestamp | DateTimeImmutable | Когда произошла операция; сборщик проставляет отметку в момент записи |
$tenantId | non-empty-string | Идентификатор арендатора |
$licenseId | non-empty-string | Идентификатор лицензии |
$pagesProcessed | int<0, max> | Обработанные страницы PDF; 0 для операций не с PDF |
$durationMs | float | Длительность операции в миллисекундах |
$metadata | array<string, mixed> | Произвольные метаданные, специфичные для операции |
Контракт поведения
Заголовок раздела «Контракт поведения»MeterCollector::record()создаёт одну неизменяемуюMeterEntry, проставляет отметку текущего времени и добавляет её в буфер в памяти. Когда буфер достигает$bufferSizeзаписей, сборщик автоматически сбрасывает.flush()идемпотентен и безопасен при повторном входе. Пустой буфер — no-op. Буфер выгружается до передачи пакета репортёру, так что повторный сброс не может отправить дважды.MeteringReporterотклоняет построение с пустым списком бэкендов. ЭтоInvalidArgumentException— единственное исключение на пути сборщика/репортёра.MeteringReporter::report()доставляет каждый пакет каждому бэкенду независимо. Сбойный бэкенд никогда не мешает другому бэкенду получить тот же пакет.$maxRetriesсчитает общее число попыток доставки на каждый бэкенд; значение по умолчанию2означает одну исходную попытку плюс один повтор. Каждая неудачная попытка логирует предупреждение с именем бэкенда, номером попытки и числом записей.- Когда финальная попытка для бэкенда проваливается, репортёр дополнительно логирует на уровне ошибки со счётчиком отброшенных записей и идёт дальше. Он никогда не выбрасывает из
report(), поэтому вызывающие не должны делать вывод о доставке из обычного возврата. - Бэкенды ДОЛЖНЫ быть идемпотентны. Контракт интерфейса требует дедупликации по ключу из метки времени, операции и идентификатора арендатора. Сам репортёр не дедуплицирует.
PrometheusMeteringBackend::report()агрегирует пакет в серии по операции и арендатору и отправляет POST с текстовой экспозицией Prometheus на<pushgatewayUrl>/metrics/job/<jobName>с Content-Typetext/plain; version=0.0.4. Имя задания по умолчанию —nextpdf_metering.- Отправленная полезная нагрузка несёт три счётчика —
nextpdf_operations_total,nextpdf_pages_processed_totalиnextpdf_operation_duration_ms_total— каждый с метками по операции и арендатору. - Этот поток учёта неавторитетен. Навязывание квот и авторитетный учёт вычислений потребляют отдельную авторитетную величину потребления развёртывания, а не этот буфер. Пробел в учёте оркестрации — это пробел наблюдаемости, а не пробел корректности биллинга.
Граничные случаи и режимы сбоя
Заголовок раздела «Граничные случаи и режимы сбоя»- Дублированный или воспроизведённый пакет. Поглощается идемпотентностью бэкенда; репортёр не дедуплицирует. Не полагайтесь на доставку строго один раз.
- Исчерпанные повторные попытки. Пакет для этого бэкенда отбрасывается и логируется на уровне ошибки. Обычный возврат из
report()илиflush()никогда не подразумевает доставку. - Выход процесса до сброса. Буфер существует только в памяти. Аварийное завершение или выход без зарегистрированного обработчика завершения теряет буферизованные записи.
- Несоответствие модели воркера. Развёртывания PHP-FPM вызывают
registerShutdownFlush()один раз при загрузке, чтобы остаток сбрасывался в конце запроса. Долгоживущие воркеры (Octane, воркер Symfony, воркер очереди) должны вместо этого сбрасывать по периодическому таймеру; иначе записи накапливаются до выхода процесса воркера. $bufferSizeменьше1. Нарушает документированный контрактpositive-int; наблюдаемый результат — сброс при каждом вызовеrecord().- Чувствительные метаданные.
$metadataпроизвольны по форме и могут нести чувствительный контекст операции. Хранение, удержание и контроль доступа — ответственность оператора бэкенда. - Сбой доставки в Pushgateway. Ответ не 2xx вызывает
PrometheusPushgatewayExceptionс HTTP-статусом и телом ответа; сбой транспорта PSR-18 оборачивается в тот же тип исключения. Цикл повторов и изоляции репортёра поглощает оба. - Зонд работоспособности.
PrometheusMeteringBackend::isHealthy()выполняет GET к<pushgatewayUrl>/-/healthyи возвращаетtrueтолько при HTTP 200. Любая ошибка транспорта возвращаетfalse; зонд никогда не выбрасывает. - Враждебные значения меток. Символы обратной косой черты, двойной кавычки и перевода строки в значениях операции или арендатора экранируются при выводе, так что значение метки не может внедрить дополнительные строки экспозиции или испортить блок меток.
- Режим FIPS. Сборщик и репортёр не выполняют криптографических операций и не имеют поведения, специфичного для FIPS. Бэкенд, который подписывает или шифрует при передаче, наследует FIPS-позицию криптопровайдера своего хоста.
Соответствие
Заголовок раздела «Соответствие»Никакой внешний стандарт не управляет контрактом внутрипроцессного сборщика, репортёра или бэкенда; нет нормативной спецификации для цитирования, поэтому эта страница по замыслу не несёт RAG-цитаты. Бэкенд Prometheus выдаёт текстовый формат экспозиции Prometheus и отправляет с Content-Type text/plain; version=0.0.4; этот формат — конвенция экосистемы, а не стандарт ISO или IETF, и утверждение основано на исходном коде продукта. NextPDF не делает никаких заявлений о соответствии или сертификации для этой поверхности.
Замечания по разработке
Заголовок раздела «Замечания по разработке»- Все классы объявляют
strict_types=1и являютсяfinal;MeterEntry—final readonlyс продвинутыми публичными свойствами. Несоответствие типов аргументов вызывает PHPTypeErrorв вызывающем коде. - Классы модуля несут пакетную аннотацию
@since2.1.0;PrometheusPushgatewayExceptionнесёт@since3.2.0. - Логгер репортёра по умолчанию — PSR-3
NullLogger. Внедрите реальный логгер в продакшене, иначе отброшенные пакеты не оставляют следа. - Модульное тестирование: реализуйте поддельный
MeteringBackendInterfaceи создавайте значенияMeterEntryнапрямую. Бэкенд Prometheus принимает абстракции PSR-18/PSR-17, так что мок HTTP-клиента прогоняет весь путь push офлайн. - Рекомендуемые граничные тесты: буфер ровно на
$bufferSize, повторный сброс, сброс пустого буфера, сбой одного бэкенда при успехе второго и логирование исчерпания повторов. - Реализаторы бэкендов выбрасывают
RuntimeException(или подкласс) при сбое доставки; репортёр его поглощает. Соблюдайте требование идемпотентности, прежде чем добавлять дополнительные повторы выше по потоку.
Граница публикации
Заголовок раздела «Граница публикации»Эта страница документирует только внешне наблюдаемое поведение и поддерживаемую публичную поверхность API. Внутренние пути пространств имён, вспомогательные классы, таблицы механизмов, имена файлов руководств по эксплуатации и префиксы тикетов вне области действия.
См. также
Заголовок раздела «См. также»- Metering — NextPDF Enterprise — страница возможности: рабочий процесс, конфигурация и проработанные примеры развёртывания.
- Billing — глубокий справочник — уровни планов, семантика превышения и лестница оповещений.
- SaaS — глубокий справочник — поверхность многоарендной оркестрации.
- Licensing — глубокий справочник — лицензионный конверт, который активирует возможности Enterprise.