跳转到内容
getnextpdf.com

Enterprise 版本

计量 — 深度参考

NextPDF\Enterprise\Metering 命名空间提供编排级的用量计量,用于计费可见性与审计。其公共接口面为六个符号:MeterCollectorMeterEntryMeteringReporterMeteringBackendInterfacePrometheusMeteringBackendPrometheusPushgatewayException。收集器在内存中缓冲不可变条目,并按批次刷新。上报器将每个批次扇出到一个或多个后端,并带有逐后端的重试与故障隔离。计量是尽力而为且非致命的:一次计量后端中断会降低可观测性,绝不会影响文档处理。此数据流并非配额强制执行的权威来源。工作流层面的指南请参见 计量

此能力随 NextPDF Enterprisenextpdf/enterprise)发行,并通过 Enterprise 层级的许可证封套激活。缺少该授权的部署不会加载此能力的类。比较版本并获取许可证

计量是一项基础 Enterprise 能力,只要安装了 Enterprise 包即可用;没有单独的逐功能标记。NextPDF Core(Apache-2.0)与 NextPDF Pro 没有任何收集器、上报器或后端接口面;该契约仅随 nextpdf/enterprise 发行。

符号参数默认行为返回抛出或失败于说明
MeterCollector::__constructMeteringReporter $reporterint $bufferSize = 100创建一个带空内存缓冲的收集器新的 MeterCollector不抛出$bufferSize 记录为 positive-int
MeterCollector::recordstring $operationint $countstring $tenantIdstring $licenseIdint $pagesProcessed = 0float $durationMs = 0.0array $metadata = []追加一个以当前时间打上时间戳的不可变 MeterEntry;当缓冲达到 $bufferSize 时自动刷新void不抛出;自动刷新委托给上报器,而上报器绝不抛出时间戳在记录时刻取得
MeterCollector::flush将所有已缓冲条目交给上报器;空缓冲为无操作void不抛出;后端故障由上报器吸收缓冲在交接前被换出;可重入安全
MeterCollector::bufferCount返回已缓冲条目的数量int<0, max>不抛出用于诊断与背压决策
MeterCollector::registerShutdownFlush通过 register_shutdown_function 注册 flush()void不抛出在 PHP-FPM 部署中于引导时调用一次
MeterEntry::__constructstring $operationint $countDateTimeImmutable $timestampstring $tenantIdstring $licenseIdint $pagesProcessed = 0float $durationMs = 0.0array $metadata = []原样存储所提供的值新的 MeterEntry未声明 @throws;在 strict_types 下,参数类型不匹配时 PHP 会抛出 TypeErrorfinal readonly;全部八个提升属性均为 public
MeteringReporter::__constructlist<MeteringBackendInterface> $backendsint $maxRetries = 2LoggerInterface $logger = new NullLogger()校验并存储后端列表新的 MeteringReporter$backends 为空时抛出 InvalidArgumentException$maxRetries 计入每个后端的投递尝试总数
MeteringReporter::reportlist<MeterEntry> $entries独立地将该批次投递给每个后端,并带逐后端重试void不抛出;尝试用尽时以错误级别记录并丢弃该后端的批次空列表为无操作
MeteringBackendInterface::reportlist<MeterEntry> $entries将一个批次投递给后端void当后端不可达时抛出 RuntimeException各实现必须是幂等的(按 timestamp + operation + tenantId 去重)
MeteringBackendInterface::isHealthy可达性探针bool未声明 @throws仅用于诊断;上报器不据此进行门控
MeteringBackendInterface::backendName诊断用的后端名称non-empty-string未声明 @throws例如 "prometheus""billing-api""null"
PrometheusMeteringBackend::__constructClientInterface $httpClientRequestFactoryInterface $requestFactoryStreamFactoryInterface $streamFactorystring $pushgatewayUrlstring $jobName = 'nextpdf_metering'配置一个 Pushgateway 推送目标新的 PrometheusMeteringBackend不抛出注入 PSR-18 客户端与 PSR-17 工厂
PrometheusMeteringBackend::reportlist<MeterEntry> $entries按操作与租户序列聚合该批次,并将展示文本 POST 到 <pushgatewayUrl>/metrics/job/<jobName>void在非 2xx 状态或 PSR-18 传输失败时抛出 PrometheusPushgatewayException空列表为无操作
PrometheusMeteringBackend::isHealthy探测 Pushgateway 健康端点;仅在 HTTP 200 时为 truebool不抛出;任何失败返回 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(): void
public 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): void
public 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 {}

MeterEntry 的 public readonly 属性

