Impala总共分为3个组件:impalad, statestored, client/impala-shell。关于这三个组件的基本功能在这篇文章中已经介绍过了。

Client : 可以是Python CLI(官方提供的impala_shell.py),JDBC/ODBC或者Hue。无论哪个其实就是一个Thrift的client,连接到impalad的21000端口。

Impalad: 分为frontend和backend两部分,这个进程有三个ThriftServer(beeswax_server, hs2_server, be_server)对系统外和系统内提供服务。

Statestored: 集群内各个backend service的数据交换中心,每个backend会在statestored注册,以后statestored会与所有注册过的backend交换update消息。

RPC

Component Service Port Access Requirement Comment
ImpalaDaemon Impala Daemon Backend Port 22000 Internal ImpalaBackendService export
  Impala Daemon Frontend Port 21000 External ImpalaService export
  Impala Daemon HTTP Server Port 25000 External Impala debug web server
  StateStoreSubscriber Service Port 23000 Internal StateStoreSubscriberService
 ImpalaStateStore Daemon StateStore HTTP Server Port 25010 External StateStore debug web server
  StateStore Service Port 24000 Internal StateStoreService export

下面介绍三个组件之间的Thrift RPC(“<->”前面的表示RPC client,“<->”后面的表示RPC server)

(1)Client <-> impalad(frontend)

BeeswaxService(beeswax.thrift): client通过query()提交SQL请求,然后异步调用get_state()监听该SQL的查询进度,一旦完成,调用fetch()取回结果。

TCLIService(cli_service.thrift): client提交SQL请求,功能和上面类似,更丰富的就是对DDL操作的支持,例如GetTables()返回指定table的元数据。

ImpalaService和ImpalaHiveServer2Service(ImpalaService.thrift)分别是上面两个类的子类,各自丰富了点功能而已,核心功能没啥大变化。

(2)Impalad(backend) <-> statestored

StateStoreService(StateStoreService.thrift): statestored保存整个系统所有backend service状态的全局数据库,这里是个单节点中央数据交换中心(该节点保存的状态是soft state,一旦宕机,保存的状态信息就没了)。例如每个impala backend启动的时候会调用StateStoreService.RegisterService()向statestored注册自己(其实是通过跟这个backend service捆绑在一起的StateStoreSubscriber标识的),然后再调用StateStoreService.RegisterSubscription()表明这个StateStoreSubscriber接收来自statestored的update。

(3)Statestord <-> impalad(backend)

StateStoreSubscriberService(StateStoreSubscriberService.thrift): backend向statestored调用RegisterSubscription之后,statestored就会定期向backend这边捆绑的StateStoreSubscriber发送该backend的状态更新信息。然后backend这边调用StateStoreSubscriberService.UpdateState()更新相关状态。同时这个UpdateState()调用在impalad
backend/StateStoreSubscriber这端还会返回该backend的一些update信息给statestored。

(4)Impalad(backend) <-> other impalad(backend) (这两个是互为client/server的)

ImpalaInternalService(ImpalaInternalService.thrift):某个backend的coordinator要向其他backend的execute engine发送执行某个plan fragment的请求(提交ExecPlanFragment并要求返回ReportExecStatus)。这部分功能会在backend分析中详细讨论。

(5)Impalad backend <-> other frontend

ImpalaPlanService(ImpalaPlanService.thrift):可以由其他形式的frontend生成TExecRequest然后交给backend执行。

另外,Impala frontend是用Java写的,而backend使用C++写的。Frontend负责把输入的SQL解析,然后生成执行计划,之后通过Thrift的序列化/反序列化的方式传给backend。TExecRequest(frontend.thrift)是中间传输的数据结构,表示了一个Query/DML/DDL的查询请求,也是SQL执行过程中在frontend和backend之间的数据接口。所以我们可以把impala-frontend换掉,用其他的形式拼凑出这个TExecRequest就可以传给backend执行,这也就是前面说的ImpalaPlanService干的事。

impala组件执行流程

1, impala-shell

client就可以通过Beeswax和HiveServer2的Thrift API向Impala提交query。这两种访问接口的作用是一样的(都是用于client提交query,返回query result)。

Impala_shell.py是通过Beeswax方式访问impala的,下面我们看看impala_shell.py是怎么向impalad提交query的。

