在前两个章节分别介绍了Query Frontend组件如何使用Middleware以及各Middleware的执行顺序和完成的工作。在完成所有的Middleware的处理函数最后是Middleware.Wrap(next),这个next的真面目是什么呢?本节将通过next的定义介绍Query Frontend组件发起Grpc请求至Querier的过程。
真正的next
在最开始讲解Interface接口时,提到了MiddleWare实现Wrap方法从而实现了该接口。在前面讲解的各类MiddleWare的实现可以看到,每一层MiddleWare的Wrap函数的next参数是下一个MiddleWare,一层一层的传下去。
那么最后的next是谁呢?
在new QueryFrontend可以看到traceByIDhandler
最后Wrap的next是由New方法传入。
next由在initQueryFrontend()
函数中的InitFrontend生成而来。
InitFrontend
在InitFrontend中,首先是new了一个fr *v1.Frontend
对象,将fr通过transport.AdaptGrpcRoundTripperToHTTPRoundTripper(fr)
集成后为我们一直在找的next。
我们再看看AdaptGrpcRoundTripperToHTTPRoundTripper
的定义,其handler函数是先将http请求转化为grpc请求,然后调用封装的fr.RoundTripGRPC()
。
所以到这里还没有完,还没真正的找到最后的next,我们再来看看fr *v1.Frontend
的定义。
fr*v1.Frontend
我们先直奔RoundTripGRPC()
,可以看到这里真的就是next最终的执行了。但是这里还是没有向Querier发起请求的逻辑。
在RoundTripGRPC里将req封装为request后,将request写入维护的queueRequest
。
封装的request中包含一个长度为1的chan *httpgrpc.HTTPResponse
类型的response,通过从request.response的channel中读取到值确认请求的返回。
request中也包含一个err channel
的定义。在后续的处理过程中有异常的产生将通过这个channel返回。
这两个channel读取到任意一个此次查询都将结束。
RequestQueue
既然将请求写入到queue,很容易联想到RequestQueue是用来做向后端发起请求限流的,那么RequestQueue是如何限流的呢?这里有两个重要参数maxOutstandingPerTenant int
、forgetDelay time.Duration
。
maxOutstandingPerTenant
来自配置max_outstanding_per_tenant
(默认2000)
QuerierForgetDelay
来自配置querier_forget_delay
(默认为0)
在向队列写入请求时,首先会将根据租户ID获取queue,若不存在这个租户ID的queue,则创建一个包含该长度大小的channel的queue。

forgetDisconnectedQueriers
在NewRequestQueue时,会启动一个serive,该service每5秒将执行一次q.forgetDisconnectedQueriers
对queue进行检查。
如果配置了querier_forget_delay
在forgetDisconnectedQueriers()
中,会对超过这个时间的请求从队列中清理掉。通过函数名称直译也可以了解其含义,忘记已经断开连接的请求。
Frontend.Process
在process中会不断地从queue中读取请求,并完成真正的grpc请求返回请求的响应。
根据配置max_batch_size
(默认值5),一次从队列中获取批量处理的请求。
将请求放入至reqBatch
并发执行reqBatch中的请求
最终将每个请求的结果写入response channel,完成请求返回。
总结
到这里,完成了一次请求进入QueryFrontend组件后完整的处理流程,我们再对这个流程进行一个总结:
HTTPAuthMiddleware:获取租户信息
httpGzipMiddleware:对请求body进行gzip解压
进入handler.ServeHTTP,所有请求的汇集处,打印请求的日志。
newDeduper:对查询返回的Span作去重、相同spanID的前后端服务父节点关系整理。
newTraceByIDSharder:对请求拆分成若干个小的请求后并发的进行查询后端(Querier)
newHedgedRequest:对请求的统计,提供监控指标。
retryWare:对子请求出现异常后,进行重试
grpcRoundTripperAdapter:将子请求从HTTP转化为Grpc协议请求
Frontend.RoundTrip: 对请求进行控制限流等逻辑处理的同时批量完成Grpc请求,得到返回的Trace数据。
以上就是QueryFrontend组件经过多层中间件的缠绕后完成的查询过程所涉及的函数和中间件的完整过程。下一节将开始阅读Querier组件的源码,了解请求到达Querier组件后其处理过程。
本系列回顾:
Grafana Tempo源码解读(九)Query Frontend组件的各MiddleWare加载顺序和作用
Grafana Tempo源码解读(八)Query Frontend组件的MiddleWare使用解读
Grafana Tempo源码解读(七)Compactor将Block进行压缩和删除的过程
Grafana Tempo源码解读(六)Compactor的代码结构和同步Block过程
Grafana Tempo源码解读(五)总结Tempo接收数据的限制以及配置调整
Grafana Tempo源码解读(四)Ingester组件将数据写入持久化存储
Grafana Tempo源码解读(三)Ingester组件接收Trace数据的过程
Grafana Tempo源码解读(二)Distributor对Trace数据的处理和发送至Ingester
Grafana Tempo源码解读(一)Distributor建立监听接收Trace数据