本文共 10546 字,大约阅读时间需要 35 分钟。
本文档包含两个部分,在第一部分中列出了实现OpenTelemetry Metric SDK的默认需求。实现者需要根据这些规则来实现OpenTelemetry API。
在第二部分中,以OpenTelemetry-Go Metric SDK为例描述了SDK模型的架构细节,给出所需要实现的内容,以此作为对实现者的指导,而不需要强制跨语言精确地去复制这种模型体系结构。
SDK实现者在实现OpenTelemetry API时应该遵守该语言的最佳实践和运行时环境。实现者应该遵循 给出的有关安全和性能的规定。
Metrics SDK提供了一种Metrics API的实现,包含如下术语(本文后续将直接采用如下术语,不作翻译):
这些术语用于描述API和SDK的边界,但它们都是API级别的结构。我们可以使用API级别的术语来描述SDK和API之间的边界,但从API的角度看,SDK是不透明的,且没有结构。
本文给出了默认OpenTelemetry SDK的主要组件的内部结构,通过术语来解释每个组件在将metric数据从输入(API级别的事件)导出到输出(metric呈现格式)中所扮演的角色。我们使用Export Pipeline来描述SDK级别的功能。
export pipeline中有三个数据流会经过的主要组件,按顺序为:
Controller 组件在export pipeline中协调Accumulator、Processor和Exporter。
Metrics API规范定义了如下术语:
定义了如下术语:
下面为架构中重要的数据类型:
术语SDK instrument 指Instrument的底层实现。
从外部看, Metrics SDK 实现了Meter
和MeterProvider
接口,从内部看, Metrics SDK为每个metric数据封装了一个export pipeline,包含四个重要组件。
Accumulator组件是将metric event并发地传递给Aggregator的地方,这是负责SDK性能的组件。Accumulator 负责bound 和unbound Instrument,更新和同步拷贝Aggregator 状态,调用Observer Instrument以及唤醒采集周期。
Processor 组件是exporter pipeline中可定制的组件。Processor 负责通过一个独立的AggregationSelector
接口为特定的Instrument选择Aggregators ,用于减少维数,以及用于在DELTA和CUMULATIVE数据表示之间进行转换。Processor 接口支持任意协议独立的数据转换,且可以将Processor连接在一起,形成更复杂的export pipelines。
Exporter 组件将处理的数据转换为特定的协议,并将其转发到某处。根据 ,exporter应该包含最小的功能,定制时最好通过Processors来表示。
Controller 组件协调在一个采集间隔内的采集动作,处理和导出采集间隔内的metric数据,确保对export pipeline的访问是同步的。
下面列出了针对OpenTelemetry SDK主要组件的要求。
上面展示了一个抽象的数据流图,将本文档使用的标准组件名与数据路径做了映射。上图并非要说明SDK的整体架构,仅命名了一个export pipeline的过程,并将组件放到了上下文中。
SDK要求这些组件使用标准名称,这样做有助于在OpenTelemetry中构建一致性。每个SDK都应该包含这些组件,以及下面列出的接口,尽管每个SDK的实际组织可能会因可用的库和源语言的性能特征而有所不同。例如,一个SDK可能为每个instrument实现了一个Accumulator,或者可以为每个采集周期使用一个Accumulator(假设支持多采集周期)。
SDK封装了OpenTelemetry Metric export pipeline,实现了,并管理SDK instrument,Resource和Instrumentation Library 元数据。
该方法为provider提供了一种清理环境的方法。
每个MeterProvider实例只能调用一次Shutdown
,在调用Shutdown
之后,将不允许获得Meter
。对于这些调用,SDK应该返回一个有效的无操作Meter。
Shutdown
应该提供一种方式来让调用者知道是否成功,失败或超时。
当出现超时时,Shutdown
应该结束或中止。Shutdown
可以被实现为一个阻塞API,或异步API,通过回调或事件通知调用者。语言库作者可以决定是否配置shutdown的超时时间。
OpenTelemetry SDK负责确保单个Meter的实现不会报告具有相同名称但不同定义的多个Instrument。为了实现该要求,如果一个Meter已经注册了一个相同名称的metric,则SDK必须拒绝在该Meter重复注册该Instrument。该要求甚至适用于尝试注册一个具有相同定义的Instrument。我们假设单个instrumentation library可以使用单个Instrument定义,而不是依赖SDK的重复注册。
不同的Meter具有不同的instrumentation library名称,允许在不同的instrumentation library中注册相同名称的Instrument,这种情况下,SDK必须将它们认为是不同的Instruments。
SDK负责实现API规范中包含的metric名称的语法要求。
TODO: Add functional requirements for RecordBatch().
SDK负责实现Collect()
函数,该函数会调用一个或多个Accumulators。本规范刻意避免在SDK和Accumulator之间建立特定关系;还有一个实现细节,即一个SDK是否会维护一个Accumulator,或每个Instrument对应一个Accumulator,或介于两者之间的某些配置。
SDK Collect()
函数必须通过活动的synchronous instruments以及所有注册的asynchronous instruments的Accumulations来调用Processor。
SDK必须允许在评估asynchronous instrument回调期间使用synchronous metric instruments。但通过asynchronous instrument回调来使用synchronous instruments是有副作用的,这种情况下,SDK应该在处理asynchronous instrument之后再处理synchronous instruments,这样synchronous measurements会作为asynchronous observations采集间隔的一部分进行处理。
下图展示了API和Accumulator之间的关系,以及synchronous instruments的细节。
对于一个synchronous instrument,Accumulator会:
Accumulator必须提供选项来将一个与它生成的Accumulations进行关联。
Synchronous metric instruments可以同步使用,除非源语言不支持该特性。SDK Accumulator 组件应该注意同步产生的性能压力。
Accumulator可能会使用排他锁定来维护synchronous instruments的更新。在调用Aggregator 时,Accumulator不应该持有排他锁,因为Aggregators可能具有更高的同步期望。
Accumulator必须实现Collect方法来为活动的instruments构建和处理当前的Accumulation值,即在上一次采集之后会更新这些值。Collect方法必须调用Processor来处理对应调用之前的所有metric events的Accumulations。
必须在Collect期间使用Aggregator上的同步移动操作来计算Accumulations。该操作会同步拷贝当前的Aggregator,并将其重置为0状态,这样Aggregator会在当前采集周期处理结束之后,下次采集周期开始时立即开始累加事件。一个Accumulation 定义为同步拷贝的Aggregator与Label Set, Resource, 和metric Descriptor的组合。
TODO: Are there more Accumulator functional requirements?
TODO Processor functional requirements
TODO Controller functional requirements
TODO Aggregator functional requirements
如果可能的话,Sum Aggregator应该使用原子操作。
本模型实现基于,本节为实现者提供指南。
为了构造一个Accumulator,需要提供Processor和options
// NewAccumulator constructs a new Accumulator for the given// Processor and options.func NewAccumulator(processor export.Processor, opts ...Option) *Accumulator// WithResource sets the Resource configuration option of a Config.func WithResource(res *resource.Resource) Option
Controller会使用Collect()
方法来调用Accumulator,对采集进行协调。
// Collect traverses the list of active instruments and exports// data. Collect() may not be called concurrently.//// During the collection pass, the Processor will receive// one Process(Accumulation) call per current aggregation.//// Returns the number of accumulations that were exported.func (m *Accumulator) Collect(ctx context.Context) int
该接口位于SDK和API的边界处,包含3个方法:
// MeterImpl is the interface an SDK must implement to supply a Meter// implementation.type MeterImpl interface { // RecordBatch atomically records a batch of measurements. RecordBatch(context.Context, []label.KeyValue, ...Measurement) // NewSyncInstrument returns a newly constructed // synchronous instrument implementation or an error, should // one occur. NewSyncInstrument(descriptor Descriptor) (SyncImpl, error) // NewAsyncInstrument returns a newly constructed // asynchronous instrument implementation or an error, should // one occur. NewAsyncInstrument( descriptor Descriptor, runner AsyncRunner, ) (AsyncImpl, error)}
这些方法覆盖了实现OpenTelemetry Metric API所需的所有入口。
是一个直接由Accumulator实现的用户级别的API。另外两个instrument构造器用于创建同步和异步SDK instruments。
用户通常对感兴趣,该接口通过获得。可以通过封装SDK Meter
实现来构建Meter
接口。
// WrapMeterImpl constructs a named `Meter` implementation from a// `MeterImpl` implementation. The `instrumentationName` is the// name of the instrumentation library.func WrapMeterImpl(impl MeterImpl, instrumentationName string, opts ...MeterOption) Meter
该方法的选项:
instrument注册:
// NewUniqueInstrumentMeterImpl returns a wrapped metric.MeterImpl with// the addition of uniqueness checking.func NewUniqueInstrumentMeterImpl(impl metric.MeterImpl) metric.MeterImpl {
API Descriptor
方法根据API级别的构造定义了instrument ,包括名称,instrument类型,描述和度量单位。在实现SDK时必须提供方式来访问传递给constructor的Descriptor。
// InstrumentImpl is a common interface for synchronous and// asynchronous instruments.type InstrumentImpl interface { // Descriptor returns a copy of the instrument's Descriptor. Descriptor() Descriptor}
支持直接和绑定调用规范。
// SyncImpl is the implementation-level interface to a generic// synchronous instrument (e.g., ValueRecorder and Counter instruments).type SyncImpl interface { // InstrumentImpl provides Descriptor(). InstrumentImpl // Bind creates an implementation-level bound instrument, // binding a label set with this instrument implementation. Bind(labels []label.KeyValue) BoundSyncImpl // RecordOne captures a single synchronous metric event. RecordOne(ctx context.Context, number Number, labels []label.KeyValue)}// BoundSyncImpl is the implementation-level interface to a// generic bound synchronous instrumenttype BoundSyncImpl interface { // RecordOne captures a single synchronous metric event. RecordOne(ctx context.Context, number Number) // Unbind frees the resources associated with this bound instrument. Unbind()}
支持单观察者和匹配观测者调用规范。用于执行Observer回调的接口会传递给constructor,这样asynchronous instruments就不需要其他API级别的访问方法。
// AsyncImpl is an implementation-level interface to an// asynchronous instrument (e.g., Observer instruments).type AsyncImpl interface { // InstrumentImpl provides Descriptor(). InstrumentImpl}
传递给asynchronous SDK instrument constructor 的异步"runner"接口(AsyncRunner)同时支持和调用规范。
TODO: define AggregatorSelector, Aggregator, Accumulation, ExportKind, ExportKindSelector, Aggregation, AggregationKind ExportRecord, ExportRecordSet
TODO: define the Processor interface
TODO: define how ExportKind conversion works (delta->cumulative required, cumulative->delta optional), Memory option (to not forget prior collection state).
TODO: Label filter, LabelFilterSelector
TODO: Push, Pull
TODO: Sum, LastValue, Histogram, MinMaxSumCount, Exact, and Sketch.
TODO: The default SDK behavior for ValueRecorder
instruments is still in question. Options are: LastValue, Histogram, MinMaxSumCount, and Sketch.
TODO: T.B.D.: DDSketch considered a good choice for ValueRecorder instrument default aggregation.
转载地址:http://mlakz.baihongyu.com/