(1)通过OptionParser()解析命令行参数。如果参数中有—query或者—query_file,则执行execute_queries_non_interactive_mode(options),这是非交互查询(也就是就查询一个SQL或者一个写满SQL的文件);否则进入ImpalaShell.cmdloop (intro)循环。

(2)进入命令行循环后,一般是先connect某一个impalad,输入”connect localhost:21000”,进入do_connect(self, args)函数。这个函数根据用户指定的host和port,生成与相应的impalad的socket连接。最重要的就是这行代码:

self.imp_service = ImpalaService.Client(protocol)

至此imp_service就是client端的代理了,所有请求都通过它提交。

(3)下面以select命令为例说明,如果client输入这样的命令”select col1, col2 from tbl”,则进入do_select(self, args)函数。在这个函数里首先生成BeeswaxService.Query对象,向这个对象填充query statement和configuration。然后进入__query_with_result()函数通过imp_service.query(query)提交query。注意ImpalaService都是异步的,提交之后返回一个QueryHandle,然后就是在一个while循环里不断__get_query_state()查询状态。如果发现这个SQL的状态是FINISHED,那么就通过fetch()
RPC获取结果。

2, statestored

Statestored进程对外提供StateStoreService RPC服务,而StateStoreSubscriberService RPC服务是在impalad进程中提供的。StateStoreService这个RPC的逻辑实现是在StateStore这个类里面实现的。

Statestored收到backend发送的RegisterService RPC请求时,调用StateStore::RegisterService()处理,主要做两件事:

(1)根据TRegisterServiceRequest提供的service_id把该service加入StateStore.service_instances_。

通常在整个impala集群只存在名为“impala_backend_service”这一个服务,所以service_id=”impala_backend_service”。而每个backend捆绑的<SubscriberId,impala::THostPort>是不一样的,所以就形成了service和backend一对多的关系,这个关系存储在StateStore.service_instances_组。

(2)Impalad backend在向statestored RegisterService的时候,会把subscriber_address发送过去。在statestored端,会根据这个subscriber_address生成对应的Subscriber对象(表示与该Subscriber捆绑的backend)。把与该backend绑定的Subscriber加入StateStore.subscribers_这个map里。每个Subscriber有个唯一的id,这样分布在集群内的impala backend就有了全局唯一id了。

这样如果以后某个backend/StateStoreSubscriber fail或者其中运行的SQL任务出了问题,在statestored这里就会有体现了,那么就会通知给其他相关的backend。

那么每个backend是怎么update的呢?StateStore::UpdateLoop()负责定期向各个backend推送其所订阅的service的所有成员的更新,目前的更新策略是全量更新,未来会考虑增量更新。

3, impalad

Impalad进程的服务被wrapper在ImpalaServer这个类中。ImpalaServer包括fe和be的功能,实现了ImpalaService(Beeswax), ImpalaHiveServer2Service(HiveServer2)和ImpalaInternelService API。

全局函数CreateImpalaServer()创建了一个ImpalaServer其中包含了多个ThriftServer:

(1)创建一个名为beeswax_server的ThriftServer对系统外提供ImpalaService(Beeswax)服务,主要服务于Query查询,是fe/frontend的核心服务,端口21000

(2)创建一个名为hs2_server的ThriftServer对系统外提供ImpalaHiveServer2Service服务,提供Query, DML, DDL相关操作,端口21050

(3)创建一个名为be_server的ThriftServer对系统内其他impalad提供ImpalaInternalService,端口22000

(4)创建ImpalaServer对象,前面三个ThriftServer的TProcessor被赋值这个ImpalaServer对象,所以对前面三个Thrift服务的RPC请求都交由这个ImpalaServer对象处理。最典型的例子就是我们通过Beeswax接口提交了一个BeeswaxService.query()请求,在impalad端的处理逻辑是由void ImpalaServer::query(QueryHandle& query_handle, const Query& query)这个函数(在impala-beeswax-server.cc中实现)完成的。

下面是impalad-main.cc的主函数:

