HiveServer2 架構(gòu)源碼詳解

https://blog.csdn.net/u013332124/article/details/94182021

以下內(nèi)容都基于hive-2.3.3版本片排。

一、HiveServer2的啟動

通常我們是通過調(diào)用${hive_home}/bin下的start-hiveserver2.sh腳本來啟動HiveServer2的永部。start-hiveserver2.sh腳本實際是調(diào)用${hive_home}/bin/hive腳本:

#! /bin/bash
export HIVE_HOME=/www/hive
${HIVE_HOME}/bin/hive --service hiveserver2 >> ${HIVE_HOME}/logs/hiveserver2.out 2>&1 &

之后hive腳本中會根據(jù)傳入的--service的參數(shù)去調(diào)用${hive_home}/bin/ext/hiveserver2.sh腳本鉴竭,最終在hiveserver2.sh腳本中才調(diào)用了HiveServer2類的main方法尊蚁。

//HiveServer2.java
public static void main(String[] args) {
    HiveConf.setLoadHiveServer2Config(true);
    try {
      ServerOptionsProcessor oproc = new ServerOptionsProcessor("hiveserver2");
        //解析傳入的參數(shù)
      ServerOptionsProcessorResponse oprocResponse = oproc.parse(args);

      String initLog4jMessage = LogUtils.initHiveLog4j();
      LOG.debug(initLog4jMessage);
      HiveStringUtils.startupShutdownMessage(HiveServer2.class, args, LOG);

      LOG.debug(oproc.getDebugMessage().toString());
    //調(diào)用對應(yīng)Executor的execute方法
      oprocResponse.getServerOptionsExecutor().execute();
    } catch (LogInitializationException e) {
      LOG.error("Error initializing log: " + e.getMessage(), e);
      System.exit(-1);
    }
  }

HiveServer2的main函數(shù)執(zhí)行時會先解析傳入的參數(shù)滤否,然后返回一個response,里面封裝了一個ServerOptionsExecutor。之后調(diào)用ServerOptionsExecutor#execute方法即可執(zhí)行對應(yīng)的邏輯。

ServerOptionsExecutor只是一個接口九默,它的實現(xiàn)主要有:

在這里插入圖片描述

比如如果傳入的ServiceName是hiveserver2,則會執(zhí)行StartOptionExecutor的execute方法罚随,這個方法中調(diào)用了HiveServer2的startHiveServer2方法。

這時,HiveServer才開始啟動它的各個組件并提供服務(wù)。

二指蚁、HiveServer2的各個服務(wù)組件

Hive定義了一個接口Service,很多服務(wù)組件都實現(xiàn)了這個接口自晰。這個接口定義了一個組件基本的生命周期欣舵,以及組件的各種狀態(tài):

public interface Service {
  public enum STATE {
    NOTINITED,
    INITED,
    STARTED,
    STOPPED
  }
  void init(HiveConf conf);
  void start();
  void stop();
  void register(ServiceStateChangeListener listener);
  void unregister(ServiceStateChangeListener listener);
  String getName();
  HiveConf getHiveConf();
  STATE getServiceState();
  long getStartTime();
}

Service接口的具體實現(xiàn)有:

在這里插入圖片描述

可以看到,包括HiveServer2其實也算Hive的一個服務(wù)組件缀磕。HiveServer2繼承了CompositeService類,這里用到了設(shè)計模式中的組合模式劣光。這樣袜蚕,HiveServer2可以同時持有多個服務(wù)組件。在HiveServer2啟動時绢涡,會相應(yīng)啟動它的那些服務(wù)組件牲剃。

HiveServer2的服務(wù)組件主要有如下這些:

在這里插入圖片描述

1、ThriftCLIService

一個Server的服務(wù)組件雄可,它會監(jiān)聽指定的端口來對外提供服務(wù)凿傅,主要基于Thrift實現(xiàn)的rpc服務(wù)。HiveServer2在啟動ThriftCLIService時数苫,會將CLIService的實例也傳給它聪舒。這樣,ThriftCLIService收到請求后虐急,就可以委托給CLIService處理了箱残。

另外,ThriftCLIService啟動時,根據(jù)hive.server2.transport.mode參數(shù)的值來決定是啟動ThriftHttpCLIService還是ThriftBinaryCLIService被辑。默認是ThriftBinaryCLIService:

    if (isHTTPTransportMode(hiveConf)) {
      thriftCLIService = new ThriftHttpCLIService(cliService, oomHook);
    } else {
      thriftCLIService = new ThriftBinaryCLIService(cliService, oomHook);
    }

