news 2026/9/15 22:40:50

Quickwit 原生 OTLP 分布式追踪服务:gRPC 端点、Span 数据模型与配置实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Quickwit 原生 OTLP 分布式追踪服务:gRPC 端点、Span 数据模型与配置实战

Quickwit 原生 OTLP 分布式追踪服务:gRPC 端点、Span 数据模型与配置实战

【免费下载链接】quickwitCloud-native OSS search engine for observability项目地址: https://gitcode.com/GitHub_Trending/qu/quickwit

Quickwit 原生支持 OpenTelemetry Protocol(OTLP),开箱即用地提供了一个 gRPC 端点,用于接收来自 OpenTelemetry Collector 或应用内 exporter 上报的 Span 数据,并自动完成索引创建与写入。本文将以 docs/distributed-tracing/otel-service.md 为主线,结合仓库源码,系统讲解 OTLP 端点的启用/禁用方式、自定义索引路由、otel-traces-v0_*索引的完整 Doc Mapping,以及当前版本的已知限制,帮助你快速把 Quickwit 接入现有可观测性链路,构建云原生的分布式追踪后端。

OTLP 服务概览:Quickwit 如何接收 Span

分布式追踪用于跟踪一个请求在多个服务(前端、后端、数据库等)之间的流转过程,是排查性能瓶颈的利器。Quickwit 作为云原生的非结构化数据检索引擎,天然适合充当 Trace 后端。它原生实现了 OpenTelemetry Protocol (OTLP) 的 gRPC 端点,Span 可以来自:

  • 一个独立的OpenTelemetry Collector(通过 otlp exporter 转发);
  • 应用代码中的OTLP exporter(如 Python SDK 的OTLPSpanExporter)直连 Quickwit。

该端点默认启用。启用后,Quickwit 会启动一个 gRPC 服务等待接收 Span,并把数据索引到默认索引中;如果该索引不存在,Quickwit 会自动创建,无需人工干预。

从源码看,OTLP 服务的注册发生在 quickwit-serve/src/grpc.rs 中:服务启动时会根据services.otlp_traces_service_opt是否存在来决定是否挂载TraceServiceServer,并为其开启Gzip 与 Zstd 两种压缩编码支持,同时应用grpc_config.max_message_size限制消息大小。也就是说,OTLP gRPC 端点与 Quickwit 自身的 gRPC 服务共用同一端口(默认7281)与同一套消息体量控制。

需要注意的是,OTLP 端点只在启用了indexer服务的节点上生效。核心判断逻辑位于 quickwit-serve/src/lib.rs:

