Skip to content

Commit 8862767

Browse files
[KYUUBI #6034] Kyuubi Server HA&ZK get server from serverHosts support more strategy
# 🔍 Description ## Issue References 🔗 This pull request fixes #6034 ## Describe Your Solution 🔧 Currently, use beeline to connect kyuubiServer with HA mode, the strategy only support random, this will lead to a high load on the machine. So i make this pr to support choose strategy. [description] First, we need know, beeline connect kyuubiServer dependency on kyuubi-hive-jdbc, it is isolated from the kyuubi cluster, so the code only support random choose serverHost from zk node /${namespace}. Because kyuubi-hive-jdbc is a stateless module, only run once, cannot store var about get serverHost from zk node. [Solution] This pr, we could implement a interface named ChooseServerStrategy to choose serverHost. I implement two strategy 1. poll: it will create a zk node named ${namespace}-counter, when a beeline client want connect kyuubiServer, the node will increment 1, use this value to take the remainder from serverHosts, like counter % serverHost.size, so we could get a order serverHost 2. random: random get serverHost from serverHosts 3. User Definied Class: implemented the ChooseServerStrategy, then put the jar to beeline-jars, it can use your strategy to choose serverHost ## Types of changes 🔖 - [ ] Bugfix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) ## Test Plan 🧪 Test the Strategy in my test Cluster #### Behavior Without This Pull Request ⚰️ ![image](https://github.com/apache/kyuubi/assets/51512358/d65b14c1-1b02-4436-8843-27b2e55d27ce) ![image](https://github.com/apache/kyuubi/assets/51512358/0524a30c-c2c3-464e-8453-84f3f1a74fb1) ![image](https://github.com/apache/kyuubi/assets/51512358/12feb93e-b743-4a43-821d-454f3c1af336) #### Behavior With This Pull Request 🎉 [Use Case] 1. poll: `bin/beeline -u 'jdbc:hive2://xxx:2181/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=kyuubi;zooKeeperStrategy=poll?spark.yarn.queue=root.kylin;spark.app.name=testspark;spark.shuffle.useOldFetchProtocol=true' -n mfw_hadoop --verbose=true --showNestedErrs=true` 2. random: `bin/beeline -u 'jdbc:hive2://xxx:2181/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=kyuubi;zooKeeperStrategy=random?spark.yarn.queue=root.kylin;spark.app.name=testspark;spark.shuffle.useOldFetchProtocol=true' -n mfw_hadoop --verbose=true --showNestedErrs=true` or `bin/beeline -u 'jdbc:hive2://xxx:2181/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=kyuubi?spark.yarn.queue=root.kylin;spark.app.name=testspark;spark.shuffle.useOldFetchProtocol=true' -n mfw_hadoop --verbose=true --showNestedErrs=true` 3. YourStrategy: `bin/beeline -u 'jdbc:hive2://xxx:2181/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=kyuubi;zooKeeperStrategy=xxx.xxx.xxx.XxxChooseServerStrategy?spark.yarn.queue=root.kylin;spark.app.name=testspark;spark.shuffle.useOldFetchProtocol=true' -n mfw_hadoop --verbose=true --showNestedErrs=true` [Result: The Cluster have two Server (221,233)] 1. poll: 1.1. zkNode: counterValue ![image](https://github.com/apache/kyuubi/assets/51512358/5cbd15f9-bba4-4b23-bbfb-d61ed46f931f) 1.2. result: ![image](https://github.com/apache/kyuubi/assets/51512358/5a867167-8b06-49ed-aa44-b70726f3ae97) ![image](https://github.com/apache/kyuubi/assets/51512358/404b05e8-c828-458c-a9c4-97a323bf6ce7) ![image](https://github.com/apache/kyuubi/assets/51512358/3182e92b-6976-4931-a899-5e0d89cd2ac2) ![image](https://github.com/apache/kyuubi/assets/51512358/a55450ff-49cf-4b4a-9b90-91dd02982aa5) 2. random: ![image](https://github.com/apache/kyuubi/assets/51512358/d65b14c1-1b02-4436-8843-27b2e55d27ce) ![image](https://github.com/apache/kyuubi/assets/51512358/0524a30c-c2c3-464e-8453-84f3f1a74fb1) ![image](https://github.com/apache/kyuubi/assets/51512358/12feb93e-b743-4a43-821d-454f3c1af336) 3. YourStrategy(the test case only get the first serverHost): ![image](https://github.com/apache/kyuubi/assets/51512358/2e6395c2-6496-4516-9cf6-90abc921de7f) ![image](https://github.com/apache/kyuubi/assets/51512358/72975513-48d2-4f41-8a95-95cde0302c5b) ![image](https://github.com/apache/kyuubi/assets/51512358/487951fd-de45-4e1c-861a-94e0e5564e37) #### Related Unit Tests There is no Unit Tests. --- # Checklist 📝 - [ ] This patch was not authored or co-authored using [Generative Tooling](https://www.apache.org/legal/generative-tooling.html) **Be nice. Be informative.** Closes #6213 from davidyuan1223/ha_zk_support_more_strategy. Closes #6034 961d3e9 [Bowen Liang] rename ServerStrategyFactory to ServerSelectStrategyFactory 353f940 [Bowen Liang] repeat 8822ad4 [Bowen Liang] repeat 6193394 [Bowen Liang] nit e94f9e9 [Bowen Liang] nit 40f427a [Bowen Liang] rename StrategyFactory to StrategyFactoryServerStrategyFactory 7668f99 [Bowen Liang] test name e194ea6 [Bowen Liang] remove ZooKeeperHiveClientException from method signature of chooseServer 265965e [Bowen Liang] polling b39c567 [Bowen Liang] style 1ab79b4 [Bowen Liang] strategyName 8f8ca28 [Bowen Liang] nit 228bf10 [Bowen Liang] rename parameter zooKeeperStrategy to serverSelectStrategy 125c823 [Bowen Liang] rename ChooseServerStrategy to ServerSelectStrategy b4aeb3d [Bowen Liang] repeat testing on pollingChooseStrategy 4655480 [davidyuan] update 09a84f1 [david yuan] remove the distirbuted lock 93f4a26 [davidyuan] remove reset 7b0c1b8 [davidyuan] fix var not valid and counter getAndIncrement c95382a [davidyuan] fix var not valid and counter getAndIncrement 9ed2cac [david yuan] remove test comment 8eddd76 [davidyuan] Add Strategy Unit Test Case and fix the polling strategy counter begin with 0 73952f8 [davidyuan] Kyuubi Server HA&ZK get server from serverHosts support more strategy 97b9597 [davidyuan] Kyuubi Server HA&ZK get server from serverHosts support more strategy ee5a9ad [davidyuan] Kyuubi Server HA&ZK get server from serverHosts support more strategy 6a04453 [davidyuan] Kyuubi Server HA&ZK get server from serverHosts support more strategy 1892f14 [davidyuan] add common method to get session level config 7c0c605 [yuanfuyuan] fix_4186 Lead-authored-by: Bowen Liang <[email protected]> Co-authored-by: davidyuan <[email protected]> Co-authored-by: davidyuan <[email protected]> Co-authored-by: david yuan <[email protected]> Co-authored-by: yuanfuyuan <[email protected]> Signed-off-by: Bowen Liang <[email protected]>
1 parent e446724 commit 8862767

File tree

8 files changed

+225
-3
lines changed

8 files changed

+225
-3
lines changed

kyuubi-ha/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,13 @@
132132
<version>${project.version}</version>
133133
<scope>test</scope>
134134
</dependency>
135+
136+
<dependency>
137+
<groupId>org.apache.kyuubi</groupId>
138+
<artifactId>kyuubi-hive-jdbc</artifactId>
139+
<version>${project.version}</version>
140+
<scope>test</scope>
141+
</dependency>
135142
</dependencies>
136143

137144
<build>

kyuubi-ha/src/test/scala/org/apache/kyuubi/ha/client/zookeeper/ZookeeperDiscoveryClientSuite.scala

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ import org.apache.kyuubi.ha.HighAvailabilityConf._
3434
import org.apache.kyuubi.ha.client._
3535
import org.apache.kyuubi.ha.client.DiscoveryClientProvider.withDiscoveryClient
3636
import org.apache.kyuubi.ha.client.zookeeper.ZookeeperClientProvider._
37+
import org.apache.kyuubi.jdbc.hive.strategy.{ServerSelectStrategy, ServerSelectStrategyFactory}
3738
import org.apache.kyuubi.service._
38-
import org.apache.kyuubi.shaded.curator.framework.CuratorFrameworkFactory
39+
import org.apache.kyuubi.shaded.curator.framework.{CuratorFramework, CuratorFrameworkFactory}
3940
import org.apache.kyuubi.shaded.curator.retry.ExponentialBackoffRetry
4041
import org.apache.kyuubi.shaded.zookeeper.ZooDefs
4142
import org.apache.kyuubi.shaded.zookeeper.data.ACL
@@ -227,4 +228,41 @@ abstract class ZookeeperDiscoveryClientSuite extends DiscoveryClientTests
227228
discovery.stop()
228229
}
229230
}
231+
232+
test("server select strategy with zookeeper") {
233+
val zkClient = CuratorFrameworkFactory.builder()
234+
.connectString(getConnectString)
235+
.sessionTimeoutMs(5000)
236+
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
237+
.build
238+
zkClient.start()
239+
240+
val namespace = "kyuubi-strategy-test"
241+
val testServerHosts = Seq(
242+
"testNode1",
243+
"testNode2",
244+
"testNode3").asJava
245+
// test polling strategy
246+
val pollingStrategy = ServerSelectStrategyFactory.createStrategy("polling")
247+
1 to testServerHosts.size() * 2 foreach { _ =>
248+
assertResult(f"testNode1")(pollingStrategy.chooseServer(testServerHosts, zkClient, namespace))
249+
assertResult(f"testNode2")(pollingStrategy.chooseServer(testServerHosts, zkClient, namespace))
250+
assertResult(f"testNode3")(pollingStrategy.chooseServer(testServerHosts, zkClient, namespace))
251+
}
252+
253+
// test only get first serverHost strategy
254+
val customStrategy = new ServerSelectStrategy {
255+
override def chooseServer(
256+
serverHosts: util.List[String],
257+
zkClient: CuratorFramework,
258+
namespace: String): String = serverHosts.get(0)
259+
}
260+
1 to testServerHosts.size() * 2 foreach { _ =>
261+
assertResult("testNode1") {
262+
customStrategy.chooseServer(testServerHosts, zkClient, namespace)
263+
}
264+
}
265+
266+
zkClient.close()
267+
}
230268
}

kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/JdbcConnectionParams.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public class JdbcConnectionParams {
7979
// Use ZooKeeper for indirection while using dynamic service discovery
8080
static final String SERVICE_DISCOVERY_MODE_ZOOKEEPER = "zooKeeper";
8181
static final String ZOOKEEPER_NAMESPACE = "zooKeeperNamespace";
82+
static final String SERVER_SELECT_STRATEGY = "serverSelectStrategy";
8283
// Default namespace value on ZooKeeper.
8384
// This value is used if the param "zooKeeperNamespace" is not specified in the JDBC Uri.
8485
static final String ZOOKEEPER_DEFAULT_NAMESPACE = "hiveserver2";

kyuubi-hive-jdbc/src/main/java/org/apache/kyuubi/jdbc/hive/ZooKeeperHiveClientHelper.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@
2222
import java.util.ArrayList;
2323
import java.util.List;
2424
import java.util.Map;
25-
import java.util.concurrent.ThreadLocalRandom;
2625
import java.util.regex.Matcher;
2726
import java.util.regex.Pattern;
27+
import org.apache.kyuubi.jdbc.hive.strategy.ServerSelectStrategy;
28+
import org.apache.kyuubi.jdbc.hive.strategy.ServerSelectStrategyFactory;
29+
import org.apache.kyuubi.jdbc.hive.strategy.zk.RandomSelectStrategy;
2830
import org.apache.kyuubi.shaded.curator.framework.CuratorFramework;
2931
import org.apache.kyuubi.shaded.curator.framework.CuratorFrameworkFactory;
3032
import org.apache.kyuubi.shaded.curator.retry.ExponentialBackoffRetry;
@@ -111,7 +113,7 @@ static void configureConnParams(JdbcConnectionParams connParams)
111113
try (CuratorFramework zooKeeperClient = getZkClient(connParams)) {
112114
List<String> serverHosts = getServerHosts(connParams, zooKeeperClient);
113115
// Now pick a server node randomly
114-
String serverNode = serverHosts.get(ThreadLocalRandom.current().nextInt(serverHosts.size()));
116+
String serverNode = chooseServer(connParams, serverHosts, zooKeeperClient);
115117
updateParamsWithZKServerNode(connParams, zooKeeperClient, serverNode);
116118
} catch (Exception e) {
117119
throw new ZooKeeperHiveClientException(
@@ -120,6 +122,22 @@ static void configureConnParams(JdbcConnectionParams connParams)
120122
// Close the client connection with ZooKeeper
121123
}
122124

125+
private static String chooseServer(
126+
JdbcConnectionParams connParams, List<String> serverHosts, CuratorFramework zkClient) {
127+
String zooKeeperNamespace = getZooKeeperNamespace(connParams);
128+
String strategyName =
129+
connParams
130+
.getSessionVars()
131+
.getOrDefault(
132+
JdbcConnectionParams.SERVER_SELECT_STRATEGY, RandomSelectStrategy.strategyName);
133+
try {
134+
ServerSelectStrategy strategy = ServerSelectStrategyFactory.createStrategy(strategyName);
135+
return strategy.chooseServer(serverHosts, zkClient, zooKeeperNamespace);
136+
} catch (Exception e) {
137+
throw new RuntimeException("Failed to choose server with strategy " + strategyName, e);
138+
}
139+
}
140+
123141
static List<JdbcConnectionParams> getDirectParamsList(JdbcConnectionParams connParams)
124142
throws ZooKeeperHiveClientException {
125143
try (CuratorFramework zooKeeperClient = getZkClient(connParams)) {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.kyuubi.jdbc.hive.strategy;
19+
20+
import java.util.List;
21+
import org.apache.kyuubi.shaded.curator.framework.CuratorFramework;
22+
23+
public interface ServerSelectStrategy {
24+
String chooseServer(List<String> serverHosts, CuratorFramework zkClient, String namespace);
25+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.kyuubi.jdbc.hive.strategy;
19+
20+
import java.lang.reflect.Constructor;
21+
import org.apache.kyuubi.jdbc.hive.strategy.zk.PollingSelectStrategy;
22+
import org.apache.kyuubi.jdbc.hive.strategy.zk.RandomSelectStrategy;
23+
24+
public class ServerSelectStrategyFactory {
25+
public static ServerSelectStrategy createStrategy(String strategyName) {
26+
try {
27+
switch (strategyName) {
28+
case PollingSelectStrategy.strategyName:
29+
return new PollingSelectStrategy();
30+
case RandomSelectStrategy.strategyName:
31+
return new RandomSelectStrategy();
32+
default:
33+
Class<?> clazz = Class.forName(strategyName);
34+
if (ServerSelectStrategy.class.isAssignableFrom(clazz)) {
35+
Constructor<? extends ServerSelectStrategy> constructor =
36+
clazz.asSubclass(ServerSelectStrategy.class).getConstructor();
37+
return constructor.newInstance();
38+
} else {
39+
throw new ClassNotFoundException(
40+
"The loaded class does not implement ServerSelectStrategy");
41+
}
42+
}
43+
} catch (Exception e) {
44+
throw new RuntimeException("Failed to init server select strategy", e);
45+
}
46+
}
47+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.kyuubi.jdbc.hive.strategy.zk;
19+
20+
import java.util.List;
21+
import org.apache.kyuubi.jdbc.hive.strategy.ServerSelectStrategy;
22+
import org.apache.kyuubi.shaded.curator.framework.CuratorFramework;
23+
import org.apache.kyuubi.shaded.curator.framework.recipes.atomic.AtomicValue;
24+
import org.apache.kyuubi.shaded.curator.framework.recipes.atomic.DistributedAtomicInteger;
25+
import org.apache.kyuubi.shaded.curator.retry.RetryForever;
26+
27+
public class PollingSelectStrategy implements ServerSelectStrategy {
28+
public static final String strategyName = "polling";
29+
30+
private static final String COUNTER_PATH_PREFIX = "/";
31+
private static final String COUNTER_PATH_SUFFIX = "-counter";
32+
33+
@Override
34+
public String chooseServer(
35+
List<String> serverHosts, CuratorFramework zkClient, String namespace) {
36+
String counterPath = COUNTER_PATH_PREFIX + namespace + COUNTER_PATH_SUFFIX;
37+
try {
38+
return serverHosts.get(getAndIncrement(zkClient, counterPath) % serverHosts.size());
39+
} catch (Exception e) {
40+
throw new RuntimeException("Failed to choose server by polling select strategy", e);
41+
}
42+
}
43+
44+
private int getAndIncrement(CuratorFramework zkClient, String path) throws Exception {
45+
DistributedAtomicInteger dai =
46+
new DistributedAtomicInteger(zkClient, path, new RetryForever(3000));
47+
AtomicValue<Integer> atomicVal;
48+
do {
49+
atomicVal = dai.add(1);
50+
} while (atomicVal == null || !atomicVal.succeeded());
51+
return atomicVal.preValue();
52+
}
53+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.kyuubi.jdbc.hive.strategy.zk;
19+
20+
import java.util.List;
21+
import java.util.concurrent.ThreadLocalRandom;
22+
import org.apache.kyuubi.jdbc.hive.strategy.ServerSelectStrategy;
23+
import org.apache.kyuubi.shaded.curator.framework.CuratorFramework;
24+
25+
public class RandomSelectStrategy implements ServerSelectStrategy {
26+
public static final String strategyName = "random";
27+
28+
@Override
29+
public String chooseServer(
30+
List<String> serverHosts, CuratorFramework zkClient, String namespace) {
31+
return serverHosts.get(ThreadLocalRandom.current().nextInt(serverHosts.size()));
32+
}
33+
}

0 commit comments

Comments
 (0)