属性类型含义
$operationnon-empty-string操作类型,例如 "parse""compress""embed""rag_query"
$countpositive-int所消耗的单位数
$timestampDateTimeImmutable操作发生的时刻;收集器在记录时刻打上时间戳
$tenantIdnon-empty-string租户标识符
$licenseIdnon-empty-string许可证标识符
$pagesProcessedint<0, max>已处理的 PDF 页数;非 PDF 操作为 0
$durationMsfloat操作时长(毫秒)
$metadataarray<string, mixed>自由格式的、操作特定的元数据
  • MeterCollector::record() 构造一个不可变的 MeterEntry,为其打上当前时间的时间戳,并将其追加到内存缓冲中。当缓冲达到 $bufferSize 个条目时,收集器会自动刷新。
  • flush() 是幂等且可重入安全的。空缓冲为无操作。在该批次被交给上报器之前,缓冲会被换出,因此一次可重入的刷新无法重复发送。
  • MeteringReporter 会以空后端列表拒绝构造。该 InvalidArgumentException 是收集器/上报器路径上唯一的异常。
  • MeteringReporter::report() 独立地将每个批次投递给每个后端。一个失败的后端绝不会阻止另一个后端接收同一批次。
  • $maxRetries 计入每个后端的投递尝试总数;默认值 2 意味着一次初始尝试加一次重试。每次失败的尝试都会记录一条警告,包含后端名称、尝试次数与条目数。
  • 当某个后端的最后一次尝试失败时,上报器会额外以错误级别记录被丢弃的条目数,然后继续。它绝不会从 report() 抛出,因此调用方不得从一次正常返回推断已投递。
  • 各后端必须是幂等的。接口契约要求按时间戳、操作与租户标识符进行去重。上报器本身不去重。
  • PrometheusMeteringBackend::report() 将该批次聚合为逐操作、逐租户的序列,并以 Content-Type text/plain; version=0.0.4 将 Prometheus 文本展示 POST 到 <pushgatewayUrl>/metrics/job/<jobName>。默认的 job 名称为 nextpdf_metering
  • 推送的载荷携带三个计数器——nextpdf_operations_totalnextpdf_pages_processed_totalnextpdf_operation_duration_ms_total——每个都按操作与租户打标签。
  • 此计量数据流是非权威的。配额强制执行与权威的计算计量消费的是该部署单独的权威用量数字,绝非此缓冲。编排计量中的一处缺口是一处可观测性缺口,而非一处计费正确性缺口。
  • 被重复或被重放的批次。 由后端幂等性吸收;上报器不去重。不要依赖恰好一次的投递。
  • 用尽的重试。 该后端的批次会被丢弃并以错误级别记录。report()flush() 的一次正常返回绝不意味着已投递。
  • 刷新前进程退出。 缓冲仅存于内存。一次崩溃,或一次未注册关闭处理器的退出,都会丢失已缓冲的条目。
  • 工作进程模型不匹配。 PHP-FPM 部署在引导时调用一次 registerShutdownFlush(),使剩余部分在请求结束时刷新。长时运行的工作进程(Octane、Symfony worker、队列 worker)则必须以周期定时器刷新;否则条目会累积,直到工作进程退出。
  • $bufferSize 小于 1 违反已记录的 positive-int 契约;可观察到的结果是每次 record() 调用都会刷新。
  • 敏感元数据。 $metadata 是自由格式的,且可能携带敏感的操作上下文。存储、留存与访问控制是后端运营方的责任。
  • Pushgateway 投递失败。 一次非 2xx 响应会抛出携带 HTTP 状态与响应体的 PrometheusPushgatewayException;一次 PSR-18 传输失败会被包裹为同一异常类型。上报器的重试与隔离循环会吸收两者。
  • 健康探针。 PrometheusMeteringBackend::isHealthy()<pushgatewayUrl>/-/healthy 发起一次 GET,仅在 HTTP 200 时返回 true。任何传输错误返回 false;该探针绝不抛出。
  • 恶意标签值。 操作或租户值中的反斜杠、双引号与换行符会在发出时被转义,因此一个标签值无法注入额外的展示行或破坏标签块。
  • FIPS 模式。 收集器与上报器不执行任何密码学操作,且没有任何 FIPS 特有的行为。一个在传输中签名或加密的后端,会继承其宿主密码学提供方的 FIPS 态势。

没有任何外部标准管辖进程内的收集器、上报器或后端契约;没有任何规范性规范可供引用,因此本页按设计不携带任何 RAG 引用。Prometheus 后端发出 Prometheus 文本展示格式,并以 Content-Type text/plain; version=0.0.4 推送;该格式是一种生态系统约定,而非 ISO 或 IETF 标准,此论断以产品源码为依据。NextPDF 不对此接口面作出任何符合性或认证声明。

  • 所有类都声明 strict_types=1 且为 finalMeterEntryfinal readonly,并带有提升的 public 属性。参数类型不匹配会在调用方引发 PHP TypeError
  • 该模块的类携带包级 @since 注解 2.1.0PrometheusPushgatewayException 携带 @since 3.2.0
  • 上报器的 logger 默认为 PSR-3 NullLogger。请在生产中注入一个真实的 logger,否则被丢弃的批次不会留下任何痕迹。
  • 单元测试:实现一个假的 MeteringBackendInterface,并直接构造 MeterEntry 值。Prometheus 后端接受 PSR-18/PSR-17 抽象,因此一个 mock HTTP 客户端可以离线演练完整的推送路径。
  • 建议的边界测试:缓冲恰好位于 $bufferSize、可重入刷新、空缓冲刷新、一个后端失败而第二个成功,以及重试用尽的日志记录。
  • 后端实现者在投递失败时抛出 RuntimeException(或其子类);上报器会吸收它。在上游添加进一步重试之前,请遵守幂等性要求。

本页仅记录可从外部观察到的行为与受支持的公共 API 接口面。内部命名空间路径、辅助类、机制表、runbook 文件名与工单前缀均不在范围内。