負載均衡的高可用

最近在工作中遇到了hiveserver2需要部署高可用的場景,去網上搜索瞭解過後,用了絕大多數人推薦的共同方法:

Property_name Property_value Description
hive.server2.support.dynamic.service.discovery true(預設false) 使hiveserver2服務可被動態發現
hive.server2.zookeeper.namespace hiveserver2(預設值) hiveserver2例項在zk中註冊的znode名
hive.zookeeper.client.port 2181(預設值) zk埠
hive.zookeeper.quorum zk1:2181,zk2:2181,zk3:2181(預設空) zk叢集連線方式


當如上配置後,啟動的hiveserver2例項都會註冊到zk的/hiveserver2節點下,如下所示


get一下其中一個例項,就會發現它包含了這個hs2的uri、埠號等連線配置,如下所示


此時用客戶端(如beeline)連線hs2時,url需使用

jdbc:hive2://zk1:2181,zk2:2181,zk3:2181/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=hiveserver2

此時zk會隨機從多個例項中隨機拿一個例項供連線使用,過程在程式碼中體現如下:



先去指定的znode(/hiveserver2)下拿到所有例項,再通過Random隨機拿其中一個去連線;也正因為這個隨機的過程,此種方式的hs2高可用一定程度上實現了hiveserver2的負載均衡

問題

但這種方式的高可用在使用中存在一個問題,即當你同時開啟了鑑權的服務(如ranger),hs2在啟動時不僅會註冊hs2的例項,還會註冊一個leader節點,如下所示



從圖中可以看出,leader節點下有兩個子節點,而每個子節點實際沒有任何內容,因為這個leader節點在此處沒有用處。

但因為leader節點的存在,使上面講的隨機拿的過程中就可能會拿到這個leader節點,而該節點實際不是有效的hs2例項,故此時連線會報錯“Unable to read HiveServer2 configs from ZooKeeper”.

那麼Leader節點為什麼會產生呢?我們看下原始碼裡對應的部分



而目前還沒有發現leader節點的具體作用,只會在從zk拿例項時徒增報錯。

為了解決此問題,我重新編譯了hive-jdbc的原始碼,在從zk拿例項的過程中過濾掉了leader節點。

能解決此問題的,還有另一種方式,即配置ActivePassiveHA。


ActivePassiveHA

探究原始碼過程中,發現hive還提供了另一種高可用方案,即ActivePassiveHA,開啟需如下配置:

Property_name Property_value Description
hive.server2.support.dynamic.service.discovery true(預設false) 使hiveserver2服務可被動態發現
hive.server2.active.passive.ha.enable true(預設false) ActivePassiveHA啟用
hive.server2.active.passive.ha.registry.namespace hs2ActivePassiveHA(預設值) hiveserver2例項及leader在zk中註冊的znode名
hive.zookeeper.quorum zk1:2181,zk2:2181,zk3:2181(預設空) zk叢集連線方式
hive.zookeeper.client.port 2181(預設值) zk埠

當如上配置後,啟動的hiveserver2例項都會註冊到zk的/hs2ActivePassiveHA節點下,如下所示



由圖可見,其本質還是和上面類似的註冊例項的過程相似,但註冊的例項統一放在了instances下面,且註冊時資訊更詳細;而單獨產生的_LEADER節點則將兩個例項中的registry.unique.id拿出單獨放置。(hs2ActivePassiveHA後的unsecure或secure是根據是否開啟身份驗證或鑑權後自動新增的)


而ActivePassiveHA和上述高可用方案最大的區別,就是通過_LEADER節點分配可連線例項中的"leader"和"worker",當leader沒有掛掉的時候,所有通過zk連線到hs2的連線都會指向leader節點,而不會連線到其他節點,與上述高可用方案的隨機方式有一定區別。

此時,連線的url需使用

jdbc:hive2://zk1:2181,zk2:2181,zk3:2181/;serviceDiscoveryMode=zooKeeperHA;zooKeeperNamespace=hs2ActivePassiveHA


這種高可用方案同時解決了另一個問題:java.lang.NoClassDefFoundError: org/apache/tez/dag/api/TezConfiguration,如下所示


這問題出現在hs2啟動過程中,因為對hive3來說mr引擎已經是過時的,所以無論你的hive執行引擎選的是什麼,都會在啟動hs2時自動啟動一個TezSession,但如果你在hive-env.sh中沒有配置tez包的位置,這裡就會報ClassNotFound,並且強制等待60s後重試,重試後不管成功還是失敗都會繼續向下執行,不影響正常啟動,但拖慢了hs2啟動的時間。本來十幾秒就能解決的事非要等待一分多鐘才行。


但當開啟了ActicePassiveHA後,這個啟動TezSession的過程就會變成此處暫不啟動,後續由leader自行啟動,程式碼如下:


問題

此種高可用方案在hive官網和網路上的帖子中幾乎沒有被提及,是在hs2啟動時跟原始碼發現的,目前發現存在一些問題:

  1. 此種方式的高可用在leader節點沒有掛掉的情況下會始終連線leader節點,只有在leader不可用時才會自動切換,類似hadoop的Actice/Standby方案,但這樣只能做到故障切換,沒有做到負載均衡(這種方式在程式碼中是否有另外一套負載均衡的機制還有待探究)
  2. url中serviceDiscoveryMode=zooKeeperHA的連線方式不被一些鑑權服務所支援(如ranger)

以上就是我在探究hiveserver2高可用的過程當積累的一些心得,在此記錄一下,希望對大家有所幫助。如內容有錯誤敬請補充或指正