2燎悍、CLIService

CLIService主要封裝了處理命令的邏輯,一條命令發(fā)到HiveServer2后盼理,ThriftCLIService會委托給CLIService來處理谈山。不同的命令會調(diào)用不同的CLIService方法。比如執(zhí)行Sql就是調(diào)用CliService#executeStatementAsync()方法宏怔。

3奏路、SessionManager

CLIService在啟動時,會初始化一個SessionManager举哟,用來管理會話思劳。

當要建立一個會話時,會調(diào)用SessionManager#createSession()來獲取到唯一的SessionHandle返回給客戶端(session的唯一性是通過SessionHandle來標識的)妨猩。之后客戶端發(fā)送命令時將對應(yīng)的SessionHandle帶上潜叛,SessionManager就可以根據(jù)這個SessionHandle獲取到具體的HiveSession對象。

拿到對應(yīng)的HiveSession對象后壶硅,CLIService把具體的操作繼續(xù)委托給HiveSession的方法執(zhí)行威兜。

4、OperationManager

SessionManager在啟動時庐椒,會初始化一個OperationManager椒舵,主要用來生成Operation(每一條命令都可以理解為是一個Operation)。

SessionManager在創(chuàng)建新的HiveSession時约谈,會將OperationManager的實例也傳給HiveSession笔宿。后面HiveSession根據(jù)命令執(zhí)行對應(yīng)的操作時會通過OperationManager獲取到對應(yīng)的Operation,之后調(diào)用Operation#run()執(zhí)行具體的邏輯棱诱。

也就是說泼橘,客戶端發(fā)來的命令最終生成一個Operation對象然后執(zhí)行。不同的命令會對應(yīng)不同的Operation的實現(xiàn):

在這里插入圖片描述

很明顯迈勋,執(zhí)行Sql最終會調(diào)用SQLOperation#run()方法炬灭。

三、一個命令的具體處理過程

1靡菇、一個命令的處理流程

在這里插入圖片描述
  1. ThriftCLIService服務(wù)啟動后重归,會監(jiān)聽指定的端口,之后有請求進來厦凤,就會獲取對應(yīng)的Processor處理(可以參考thrift的架構(gòu)設(shè)計)
  2. 如果沒有配置kerberos鼻吮,最終的Processor會是TSetIpAddressProcessor。TSetIpAddressProcessor繼承自TCLIService.Processor较鼓,TCLIService.Processor中定義了具體的命令要執(zhí)行哪些ProcessFunction
  3. 通過具體的命令找到對應(yīng)的ProcessFunction后狈网,就執(zhí)行ProcessFunction的getResult()方法。之后,各個ProcessFunction實現(xiàn)類的getResult()方法最終又會調(diào)用ThriftCLIService的相關(guān)方法
  4. ThriftCLIService從請求中獲取到SessionHandle后拓哺,就委托給CLIService的相關(guān)方法來處理
  5. CLIService拿著SessionHandle從SessionManager獲取到對應(yīng)的HiveSession勇垛,之后繼續(xù)把命令委托給HiveSession處理
  6. HiveSession根據(jù)具體的命令從OperationManager中獲取到對應(yīng)的Operation,這個Operation就是真正的操作對象士鸥。
  7. 最終調(diào)用Operation#run()方法獲取到一個OperationHandle闲孤,后面客戶端還可以通過這個OperationHandle標識來獲取到此次操作的一些狀態(tài)信息。如果是要執(zhí)行Sql烤礁,就會走到SQLOperation#run()方法,之后就進入Driver.run()方法讼积,然后開始編譯執(zhí)行sql了

2脚仔、關(guān)于SessionHandle和OperationHandle

在hive中勤众,一個session表示一個會話。我們可以理解為一個beeline控制臺就是一個session鲤脏,在我們通過!conn命令連接到集群后们颜,HiveServer2就會創(chuàng)建一個HiveSession,然后交給SessionManager管理猎醇,之后返回一個SessionHandle給客戶端窥突,這個SessionHandle就是此次session的唯一標識了。后面客戶端發(fā)送命令的時候都需要帶上這個SessionHandle硫嘶,這樣HiveServer2才可以辨認出是哪個session發(fā)來的請求阻问。

在一個session中發(fā)起一個請求,HiveServer2收到請求進行處理后沦疾,會返回一個OperationHandle來作為此次操作的唯一標識称近。后面客戶端可以通過這個OperationHandle標識來獲取此次操作的具體信息(發(fā)請求時帶上這個OperationHandle信息),比如獲取操作的執(zhí)行狀態(tài)哮塞、日志等信息煌茬。

