You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ka...@apache.org on 2019/03/06 01:18:14 UTC

[phoenix-queryserver] branch master updated: PHOENIX-5063 Create a new repo for the phoenix query server (#3)

This is an automated email from the ASF dual-hosted git repository.

karanmehta93 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/phoenix-queryserver.git


The following commit(s) were added to refs/heads/master by this push:
     new 8068436  PHOENIX-5063 Create a new repo for the phoenix query server (#3)
8068436 is described below

commit 8068436b9b9c89f2b15736492b582604ac1fb80d
Author: karanmehta93 <ka...@gmail.com>
AuthorDate: Tue Mar 5 17:18:10 2019 -0800

    PHOENIX-5063 Create a new repo for the phoenix query server (#3)
    
    Added load-balancer module
---
 load-balancer/pom.xml                              |  85 ++++++++++
 .../phoenix/end2end/LoadBalancerEnd2EndIT.java     | 144 +++++++++++++++++
 .../service/LoadBalanceZookeeperConfImpl.java      | 103 ++++++++++++
 .../phoenix/loadbalancer/service/LoadBalancer.java | 178 +++++++++++++++++++++
 .../queryserver/register/ZookeeperRegistry.java    |  72 +++++++++
 ...x.loadbalancer.service.LoadBalanceZookeeperConf |   1 +
 ...rg.apache.phoenix.queryserver.register.Registry |   1 +
 pom.xml                                            |  23 +++
 8 files changed, 607 insertions(+)

diff --git a/load-balancer/pom.xml b/load-balancer/pom.xml
new file mode 100644
index 0000000..cb893f7
--- /dev/null
+++ b/load-balancer/pom.xml
@@ -0,0 +1,85 @@
+<?xml version='1.0'?>
+<!--
+
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.phoenix</groupId>
+    <artifactId>phoenix-queryserver</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>load-balancer</artifactId>
+  <name>Phoenix Load Balancer</name>
+  <description>A Load balancer which routes calls to Phoenix Query Server</description>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-common</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.curator</groupId>
+      <artifactId>curator-client</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.phoenix</groupId>
+      <artifactId>queryserver</artifactId>
+    </dependency>
+    <!-- for tests -->
+    <dependency>
+      <groupId>org.apache.curator</groupId>
+      <artifactId>curator-test</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>verify</phase>
+            <goals>
+              <goal>jar-no-fork</goal>
+              <goal>test-jar-no-fork</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.rat</groupId>
+        <artifactId>apache-rat-plugin</artifactId>
+        <configuration>
+          <excludes>
+            <exclude>src/main/resources/META-INF/services/org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf</exclude>
+            <exclude>src/main/resources/META-INF/services/org.apache.phoenix.queryserver.register.Registry</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+</project>
diff --git a/load-balancer/src/it/java/org/apache/phoenix/end2end/LoadBalancerEnd2EndIT.java b/load-balancer/src/it/java/org/apache/phoenix/end2end/LoadBalancerEnd2EndIT.java
new file mode 100644
index 0000000..a5e2c9b
--- /dev/null
+++ b/load-balancer/src/it/java/org/apache/phoenix/end2end/LoadBalancerEnd2EndIT.java
@@ -0,0 +1,144 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end;
+
+import com.google.common.net.HostAndPort;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.curator.CuratorZookeeperClient;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.curator.TestingServer;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.phoenix.loadbalancer.service.LoadBalancer;
+import org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf;
+import org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConfImpl;
+import org.apache.phoenix.queryserver.register.Registry;
+import org.apache.phoenix.queryserver.register.ZookeeperRegistry;
+import org.apache.zookeeper.KeeperException;
+import org.junit.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class LoadBalancerEnd2EndIT {
+    private static TestingServer testingServer;
+    private static CuratorFramework curatorFramework;
+    private static final Log LOG = LogFactory.getLog(LoadBalancerEnd2EndIT.class);
+    private static final LoadBalanceZookeeperConf LOAD_BALANCER_CONFIGURATION = new LoadBalanceZookeeperConfImpl();
+    private static  String path;
+    private static LoadBalancer loadBalancer;
+    private static HostAndPort pqs1 = HostAndPort.fromParts("localhost",1000);
+    private static HostAndPort pqs2 = HostAndPort.fromParts("localhost",2000);
+    private static HostAndPort pqs3 = HostAndPort.fromParts("localhost",3000);
+    public static String zkConnectString;
+    public static Registry registry;
+
+    @BeforeClass
+    public  static void setup() throws Exception{
+
+        registry = new ZookeeperRegistry();
+        zkConnectString = LOAD_BALANCER_CONFIGURATION.getZkConnectString();
+        int port = Integer.parseInt(zkConnectString.split(":")[1]);
+        testingServer = new TestingServer(port);
+        testingServer.start();
+
+        path = LOAD_BALANCER_CONFIGURATION.getParentPath();
+        curatorFramework = CuratorFrameworkFactory.newClient(zkConnectString,
+                new ExponentialBackoffRetry(1000, 3));
+        curatorFramework.start();
+        createNodeForTesting(Arrays.asList(pqs1,pqs2,pqs3));
+        curatorFramework.setACL().withACL(LOAD_BALANCER_CONFIGURATION.getAcls());
+        loadBalancer = LoadBalancer.getLoadBalancer();
+    }
+
+    @AfterClass
+    public  static void tearDown() throws Exception {
+        CloseableUtils.closeQuietly(curatorFramework);
+        CloseableUtils.closeQuietly(testingServer);
+    }
+
+    private  static void createNodeForTesting(List<HostAndPort> pqsNodes) throws Exception{
+        for(HostAndPort pqs:pqsNodes) {
+            registry.registerServer(LOAD_BALANCER_CONFIGURATION,pqs.getPort(),zkConnectString,pqs.getHostText());
+        }
+        curatorFramework.getChildren().forPath(LOAD_BALANCER_CONFIGURATION.getParentPath()).size();
+    }
+
+
+    @Test
+    public void testGetAllServiceLocation() throws Exception {
+        Assert.assertNotNull(loadBalancer);
+        List<HostAndPort> serviceLocations = loadBalancer.getAllServiceLocation();
+        Assert.assertTrue(" must contains 3 service location",serviceLocations.size() == 3);
+    }
+
+    @Test
+    public void testGetSingleServiceLocation() throws Exception {
+        Assert.assertNotNull(loadBalancer);
+        HostAndPort serviceLocation = loadBalancer.getSingleServiceLocation();
+        Assert.assertNotNull(serviceLocation);
+    }
+
+    @Test(expected=Exception.class)
+    public void testZookeeperDown() throws Exception{
+       testingServer.stop();
+       CuratorZookeeperClient zookeeperClient =  curatorFramework.getZookeeperClient();
+        //check to see if zookeeper is really down.
+       while (zookeeperClient.isConnected()){
+            Thread.sleep(1000);
+       };
+       loadBalancer.getSingleServiceLocation();
+    }
+
+    @Test(expected = KeeperException.NoNodeException.class)
+    public void testNoPhoenixQueryServerNodeInZookeeper() throws Exception{
+        List<HostAndPort> hostAndPorts = Arrays.asList(pqs1, pqs2, pqs3);
+        for(HostAndPort pqs: hostAndPorts) {
+            String fullPathToNode = LOAD_BALANCER_CONFIGURATION.getFullPathToNode(pqs);
+            curatorFramework.delete().deletingChildrenIfNeeded().forPath(fullPathToNode);
+            while (curatorFramework.checkExists().forPath(fullPathToNode) != null){
+                //wait for the node to deleted
+                Thread.sleep(1000);
+            };
+        }
+        //delete the parent
+        curatorFramework.delete().forPath(path);
+        // should throw an exception as there is
+        // no node in the zookeeper
+        try {
+            loadBalancer.getSingleServiceLocation();
+        } catch(Exception e) {
+            throw e;
+        } finally {
+            // need to create node for other tests to run.
+            createNodeForTesting(hostAndPorts);
+        }
+    }
+
+    @Test
+    public void testSingletonPropertyForLoadBalancer(){
+        LoadBalancer anotherloadBalancerRef = LoadBalancer.getLoadBalancer();
+        Assert.assertTrue(" the load balancer is not singleton",loadBalancer == anotherloadBalancerRef );
+    }
+
+
+
+}
diff --git a/load-balancer/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalanceZookeeperConfImpl.java b/load-balancer/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalanceZookeeperConfImpl.java
new file mode 100644
index 0000000..98e2682
--- /dev/null
+++ b/load-balancer/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalanceZookeeperConfImpl.java
@@ -0,0 +1,103 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.phoenix.loadbalancer.service;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.net.HostAndPort;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.query.QueryServicesOptions;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Id;
+
+import java.util.Arrays;
+import java.util.List;
+
+
+
+public class LoadBalanceZookeeperConfImpl implements LoadBalanceZookeeperConf {
+
+        private Configuration configuration;
+
+        public LoadBalanceZookeeperConfImpl() {
+           this.configuration = HBaseConfiguration.create();
+        }
+
+        public LoadBalanceZookeeperConfImpl(Configuration configuration) {
+            this.configuration = configuration;
+        }
+
+        @VisibleForTesting
+        public void setConfiguration(Configuration configuration) {
+            this.configuration = configuration;
+        }
+
+        @Override
+        public String getQueryServerBasePath(){
+            return configuration.get(QueryServices.PHOENIX_QUERY_SERVER_CLUSTER_BASE_PATH,
+                    QueryServicesOptions.DEFAULT_PHOENIX_QUERY_SERVER_CLUSTER_BASE_PATH);
+        }
+
+        @Override
+        public String getServiceName(){
+            return configuration.get(QueryServices.PHOENIX_QUERY_SERVER_SERVICE_NAME,
+                    QueryServicesOptions.DEFAULT_PHOENIX_QUERY_SERVER_SERVICE_NAME);
+        }
+
+        @Override
+        public String getZkConnectString(){
+            return String.format("%s:%s",configuration.get(QueryServices.ZOOKEEPER_QUORUM_ATTRIB,
+                    "localhost"),configuration.get(QueryServices.ZOOKEEPER_PORT_ATTRIB,"2181"));
+        }
+
+        private String getZkLbUserName(){
+            return configuration.get(QueryServices.PHOENIX_QUERY_SERVER_ZK_ACL_USERNAME,
+                    QueryServicesOptions.DEFAULT_PHOENIX_QUERY_SERVER_ZK_ACL_USERNAME);
+        }
+
+        private String getZkLbPassword(){
+            return configuration.get(QueryServices.PHOENIX_QUERY_SERVER_ZK_ACL_PASSWORD,
+                    QueryServicesOptions.DEFAULT_PHOENIX_QUERY_SERVER_ZK_ACL_PASSWORD);
+        }
+
+        @Override
+        public List<ACL> getAcls() {
+            ACL acl = new ACL();
+            acl.setId(new Id("digest",getZkLbUserName()+":"+getZkLbPassword()));
+            acl.setPerms(ZooDefs.Perms.READ);
+            return Arrays.asList(acl);
+        }
+
+        @Override
+        public String getParentPath() {
+            String path = String.format("%s/%s",getQueryServerBasePath(),getServiceName());
+            return path;
+        }
+
+        @Override
+        public String getFullPathToNode(HostAndPort hostAndPort) {
+            String path = String.format("%s/%s",getParentPath()
+                    ,hostAndPort.toString());
+            return path;
+        }
+}
+
diff --git a/load-balancer/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalancer.java b/load-balancer/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalancer.java
new file mode 100644
index 0000000..23e9025
--- /dev/null
+++ b/load-balancer/src/main/java/org/apache/phoenix/loadbalancer/service/LoadBalancer.java
@@ -0,0 +1,178 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.loadbalancer.service;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.net.HostAndPort;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.api.UnhandledErrorListener;
+import org.apache.curator.framework.recipes.cache.PathChildrenCache;
+import org.apache.curator.framework.state.ConnectionState;
+import org.apache.curator.framework.state.ConnectionStateListener;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+
+import java.net.ConnectException;
+import java.util.ArrayList;
+import java.util.concurrent.ThreadLocalRandom;
+import java.io.Closeable;
+import java.util.List;
+
+/**
+ * LoadBalancer class is singleton , used by the client
+ * to find out about various location of PQS servers.
+ * The client needs to configure the HBase-site.xml with the needed
+ * properties i.e. location of zookeeper ( or service locator), username, password etc.
+ */
+public class LoadBalancer {
+
+    private static final LoadBalanceZookeeperConf CONFIG = new LoadBalanceZookeeperConfImpl(HBaseConfiguration.create());
+    private static CuratorFramework curaFramework = null;
+    protected static final Log LOG = LogFactory.getLog(LoadBalancer.class);
+    private static PathChildrenCache   cache = null;
+    private static final LoadBalancer loadBalancer = new LoadBalancer();
+    private ConnectionStateListener connectionStateListener = null;
+    private UnhandledErrorListener unhandledErrorListener = null;
+    private List<Closeable> closeAbles = Lists.newArrayList();
+
+    private LoadBalancer()  {
+        try  {
+            start();
+        }catch(Exception ex){
+            LOG.error("Exception while creating a zookeeper clients and cache",ex);
+            if ((curaFramework != null) && (connectionStateListener != null)){
+                curaFramework.getConnectionStateListenable()
+                        .removeListener(connectionStateListener);
+            }
+            if ((curaFramework != null) && (unhandledErrorListener != null)){
+                curaFramework.getUnhandledErrorListenable()
+                        .removeListener(unhandledErrorListener);
+            }
+            for (Closeable closeable : closeAbles) {
+                CloseableUtils.closeQuietly(closeable);
+            }
+        }
+    }
+
+
+
+    /**
+     * Return Singleton Load Balancer every single time.
+     * @return LoadBalancer
+     */
+    public static LoadBalancer getLoadBalancer() {
+        return loadBalancer;
+    }
+
+    /**
+     * It returns the location of Phoenix Query Server
+     * in form of Guava <a href="https://google.github.io/guava/releases/19.0/api/docs/com/google/common/net/HostAndPort.html">HostAndPort</a>
+     * from the cache. The client should catch Exception incase
+     * the method is unable to fetch PQS location due to network failure or
+     * in-correct configuration issues.
+     * @return - return Guava HostAndPort. See <a href="http://google.com">http://google.com</a>
+     * @throws Exception
+     */
+    public HostAndPort getSingleServiceLocation()  throws Exception{
+        List<HostAndPort> childNodes = conductSanityCheckAndReturn();
+        // get an random connect string
+        int i = ThreadLocalRandom.current().nextInt(0, childNodes.size());
+        return childNodes.get(i);
+    }
+
+    /**
+     * return locations of all Phoenix Query Servers
+     * in the form of a List of PQS servers  <a href="https://google.github.io/guava/releases/19.0/api/docs/com/google/common/net/HostAndPort.html">HostAndPort</a>
+     * @return - HostAndPort
+     * @throws Exception
+     */
+    public List<HostAndPort> getAllServiceLocation()  throws Exception{
+        return conductSanityCheckAndReturn();
+    }
+
+    private List<HostAndPort> conductSanityCheckAndReturn() throws Exception{
+        Preconditions.checkNotNull(curaFramework
+                ," curator framework in not initialized ");
+        Preconditions.checkNotNull(cache," cache value is not initialized");
+        boolean connected = curaFramework.getZookeeperClient().isConnected();
+        if (!connected) {
+            String message = " Zookeeper seems to be down. The data is stale ";
+            ConnectException exception =
+                    new ConnectException(message);
+            LOG.error(message, exception);
+            throw exception;
+        }
+        List<String> currentNodes = curaFramework.getChildren().forPath(CONFIG.getParentPath());
+        List<HostAndPort> returnNodes = new ArrayList<>();
+        String nodeAsString = null;
+        for(String node:currentNodes) {
+            try {
+                returnNodes.add(HostAndPort.fromString(node));
+            } catch(Throwable ex) {
+                LOG.error(" something wrong with node string "+nodeAsString,ex);
+            }
+        }
+        return returnNodes;
+    }
+    private String getZkConnectString(){
+        return CONFIG.getZkConnectString();
+    }
+
+    private ConnectionStateListener getConnectionStateListener(){
+        return new ConnectionStateListener() {
+            @Override
+            public void stateChanged(CuratorFramework client, ConnectionState newState) {
+                if (!newState.isConnected()) {
+                    LOG.error( " connection to zookeeper broken. It is in  "+ newState.name()+" state.");
+                }
+            }
+        };
+    }
+
+    private UnhandledErrorListener getUnhandledErrorListener(){
+        return new UnhandledErrorListener() {
+            @Override
+            public void unhandledError(String message, Throwable e) {
+                LOG.error("unhandled exception:  "+ message,e);
+            }
+        };
+    }
+
+    private void start() throws Exception{
+        curaFramework = CuratorFrameworkFactory.newClient(getZkConnectString(),
+                new ExponentialBackoffRetry(1000, 3));
+        curaFramework.start();
+        curaFramework.setACL().withACL(CONFIG.getAcls());
+        connectionStateListener = getConnectionStateListener();
+        curaFramework.getConnectionStateListenable()
+                .addListener(connectionStateListener);
+        unhandledErrorListener = getUnhandledErrorListener();
+        curaFramework.getUnhandledErrorListenable()
+                .addListener(unhandledErrorListener);
+        cache = new PathChildrenCache(curaFramework, CONFIG.getParentPath(), true);
+        cache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
+        closeAbles.add(cache);
+        closeAbles.add(curaFramework);
+    }
+}
diff --git a/load-balancer/src/main/java/org/apache/phoenix/queryserver/register/ZookeeperRegistry.java b/load-balancer/src/main/java/org/apache/phoenix/queryserver/register/ZookeeperRegistry.java
new file mode 100644
index 0000000..8aee177
--- /dev/null
+++ b/load-balancer/src/main/java/org/apache/phoenix/queryserver/register/ZookeeperRegistry.java
@@ -0,0 +1,72 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.queryserver.register;
+
+
+import com.google.common.net.HostAndPort;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.curator.framework.CuratorFramework;
+import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.retry.ExponentialBackoffRetry;
+import org.apache.curator.utils.CloseableUtils;
+import org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf;
+import org.apache.phoenix.queryserver.server.QueryServer;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.data.Stat;
+
+import java.nio.charset.StandardCharsets;
+
+
+public class ZookeeperRegistry implements Registry {
+
+    private static final Log LOG = LogFactory.getLog(ZookeeperRegistry.class);
+    private CuratorFramework client;
+
+    public ZookeeperRegistry(){}
+
+    @Override
+    public  void registerServer(LoadBalanceZookeeperConf configuration, int pqsPort,
+                                String zookeeperConnectString, String pqsHost)
+            throws Exception {
+
+        this.client = CuratorFrameworkFactory.newClient(zookeeperConnectString,
+                new ExponentialBackoffRetry(1000,10));
+        this.client.start();
+        HostAndPort hostAndPort = HostAndPort.fromParts(pqsHost,pqsPort);
+        String path = configuration.getFullPathToNode(hostAndPort);
+        String node = hostAndPort.toString();
+        this.client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path
+                ,node.getBytes(StandardCharsets.UTF_8));
+        Stat stat = this.client.setACL().withACL(configuration.getAcls()).forPath(path);
+        if (stat != null) {
+            LOG.info(" node created with right ACL");
+        }
+        else {
+            LOG.error("could not create node with right ACL. So, system would exit now.");
+            throw new RuntimeException(" Unable to connect to Zookeeper");
+        }
+
+    }
+
+    @Override
+    public void unRegisterServer() throws Exception {
+        CloseableUtils.closeQuietly(this.client);
+    }
+}
diff --git a/load-balancer/src/main/resources/META-INF/services/org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf b/load-balancer/src/main/resources/META-INF/services/org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf
new file mode 100644
index 0000000..4cc6ea4
--- /dev/null
+++ b/load-balancer/src/main/resources/META-INF/services/org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConf
@@ -0,0 +1 @@
+org.apache.phoenix.loadbalancer.service.LoadBalanceZookeeperConfImpl
\ No newline at end of file
diff --git a/load-balancer/src/main/resources/META-INF/services/org.apache.phoenix.queryserver.register.Registry b/load-balancer/src/main/resources/META-INF/services/org.apache.phoenix.queryserver.register.Registry
new file mode 100644
index 0000000..05e1006
--- /dev/null
+++ b/load-balancer/src/main/resources/META-INF/services/org.apache.phoenix.queryserver.register.Registry
@@ -0,0 +1 @@
+org.apache.phoenix.queryserver.register.ZookeeperRegistry
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 99040bf..0f87d75 100644
--- a/pom.xml
+++ b/pom.xml
@@ -27,6 +27,7 @@
     <modules>
         <module>queryserver</module>
         <module>queryserver-client</module>
+        <module>load-balancer</module>
         <module>assembly</module>
         <module>phoenix-client</module>
     </modules>
@@ -51,6 +52,7 @@
         <!-- Hadoop Versions -->
         <hbase.version>1.4.0</hbase.version>
         <hadoop-two.version>2.7.5</hadoop-two.version>
+        <curator.version>2.12.0</curator.version>
         <phoenix.version>4.15.0-HBase-1.4-SNAPSHOT</phoenix.version>
 
         <!-- Dependency versions -->
@@ -402,13 +404,24 @@
             </dependency>
             <dependency>
                 <groupId>org.apache.phoenix</groupId>
+                <artifactId>queryserver</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.phoenix</groupId>
                 <artifactId>queryserver-client</artifactId>
                 <version>${project.version}</version>
             </dependency>
 
+
             <!-- HBase dependencies -->
             <dependency>
                 <groupId>org.apache.hbase</groupId>
+                <artifactId>hbase-common</artifactId>
+                <version>${hbase.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.hbase</groupId>
                 <artifactId>hbase-testing-util</artifactId>
                 <version>${hbase.version}</version>
                 <scope>test</scope>
@@ -489,6 +502,16 @@
 
             <!-- General Dependencies -->
             <dependency>
+                <groupId>org.apache.curator</groupId>
+                <artifactId>curator-client</artifactId>
+                <version>${curator.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.curator</groupId>
+                <artifactId>curator-test</artifactId>
+                <version>${curator.version}</version>
+            </dependency>
+            <dependency>
                 <groupId>org.apache.pig</groupId>
                 <artifactId>pig</artifactId>
                 <version>${pig.version}</version>