int main(int argc, char** argv) {
//参数解析,开启日志(基于Google gflags和glog)
InitDaemon(argc, argv); LlvmCodeGen::InitializeLlvm(); // Enable Kerberos security if requested.
if (!FLAGS_principal.empty()) {
EXIT_IF_ERROR(InitKerberos("Impalad"));
}
//因为frontend, HBase等相关组件是由Java开发的,所以下面这几行都是初始化JNI相关的reference和method id
JniUtil::InitLibhdfs();
EXIT_IF_ERROR(JniUtil::Init());
EXIT_IF_ERROR(HBaseTableScanner::Init());
EXIT_IF_ERROR(HBaseTableCache::Init());
InitFeSupport(); //ExecEnv类是impalad backend上Query/PlanFragment的执行环境。
//生成SubscriptionManager, SimpleScheduler和各种Cache
ExecEnv exec_env;
//生成Beeswax, hive-server2和backend三种ThriftServer用于接收client请求,不过这三种服务的后端真正的处理逻辑都是ImpalaServer* server这个对象。
ThriftServer* beeswax_server = NULL;
ThriftServer* hs2_server = NULL;
ThriftServer* be_server = NULL;
ImpalaServer* server =
CreateImpalaServer(&exec_env, FLAGS_fe_port, FLAGS_hs2_port, FLAGS_be_port,
&beeswax_server, &hs2_server, &be_server);
//因为be_server是对系统内提供服务的,先启动它。
be_server->Start();
//这里面关键是启动了SubscriptionManager和Scheduler
Status status = exec_env.StartServices();
if (!status.ok()) {
LOG(ERROR) << "Impalad services did not start correctly, exiting";
ShutdownLogging();
exit(1);
} // register be service *after* starting the be server thread and after starting
// the subscription mgr handler thread
scoped_ptr cb;
if (FLAGS_use_statestore) {
THostPort host_port;
host_port.port = FLAGS_be_port;
host_port.ipaddress = FLAGS_ipaddress;
host_port.hostname = FLAGS_hostname;
//注册这个be服务到statestored,整个集群里所有的be服务组成一个group,这样以后来了Query请求就可以在各个backend之间dispatch了。
Status status =
exec_env.subscription_mgr()->RegisterService(IMPALA_SERVICE_ID, host_port); unordered_set services;
services.insert(IMPALA_SERVICE_ID);
//注册callback函数,每当StateStoreSubscriber接收到来自statestored的update之后调用该函数。
cb.reset(new SubscriptionManager::UpdateCallback(
bind(mem_fn(&ImpalaServer::MembershipCallback), server, _1)));
exec_env.subscription_mgr()->RegisterSubscription(services, "impala.server",
cb.get()); if (!status.ok()) {
LOG(ERROR) << "Could not register with state store service: "
<< status.GetErrorMsg(); ShutdownLogging(); exit(1); } } // this blocks until the beeswax and hs2 servers terminate //前面对内服务的be_server已经成功启动,下面启动对外服务的beeswax_server和hs2_server beeswax_server->Start();
hs2_server->Start();
beeswax_server->Join();
hs2_server->Join(); delete be_server;
delete beeswax_server;
delete hs2_server;
}

exec_env.StartServices()调用SubscriptionManager.Start(),进一步调用StateStoreSubscriber.Start()启动一个ThriftServer。

StateStoreSubscriber实现了StateStoreSubscriberService(StateStoreSubscriberService.thrift中定义),用于接收来自statestored的update,并把与这个StateStoreSubscriber捆绑的backend的update反馈给statestored。这样这个backend就可以对其他backend可见,这样就可以接受其他impala backend发来的任务更新了(当然,接收backend更新是通过statestored中转的)。

参考文献:

http://www.sizeofvoid.net/wp-content/uploads/ImpalaIntroduction2.pdf