這也就是說,一個session其實可以發(fā)起多個操作彻桃,只要維護好返回的OperationHandle,我們可以并行查詢這些操作的相關(guān)狀態(tài)晾蜘。在beeline的控制臺中邻眷,我們發(fā)送完一個命令后,會阻塞在那里剔交,給我們的感覺好像一個session只能同時處理一個命令肆饶。

另外,在阻塞的過程中岖常,beeline客戶端其實也不斷的再想HiveServer2發(fā)送請求獲取日志并輸出驯镊。

beeline客戶端是如何獲取日志輸出的

只有當hive.server2.logging.operation.enabled設(shè)置為true,才會在Beeline的控制臺輸出HiveServer2那邊的相關(guān)操作日志。另外板惑,通過hive.server2.logging.operation.level還可以調(diào)整輸出級別橄镜。

如果開啟了operation的log日志功能,Driver組件在輸出日志時就會往另外一個臨時的日志文件也輸出一份冯乘。這個臨時的日志文件是一個Operation一份洽胶。后面客戶端根據(jù)OperationHandle發(fā)送fetchResult(fetchType=1)請求來獲取對應(yīng)的日志信息。這時HiveServer2只要直接去Operation對應(yīng)的臨時日志文件中拉取數(shù)據(jù)即可裆馒。

Operation臨時日志文件的存放路徑和hive.server2.logging.operation.log.location參數(shù)有關(guān)姊氓。

四、HiveServer2中的那些重要線程

1喷好、rpc請求處理線程——HiveServer2-Handler-Pool

這個線程池主要是HiveServer2用來處理rpc請求的線程翔横。線程池的coreSize和maxSize和參數(shù)hive.server2.thrift.min.worker.threadshive.server2.thrift.max.worker.threads相關(guān),默認值分別是5和500梗搅。

這個線程和netty的worker線程類似禾唁,有客戶端發(fā)送請求給ThriftServer,最終都會由這個線程來處理些膨。

2蟀俊、sql異步執(zhí)行線程 —— HiveServer2-Background-Pool

在SQLOperation#runInternal()方法中,如果請求要求異步操作订雾,就會向BackGroundPool線程池提交一個異步任務(wù)肢预,用來處理sql(也就是調(diào)用Driver#run()的邏輯),提交任務(wù)到線程池后會立馬返回一個OperationHandle洼哎,后續(xù)客戶端可以根據(jù)這個唯一標識實時的查詢?nèi)蝿?wù)運行日志烫映。

當然,如果請求沒要求異步操作噩峦,Driver#run()的操作將在HiveServer2-Handler-Pool的線程中執(zhí)行锭沟,這時會一直阻塞到任務(wù)執(zhí)行完才返回,也就是說识补,客戶端要等任務(wù)執(zhí)行完才能看到全部運行日志族淮。

BackGroundPool線程池的coreSize和maxSize都由參數(shù)hive.server2.async.exec.threads來決定,默認值是100凭涂。

是否要異步執(zhí)行Driver#run()由thrift請求體TExecuteStatementReq中的參數(shù)runAsync來決定祝辣。目前看hive-jdbc的代碼,這個參數(shù)默認都是true切油。

另外蝙斜,目前只有SQLOperation才支持異步執(zhí)行,其他的Operation都不支持

3澎胡、執(zhí)行task的線程

Driver在編譯完sql后孕荠,會生成物理執(zhí)行計劃娩鹉,這個物理執(zhí)行計劃中包含了一系列的task。Driver執(zhí)行task的方式是將task放到一個線程中執(zhí)行稚伍。這個線程沒有特別指定名稱弯予,通過日志我們看到的是[Thread-id],其中id會不斷自增槐瑞。

因為task是在線程中執(zhí)行的熙涤,因此一個Operation是允許多個task并行的。單個Operation的并行度由配置hive.exec.parallel.thread.number來決定困檩,默認值是8祠挫。

4、總結(jié)

在hive的日志中悼沿,各種任務(wù)的日志會交替出現(xiàn)等舔,日志又雜又多,因此理解好上面三種線程可以方便我們排查問題糟趾,快速定位任務(wù)相關(guān)的線程和日志慌植。

在這里插入圖片描述

五、Beeline和HiveCli的區(qū)別

在hive中义郑,有兩種方式可以執(zhí)行hiveQL蝶柿。分別是beeline和hiveCli。

1非驮、Beeline

beeline客戶端執(zhí)行的主類是Beeline.java交汤。

