負載均衡的高可用
最近在工作中遇到了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啟動時跟源碼發現的,目前發現存在一些問題:
- 此種方式的高可用在leader節點沒有掛掉的情況下會始終連接leader節點,只有在leader不可用時才會自動切換,類似hadoop的Actice/Standby方案,但這樣只能做到故障切換,沒有做到負載均衡(這種方式在代碼中是否有另外一套負載均衡的機制還有待探究)
- url中serviceDiscoveryMode=zooKeeperHA的連接方式不被一些鑒權服務所支持(如ranger)
本文首發於博客園,作者榆天紫夏,希望對大家有所幫助。如有遺漏或問題歡迎補充指正