Impala源代码分析(1)-Impala架构和RPC的更多相关文章

  1. Impala源代码分析---1

    2.Impala源代码分析 參考链接:http://www.sizeofvoid.net/wp-content/uploads/ImpalaIntroduction2.pdf 本章開始进入源代码分析阶 ...

  2. MyBatis架构设计及源代码分析系列(一):MyBatis架构

    如果不太熟悉MyBatis使用的请先参见MyBatis官方文档,这对理解其架构设计和源码分析有很大好处. 一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBa ...

  3. Hadoop源代码分析

    http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...

  4. Hadoop源代码分析(完整版)

    Hadoop源代码分析(一) 关键字: 分布式云计算 Google的核心竞争技术是它的计算平台.Google的大牛们用了下面5篇文章,介绍了它们的计算设施. GoogleCluster:http:// ...

  5. 转:SDL2源代码分析

    1:初始化(SDL_Init()) SDL简介 有关SDL的简介在<最简单的视音频播放示例7:SDL2播放RGB/YUV>以及<最简单的视音频播放示例9:SDL2播放PCM>中 ...

  6. 转:ffdshow 源代码分析

    ffdshow神奇的功能:视频播放时显示运动矢量和QP FFDShow可以称得上是全能的解码.编码器.最初FFDShow只是mpeg视频解码器,不过现在他能做到的远不止于此.它能够解码的视频格式已经远 ...

  7. 【转载】linux环境下tcpdump源代码分析

    linux环境下tcpdump源代码分析 原文时间 2013-10-11 13:13:02  CSDN博客 原文链接  http://blog.csdn.net/han_dawei/article/d ...

  8. 服务器程序源代码分析之三:gunicorn

    服务器程序源代码分析之三:gunicorn 时间:2014-05-09 11:33:54 类别:网站架构 访问: 641 次 gunicorn是一个python web 服务部署工具,类似flup,完 ...

  9. linux环境下tcpdump源代码分析

    Linux 环境下tcpdump 源代码分析 韩大卫@吉林师范大学 tcpdump.c 是tcpdump 工具的main.c, 本文旨对tcpdump的框架有简单了解,只展示linux平台使用的一部分 ...

  10. redis 源代码分析(一) 内存管理

    一,redis内存管理介绍 redis是一个基于内存的key-value的数据库,其内存管理是很重要的,为了屏蔽不同平台之间的差异,以及统计内存占用量等,redis对内存分配函数进行了一层封装,程序中 ...

随机推荐

  1. 喜报!Fluent Editor 开源富文本迎来了第一位贡献者!

    你好,我是 Kagol,个人公众号:前端开源星球. 2024年8月20日,刚开源一周的富文本 Fluent Editor 迎来了第一位贡献者:zzxming 1 Bug 描述 zzxming 同学修复 ...

  2. Java基础12

    抽象类与抽象方法 abstract : 抽象的 abstract可以用来修饰:类.方法 abstract修饰类 > 此类称为抽象类 > 抽象类不能实例化 > 抽象类中是包含构造器的, ...

  3. Spring —— bean生命周期

    bean生命周期 生命周期:从创建到消亡的完整过程 bean生命周期:bean从创建到销毁的整体过程 bean生命周期控制:在bean创建后到销毁前做一些事情   方式一:配置控制生命周期 <b ...

  4. kaggle入门 随机森林求解Titanic

    # kaggle Titanic # 导入需要的库 import pandas as pd import numpy as np import sys import sklearn import ra ...

  5. Dockerfile构建镜像(八)

    一.构建镜像 现在让我们再回到之前定制的 nginx 镜像的 Dockerfile 来.现在我们明白了这个 Dockerfile 的内容,那么让我们来构建这个镜像吧.在 Dockerfile 文件所在 ...

  6. Docker修改IP地址方法

    一.查看Docker IP root@master:/# ifconfig docker0 docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu ...

  7. find_sys_call_table和kallsysms_lookup_name的区别

    find_sys_call_table 和 kallsyms_lookup_name 都可以用于查找内核符号,但它们的具体作用和使用场景有所不同.以下是两者的详细对比: 1. find_sys_cal ...

  8. uprobe

    本章的我们来学习uprobe ,顾名思义,相对于内核函数/地址的监控,主要用于用户态函数/地址的监控.听起来是不是有点神奇,内核怎么监控用户态函数的调用呢?本章的内容包括: 如何使用uprobe 内核 ...

  9. iOS中修饰符常用小结

    1.copy,是复制引用对象地址的深拷贝 a:当修饰不可变类型的属性时,如NSArray.NSDictionary.NSString,用copy,用copy为关键字的话,调用setter方法后.是对赋 ...

  10. kotlin类和对象—>接口

    1.接口定义,使用关键字interface 来定义接口 interface MyInterface { fun bar() fun foo() { // 可选的方法体 } } 2.实现接口,一个类和对 ...