Beeline需要連接上HiveServer2后才可以執(zhí)行命令,之后通過jdbc協(xié)議往hiveServer2發(fā)送相關(guān)請求來執(zhí)行用戶的命令劫笙。

2芙扎、HiveCli

舊版的HiveCli執(zhí)行的主類是CliDriver.java,目前新版的主類是HiveCli.java(底層還是調(diào)用Beeline.java類)填大。

CliDriver.java的執(zhí)行流程詳解可以看下面這篇文章:

https://segmentfault.com/a/1190000002766035 —— 主要就是創(chuàng)建一個進程戒洼,在當前進程中執(zhí)行hive的相關(guān)命令,比如執(zhí)行Sql就初始化一個Driver允华,然后執(zhí)行run方法圈浇。

在早期的版本中,第一代HiveServer對應(yīng)的客戶端實現(xiàn)就是CliDriver靴寂,后來出現(xiàn)了HiveServer2磷蜀,完全取代了第一代。HiveServer2對應(yīng)的客戶端是Beeline榨汤,但是由于早期使用CliDriver的用戶太多了,因此CliDriver版本一直沒有被下掉怎茫。

后面社區(qū)又開發(fā)了新版的HiveCli收壕,其底層其實也是調(diào)用了Beeline妓灌,這樣社區(qū)只需要維護一份客戶端的代碼即可:

  public static void main(String[] args) throws IOException {
    int status = new HiveCli().runWithArgs(args, null);
    System.exit(status);
  }

  public int runWithArgs(String[] cmd, InputStream inputStream) throws IOException {
    beeLine = new BeeLine(false);
    try {
      return beeLine.begin(cmd, inputStream);
    } finally {
      beeLine.close();
    }
  }

Beeline類有個屬性isBeeline就表示這個Beeline實例是算真正的beeline客戶端還是HiveCli客戶端。

當isBeeline屬性為false時蜜宪,beeline客戶端連接的是一個內(nèi)嵌的HiveServer2服務(wù)虫埂,和HiveCli在同一個進程內(nèi)。其他的邏輯都和Beeline相同圃验。

3掉伏、總結(jié)

  • beeline需要連接遠程的HiveServer2來交互
  • HiveCli直接在本進程執(zhí)行命令,不用進行遠程通信

在2.3.3版本中澳窑,HiveCli默認還是使用CliDriver的實現(xiàn)方式斧散。可以通過在hive-env.sh中設(shè)置USE_DEPRECATED_CLI=false來將實現(xiàn)變成HiveCli.java的形式摊聋。因為HiveCli.java會啟動內(nèi)置的HiveServer2鸡捐,執(zhí)行的邏輯基本和HiveServer2一樣,所以在做一些測試時會更容易發(fā)現(xiàn)HiveServer2的一些問題麻裁。

官網(wǎng)關(guān)于Beeline和HiveCli的介紹:

https://cwiki.apache.org/confluence/display/Hive/Replacing+the+Implementation+of+Hive+CLI+Using+Beeline

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末箍镜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子煎源,更是在濱河造成了極大的恐慌色迂,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件手销,死亡現(xiàn)場離奇詭異歇僧,居然都是意外死亡,警方通過查閱死者的電腦和手機原献,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門馏慨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人姑隅,你說我怎么就攤上這事写隶。” “怎么了讲仰?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵慕趴,是天一觀的道長。 經(jīng)常有香客問我鄙陡,道長冕房,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任趁矾,我火速辦了婚禮耙册,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘毫捣。我一直安慰自己详拙,他們只是感情好帝际,可當我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著饶辙,像睡著了一般蹲诀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弃揽,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天脯爪,我揣著相機與錄音,去河邊找鬼矿微。 笑死痕慢,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的冷冗。 我是一名探鬼主播守屉,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蒿辙!你這毒婦竟也來了拇泛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤思灌,失蹤者是張志新(化名)和其女友劉穎俺叭,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泰偿,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡熄守,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了耗跛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裕照。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖调塌,靈堂內(nèi)的尸體忽然破棺而出晋南,到底是詐尸還是另有隱情,我是刑警寧澤羔砾,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布负间,位于F島的核電站,受9級特大地震影響姜凄,放射性物質(zhì)發(fā)生泄漏政溃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一态秧、第九天 我趴在偏房一處隱蔽的房頂上張望董虱。 院中可真熱鬧,春花似錦申鱼、人聲如沸愤诱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽转锈。三九已至,卻和暖如春楚殿,著一層夾襖步出監(jiān)牢的瞬間撮慨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工脆粥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留砌溺,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓变隔,卻偏偏與公主長得像规伐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子匣缘,可洞房花燭夜當晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容