Impala主要RPC实现及执行过程浅析(以DataStreamService为例)

impala的rpc类型

impala在CDH 5.15.0(2.12.0社区版)中正式引入了kudu rpc框架,逐渐将主要使用的rpc替换为基于protobuf的krpc实现,在此之前,impala使用的是thrift框架,thrift框架的性能虽然较好,但稳定性较差,随着并发数上升,查询成功率会逐渐下降。
在这里插入图片描述
在这里插入图片描述

impala主要使用的rpc

admission-control(admission服务专用)

AdmitQuery:调度下发查询
GetQueryStatus:coordinator获取查询调度状态
ReleaseQuery:查询结束后,释放准入控制相关资源
ReleaseQueryBackends:某个be执行完成查询相关fragment后,释放此be上该fragment执行所占用的资源
CancelAdmission:在GetQueryStatus返回之前,取消查询调度
AdmissionHeartbeat:admission服务和coordinator之间的心跳rpc

control-service

ExecQueryFInstances:coordinator调用后,be(其他executor)异步地按照执行计划分片fragment开始执行查询
ReportExecStatus:向coordinator汇报be执行状态
CancelQueryFInstances:取消一个特定查询的fragment在be的执行
RemoteShutdown:shutdown特定be

data-stream-service

TransmitData:be之间传输数据
EndDataStream:数据传输结束后,调用此rpc结束传输
UpdateFilter:更新特定fragment的bloom filter
PublishFilter:广播全局bloom filter

impala rpc实现及执行过程(以DataStreamService为例)

通过protobuf描述文件,将rpc的请求体、响应体、请求参数结构体、rpc服务定义出来;
在protoc-gen-krpc.cc中继承了protobuf的CodeGenerator类,通过其中定义好的Generate函数,利用名称通配符,将定义好的服务、rpc、其他参数等等转化为临时cc和h文件,并通过预先在指定目录定义好的CMakeLists.txt对其进行编译;
最终生成的rpc服务相关代码文件格式为{protobuf_service_name}.service.h/cc、{protobuf_service_name}.pb.h/cc、{protobuf_service_name}.proxy.h/cc,其中各个rpc服务的类命名为{rpc_service_name}ServiceIf,impala中对于各个rpc的处理逻辑则继承此类再进行实现,如DataStreamService相关实现(省略注释和部分函数声明):

// EndDataStream和TransmitData是在data_stream_service.proto中定义的rpc
class DataStreamService : public DataStreamServiceIf {
 public:
  DataStreamService(MetricGroup* metric_group);
  virtual void EndDataStream(const EndDataStreamRequestPB* request,
      EndDataStreamResponsePB* response, kudu::rpc::RpcContext* context);
  virtual void TransmitData(const TransmitDataRequestPB* request,
      TransmitDataResponsePB* response, kudu::rpc::RpcContext* context);
};

rpc逻辑均定义完成后,impala通过KrpcDataStreamMgr、KrpcDataStreamRecvr和KrpcDataStreamSender类进行管理,在KrpcDataStreamMgr中定义了多个函数实现数据传输的相关逻辑,以下简要分析DataStreamService中TransmitData和EndDataStream的执行过程。
SQL的每个fragment的执行过程中,首先会调用GetNext获取当前fragment需要的RowBatch,随后会根据自身处于的执行计划节点类型调用不同的DataSink来处理RowBatch,当需要进行节点间数据传输时,如ExchangeNode节点,会通过KrpcDataStreamSender类中继承的Send函数进行数据传输的处理。DataSink共有5种(定义于DataSink.thrift):

enum TDataSinkType {
  DATA_STREAM_SINK = 0         //管理网络数据流,用于节点间数据交换
  TABLE_SINK = 1               //一般用于Hdfs、Kudu、Hbase等类型表的DML数据文件写入
  HASH_JOIN_BUILDER = 2        //用于Join节点的执行,如runtime filter数据传输
  PLAN_ROOT_SINK = 3           //用于管理coordinator节点的数据接收,即执行计划树的根节点数据流
  NESTED_LOOP_JOIN_BUILDER = 4 //用于退化的Join节点执行
}

fragment在初始化时,会构造KrpcDataStreamSender对象,构造时会根据执行计划树来确定需要建立多少个与其他be进行通信的Channel,后续的数据传输通过Channel进行。
KrpcDataStreamSender首先将传入的RowBatch进行序列化,随后遍历所有构造好的Channel,调用Channel::TransmitData将序列化过的RowBatch通过注册过的TransmitData rpc发送到下游be。在rpc调用完成后,会自动调用TransmitDataCompleteCb的回调,用来收集rpc执行的metrics,如图中类似报错来源于此:
在这里插入图片描述
总体执行逻辑大概如图所示(省略部分调用):
在这里插入图片描述
Impala数据传输协议中的Sender和Recvr是多对多的关系,一个Sender可能有多个Recvr(如Broadcast),一个Recvr也可能接收到来自多个Sender的数据(如AggregationNode)。Sender与Recvr之间的数据流向主要依赖fragment_instance_id来确定,Sender在发送数据时,仅指定fragment_id即可,在fragment的Prepare阶段会创建Recvr,Sender在通过TransmitData的rpc发送数据前,会先通过一个recvr_map来确定Recvr,随后将数据发送给所有Recvr。Recvr接收到数据后,会先进行反序列化,并将数据放入batch_queue,如果batch_queue满了,会反压当前所有Sender-Recvr的rpc,待batch_queue消费后再继续反序列化数据。每个Recvr有一个sender_queues_数组,其中的每个元素都是存储来自多个不同sender的SenderQueue队列,每个队列中包含了Sender对应的batch_queue。在fragment的Open阶段,Recvr会开始调用GetBatch来消费sender_queues_中的各个元素的batch_queue,若SQL存在order by等需要排序的场景,执行计划中会存在SortNode节点,SortNode会使ExchangeNode中的一个is_merging_的flag=1,这使得Recvr会创建一个merger来对batch_queue中的数据进行排序后再合并,排序算法为堆排序。若GetBatch消费的速度较慢,赶不上Sender填充batch_queue的速度,batch_queue会迅速填满,此时Recvr进行rpc反压,暂时阻塞rpc直到处理完batch_queue的数据,日志中会打印如下输出:
在这里插入图片描述
在这里插入图片描述
在所有RowBatch发送结束后,即状态进入last batch sent,fragment会发送Eos信号给Recvr,此时会通过KrpcDataStreamSender::FlushFinal调用EndDataStream rpc表示数据传输结束。若rpc由于某种原因出现失败,在4.1版本,fragment会自动重新调度执行此rpc。

总结

本文首先列举了目前Impala内部使用Krpc实现的RPC,然后调研分析了DataStreamService中主要两个RPC:TransmitData和EndDataStream的执行过程和简要实现,并举例了目前线上常见的异常,希望借助此文可便于后续排查DataStreamService中的一些性能问题和故障。

参考资料

https://blog.cloudera.com/scalability-improvement-of-apache-impala-2-12-0-in-cdh-5-15-0/

  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值