if node_config.is_service_enabled(QuickwitService::Indexer) && node_config.indexer_config.enable_otlp_endpoint { // ... 构建 OtlpGrpcLogsService / OtlpGrpcTracesService }

即“indexer 服务开启”且“enable_otlp_endpoint为 true”两个条件同时满足时,端点才会被创建。

启用与禁用 OTLP 端点

默认行为与禁用方式

OTLP 端点默认开启,其默认值来源于 quickwit-config/src/node_config/mod.rs 中的default_enable_otlp_endpoint():它读取环境变量QW_ENABLE_OTLP_ENDPOINT,未设置时默认为true

如果出于安全、资源或其他原因需要关闭该端点,官方文档提供了两种等价方式:

方式一:环境变量

QW_ENABLE_OTLP_ENDPOINT=false ./quickwit run

方式二:节点配置(node config)

# ... Indexer configuration ... indexer: enable_otlp_endpoint: false

该配置项在 docs/configuration/node-config.md 的 Indexer 配置表中也有登记(默认值标为false,表示仅在显式开启或依赖环境变量默认值的情况下启用;实际运行时默认行为以QW_ENABLE_OTLP_ENDPOINT为准)。完整示例参见 config/quickwit.yaml,其中indexer小节包含该参数及其它索引器参数:

indexer: enable_otlp_endpoint: true split_store_max_num_bytes: 100G split_store_max_num_splits: 1000 max_concurrent_split_uploads: 12

提示:在测试环境或 CI 中(启用testsuitefeature 时),源码将默认值强制为false,避免测试进程意外监听 OTLP 端口。

验证服务是否就绪

启动后,OpenTelemetry 生态的 exporter 将默认把 Span 上报到http://<quickwit-host>:7281(gRPC)。若希望 Quickwit 自产自销——把自己的内部追踪 Span 发送给自己——可以参考 docs/distributed-tracing/plug-quickwit-to-jaeger.md 的启动方式:

QW_ENABLE_OPENTELEMETRY_OTLP_EXPORTER=true \ OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:7281 \ ./quickwit run

这样 Quickwit 自身的 Span 也会进入 Trace 索引,进而可以在 Jaeger UI 中观测 Quickwit 的完整搜索链路。

将 Span 发送到自定义索引

默认情况下,Span 会被写入默认 Trace 索引。若希望把不同服务的 Trace 拆分到不同索引中,只需在 gRPC 请求的 metadata 中设置自定义 header:

qw-otel-traces-index: <目标索引 ID>

该 header 的解析逻辑位于 quickwit-opentelemetry/src/otlp/mod.rs 的extract_otel_index_id_from_metadata():从请求元数据中读取qw-otel-traces-index,若缺失则回退到默认索引 ID;同时会对索引 ID 做合法性校验,非法值会直接返回错误。

对于 HTTP 路径,Quickwit 还额外提供了两种 REST 风格的上报入口(实现在 quickwit-serve/src/otlp_api/rest_handler.rs):

路径说明
POST /otlp/v1/traces默认 Trace 索引,也可通过 headerqw-otel-traces-index指定目标索引
POST /{index}/otlp/v1/traces直接在 URL 路径中指定目标索引 ID
POST /otlp/v1/logs默认 Logs 索引(otel-logs-v0_*),header 为qw-otel-logs-index
POST /{index}/otlp/v1/logs在 URL 路径中指定 Logs 目标索引

注意:当前仓库源码中默认 Trace 索引常量为OTEL_TRACES_INDEX_ID = "otel-traces-v0_9"(定义于 quickwit-opentelemetry/src/otlp/traces.rs)。文档早期版本写作otel-trace-v0_7,属于版本演进过程中的命名差异;由于 Jaeger 集成使用通配符模式otel-traces-v0_*(见下文),新旧版本索引均可被检索到,无需担心向后兼容。

通过 OpenTelemetry Collector 转发

如果你已经有自己的 OpenTelemetry Collector,只需在其配置中增加一个指向 Quickwit 的 OTLP gRPC exporter(参考 docs/distributed-tracing/send-traces/using-otel-collector.md):

receivers: otlp: protocols: grpc: http: processors: batch: exporters: otlp/quickwit: endpoint: 127.0.0.1:7281 # macOS/Windows 上使用 host.docker.internal:7281 tls: insecure: true # 默认写入 otel-traces-v0_*;如需指定索引,取消注释: # headers: # qw-otel-traces-index: otel-traces-v0_9 service: pipelines: traces: receivers: [otlp] processors: [batch] exporters: [otlp/quickwit]

启动 Collector 后,可以用 cURL 向 Collector 的 HTTP 端口(4318)发送一条 JSON 格式的 Trace 做冒烟测试,随后在 Quickwit 日志中会看到new-split之类的索引器日志,表示 Span 已被接收并进入索引管线。

此外,Quickwit 的通用 source 也支持 OTLP 格式的输入(input_format: otlp_traces_proto/otlp_traces_json),例如可以从 Kafka 消费 OTLP Protobuf 编码的 Span,参考 config/tutorials/otel-traces/kafka-source.yaml:

version: 0.8 source_id: kafka-source source_type: kafka input_format: otlp_traces_proto params: topic: otlp_spans client_params: bootstrap.servers: localhost:9092

Trace 与 Span 数据模型

概念:Trace 与 Span

  • Trace:一组 Span 的集合,代表一次完整的请求链路(例如一次用户点击经过网关、服务 A、服务 B 与数据库)。
  • Span:Trace 中的单个操作单元,描述一次具体的调用(如一次 HTTP 处理、一次数据库查询)。

OpenTelemetry Collector 将 Span 批量上报给 Quickwit,Quickwit 把 OTLP Span 模型映射为索引文档写入otel-traces-v0_*索引。该模型派生自 OpenTelemetry Trace API 规范。

默认索引 Doc Mapping 详解

下面是otel-traces-v0_*索引的 Doc Mapping(基于 docs/distributed-tracing/otel-service.md,并对照源码内嵌模板OTEL_TRACES_INDEX_CONFIG,见 quickwit-opentelemetry/src/otlp/traces.rs 第 57–173 行):

version: 0.8 index_id: otel-traces-v0_9 doc_mapping: mode: strict field_mappings: - name: trace_id type: bytes input_format: hex output_format: hex fast: true - name: trace_state type: text indexed: false - name: service_name type: text tokenizer: raw fast: true - name: resource_attributes type: json tokenizer: raw - name: resource_dropped_attributes_count type: u64 indexed: false - name: scope_name type: text indexed: false - name: scope_version type: text indexed: false - name: scope_attributes type: json indexed: false - name: scope_dropped_attributes_count type: u64 indexed: false - name: span_id type: bytes input_format: hex output_format: hex - name: span_kind type: u64 - name: span_name type: text tokenizer: raw fast: true - name: span_fingerprint type: text tokenizer: raw - name: span_start_timestamp_nanos type: datetime input_formats: [unix_timestamp] output_format: unix_timestamp_nanos indexed: false fast: true fast_precision: milliseconds - name: span_end_timestamp_nanos type: datetime input_formats: [unix_timestamp] output_format: unix_timestamp_nanos indexed: false fast: false - name: span_duration_millis type: u64 indexed: false fast: true - name: span_attributes type: json tokenizer: raw fast: true - name: span_dropped_attributes_count type: u64 indexed: false - name: span_dropped_events_count type: u64 indexed: false - name: span_dropped_links_count type: u64 indexed: false - name: span_status type: json indexed: true - name: parent_span_id type: bytes input_format: hex output_format: hex indexed: false - name: is_root type: bool indexed: true stored: false - name: events type: array<json> tokenizer: raw fast: true - name: event_names type: array<text> tokenizer: default record: position stored: false - name: links type: array<json> tokenizer: raw timestamp_field: span_start_timestamp_nanos indexing_settings: commit_timeout_secs: 5 search_settings: default_search_fields: [service_name, span_name, event_names]

说明:is_root字段与commit_timeout_secs: 5default_search_fields取自当前源码中的内嵌模板;旧版文档中的 Mapping 未包含is_root,且commit_timeout_secs为 10。如果你在自建索引时参考本文,请以当前版本的实际模板为准。仓库中另有一份更早期的示例配置 config/tutorials/otel-traces/index-config.yaml(使用span_start_timestamp_secs作时间戳字段、partition_key: service_name),可作为自定义 Trace 索引的对照参考。

关键字段解析

标识与关联字段

  • trace_id/span_id/parent_span_id:以十六进制字节存储,是链路追踪的核心关联键。trace_id开启了fast字段用于加速按 trace 聚合查询。
  • span_kind:Span 类型(u64编码)。源码SpanKind支持 6 种取值:0=unspecified1=internal2=server3=client4=producer5=consumer
  • service_name:服务名,采用raw分词器并开启fast,是 Jaeger UI 中“按服务过滤”的关键字段。它来源于 OTLP Resource 上的service.name属性;缺失时默认取unknown_service(见源码UNKNOWN_SERVICE常量)。
  • span_name:Span 名称,raw分词 +fast

时间与耗时字段

  • span_start_timestamp_nanosdatetime类型,输入为unix_timestamp,输出为纳秒;同时是索引的timestamp_field,所有按时间范围过滤的 Trace 查询都会命中该字段。开启fastfast_precision: milliseconds,在毫秒级精度上压缩存储以节省空间。
  • span_end_timestamp_nanos:结束时间,只做存储不做索引。
  • span_duration_millis:Span 耗时毫秒数,由源码根据end_time_unix_nano - start_time_unix_nano换算得到(span_duration_nanos / 1_000_000),开启fast便于排序与耗时分析。

属性与诊断字段

  • resource_attributes/span_attributes:Resource 级与 Span 级属性,均以json类型存储(tokenizer: raw)。源码通过extract_attributes()将 OTLP 的KeyValue列表转换为 JSON 对象,支持字符串、布尔、整数、浮点、数组与嵌套 KV 结构;OTLP 的 bytes 类型暂不支持,会被忽略并告警。
  • span_status:状态对象(含code与可选message),indexed: true,可支持按状态码检索。
  • span_fingerprint:由“服务名 + Span 类型 + Span 名称”三段拼接而成的签名(SpanFingerprint::new),Jaeger 的 service/operation 下拉列表与按操作聚合即依赖该字段。
  • events/event_names/links:Span 事件与链路链接。event_names单独抽取为array<text>并使用default分词器 +record: position,支持对事件名做全文检索。
  • dropped_*系列字段:记录 OTLP 上报时因超限被丢弃的属性/事件/链接数量,便于评估数据完整性。
  • is_root:布尔字段,由源码根据parent_span_id是否为空推导(is_root: Some(parent_span_id.is_none())),用于快速筛选根 Span。

索引文档的生成链路

从源码(quickwit-opentelemetry/src/otlp/traces.rs)可以还原一条 Span 从 gRPC 到索引的完整链路:

  1. TraceService::export()接收ExportTraceServiceRequest,先从 metadata 中解析目标索引 ID;
  2. parse_otlp_spans()遍历resource_spans → scope_spans → spans,为每个 Span 执行Span::from_otlp(),把 OTLP 的ResourceInstrumentationScopeSpan三种模型扁平化为一个 JSON 文档;
  3. parse_spans()使用JsonDocBatchV2Builder将文档组装为DocBatchV2,交给ingest_doc_batch_v2()路由到 Quickwit 的 ingest 管道(INGEST_V2_SOURCE_ID);
  4. 请求处理中统计INGESTED_SPANS_TOTALINGESTED_BYTES_TOTALREQUESTS_TOTAL等指标,并在响应中通过ExportTracePartialSuccess上报被拒绝的 Span 数量与错误信息。

对应地,仓库中的单元测试(同文件的tests模块)与 quickwit-serve/src/otlp_api/rest_handler.rs 的集成测试覆盖了:默认端点、gzip 压缩、header 指定索引、路径指定索引等多种场景,可作为自建 Trace 索引时的行为参考。

结合 Jaeger UI 可视化 Trace

Quickwit 实现了与 Jaeger gRPC API 兼容的 SpanReader 服务(见 quickwit-jaeger/src/lib.rs),因此可以直接用 Jaeger UI 查询 Quickwit 中存储的 Trace。Jaeger 的查询会作用于所有匹配otel-traces-v0_*模式的索引——这正是上文所说索引命名需要遵循该模式的原因。

启动 Jaeger Query 时指定存储类型为grpc(Jaeger 1.58 之前为grpc-plugin):

# Linux 下使用 host 网络模式 docker run --rm --name jaeger-qw --network=host \ -e SPAN_STORAGE_TYPE=grpc \ -e GRPC_STORAGE_SERVER=127.0.0.1:7281 \ -p 16686:16686 \ jaegertracing/jaeger-query:1.60

随后打开http://localhost:16686即可按服务、操作、时间范围检索 Trace。完整的分布式追踪概览如下图所示(来自 docs/distributed-tracing/overview.md):

更多端到端细节(包括在 Jaeger UI 中观察 Quickwit 自身find_tracesroot_searchleaf_search等内部调用链)可阅读 docs/distributed-tracing/plug-quickwit-to-jaeger.md,应用侧的上报方式可参考 docs/distributed-tracing/send-traces/using-otel-collector.md 与 docs/distributed-tracing/send-traces/using-otel-sdk-python.md。

已知限制与注意事项

基于当前文档与源码,在使用 Quickwit 作为分布式追踪后端时有以下限制需要提前评估:

  • OTLP gRPC 服务不提供高持久性(High Durability):文档明确指出该问题计划在后续版本修复。如果你的追踪数据要求严格的持久化保证,需要结合其他数据通道(如 Kafka source)做灾备设计。
  • OTLP HTTP 仅支持 Binary Protobuf 编码:通过 REST 入口上报时,Content-Type必须为application/x-protobuf(见 quickwit-serve/src/otlp_api/rest_handler.rs 中warp::header::exact_ignore_case("content-type", "application/x-protobuf")的强制校验);OTLP HTTP 的 JSON 编码尚不支持,若确有需求应关注上游 issue。
  • 索引版本命名随版本演进:文档中的otel-trace-v0_7与当前源码的otel-traces-v0_9存在差异,实际使用时应以当前版本常量为准,并保持 Jaeger 查询模式otel-traces-v0_*的一致。

总体而言,Quickwit 的 OTLP 服务让“应用 → Collector / SDK → Quickwit 索引 → Jaeger UI 可视化”这条链路可以零成本打通:默认索引自动创建、gRPC/HTTP 双入口、按 header 或路径路由自定义索引,配合otel-traces-v0_*的 Jaeger 集成模式,即可快速搭建一套云原生的分布式追踪后端。

【免费下载链接】quickwitCloud-native OSS search engine for observability项目地址: https://gitcode.com/GitHub_Trending/qu/quickwit

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 22:39:10

男人女人晚上做那事网站从零搭建避坑指南

男人女人晚上做那事网站从零搭建避坑指南 找建站公司最头疼的不是技术难,而是怕被坑高价。很多运营同行拿着预算去询价,对方一句“包含所有功能”,最后结算单出来才发现,光是基础架构就比市场价贵出三倍。这种信息差,在成人内容相关的 男人女人晚上做那事网站…

作者头像 李华
网站建设 2026/9/15 22:38:11

无人机怎么选?从需求分析到核心硬件与进阶玩法全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 22:34:58

开源UDS/ISO-TP刷写日志离线分析工具:从CAN报文到故障定位

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/15 22:34:36

告别nvm/pyenv,mise统一管理Node、Python和JDK

如果你的电脑上装着 nvm 管 Node、pyenv 管 Python&#xff0c;还要再配一个 jenv 或 sdkman 管 JDK&#xff0c;那你大概率经历过这样的瞬间&#xff1a;前端项目要切 Node 18&#xff0c;后端服务要 Python 3.10&#xff0c;中间还夹了个老系统只认 JDK 8。每进一个目录&…

作者头像 李华
网站建设 2026/9/15 22:33:51

Spring Boot 前后端分离实战:家乡特色推荐系统源码解析

简介&#xff1a;这是一套基于Java与Spring Boot框架开发的家乡特色推荐系统源码&#xff0c;面向Java Web方向初学者、课程设计及毕业设计人群&#xff0c;用于搭建一个支持家乡特色文章分类浏览、在线分享与管理维护的完整网站应用。压缩包含784个文件&#xff0c;大小约19.6…

作者头像 李华