org.apache.hadoop.hbase.master.zksyncer.ClientZKSyncer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hbase-server Show documentation
Show all versions of hbase-server Show documentation
Server functionality for HBase
/**
*
* 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.hadoop.hbase.master.zksyncer;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.Server;
import org.apache.hadoop.hbase.util.Threads;
import org.apache.hadoop.hbase.zookeeper.ZKListener;
import org.apache.hadoop.hbase.zookeeper.ZKUtil;
import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
import org.apache.yetus.audience.InterfaceAudience;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Tracks the target znode(s) on server ZK cluster and synchronize them to client ZK cluster if
* changed
*
* The target znode(s) is given through {@link #getNodesToWatch()} method
*/
@InterfaceAudience.Private
public abstract class ClientZKSyncer extends ZKListener {
private static final Logger LOG = LoggerFactory.getLogger(ClientZKSyncer.class);
private final Server server;
private final ZKWatcher clientZkWatcher;
// We use queues and daemon threads to synchronize the data to client ZK cluster
// to avoid blocking the single event thread for watchers
private final Map> queues;
public ClientZKSyncer(ZKWatcher watcher, ZKWatcher clientZkWatcher, Server server) {
super(watcher);
this.server = server;
this.clientZkWatcher = clientZkWatcher;
this.queues = new HashMap<>();
}
/**
* Starts the syncer
* @throws KeeperException if error occurs when trying to create base nodes on client ZK
*/
public void start() throws KeeperException {
LOG.debug("Starting " + getClass().getSimpleName());
this.watcher.registerListener(this);
// create base znode on remote ZK
ZKUtil.createWithParents(clientZkWatcher, watcher.getZNodePaths().baseZNode);
// set meta znodes for client ZK
Collection nodes = getNodesToWatch();
LOG.debug("Znodes to watch: " + nodes);
// initialize queues and threads
for (String node : nodes) {
BlockingQueue queue = new ArrayBlockingQueue<>(1);
queues.put(node, queue);
Thread updater = new ClientZkUpdater(node, queue);
updater.setDaemon(true);
updater.start();
watchAndCheckExists(node);
}
}
private void watchAndCheckExists(String node) {
try {
if (ZKUtil.watchAndCheckExists(watcher, node)) {
byte[] data = ZKUtil.getDataAndWatch(watcher, node);
if (data != null) {
// put the data into queue
upsertQueue(node, data);
} else {
// It existed but now does not, should has been tracked by our watcher, ignore
LOG.debug("Found no data from " + node);
watchAndCheckExists(node);
}
} else {
// cleanup stale ZNodes on client ZK to avoid invalid requests to server
ZKUtil.deleteNodeFailSilent(clientZkWatcher, node);
}
} catch (KeeperException e) {
server.abort("Unexpected exception during initialization, aborting", e);
}
}
/**
* Update the value of the single element in queue if any, or else insert.
*
* We only need to synchronize the latest znode value to client ZK rather than synchronize each
* time
* @param data the data to write to queue
*/
private void upsertQueue(String node, byte[] data) {
BlockingQueue queue = queues.get(node);
synchronized (queue) {
queue.poll();
queue.offer(data);
}
}
/**
* Set data for client ZK and retry until succeed. Be very careful to prevent dead loop when
* modifying this method
* @param node the znode to set on client ZK
* @param data the data to set to client ZK
* @throws InterruptedException if the thread is interrupted during process
*/
private final void setDataForClientZkUntilSuccess(String node, byte[] data)
throws InterruptedException {
while (!server.isStopped()) {
try {
LOG.debug("Set data for remote " + node + ", client zk wather: " + clientZkWatcher);
ZKUtil.setData(clientZkWatcher, node, data);
break;
} catch (KeeperException.NoNodeException nne) {
// Node doesn't exist, create it and set value
try {
ZKUtil.createNodeIfNotExistsNoWatch(clientZkWatcher, node, data, CreateMode.PERSISTENT);
break;
} catch (KeeperException.ConnectionLossException
| KeeperException.SessionExpiredException ee) {
reconnectAfterExpiration();
} catch (KeeperException e) {
LOG.warn(
"Failed to create znode " + node + " due to: " + e.getMessage() + ", will retry later");
}
} catch (KeeperException.ConnectionLossException
| KeeperException.SessionExpiredException ee) {
reconnectAfterExpiration();
} catch (KeeperException e) {
LOG.debug("Failed to set data to client ZK, will retry later", e);
}
Threads.sleep(HConstants.SOCKET_RETRY_WAIT_MS);
}
}
private final void reconnectAfterExpiration() throws InterruptedException {
LOG.warn("ZK session expired or lost. Retry a new connection...");
try {
clientZkWatcher.reconnectAfterExpiration();
} catch (IOException | KeeperException e) {
LOG.warn("Failed to reconnect to client zk after session expiration, will retry later", e);
}
}
@Override
public void nodeCreated(String path) {
if (!validate(path)) {
return;
}
try {
byte[] data = ZKUtil.getDataAndWatch(watcher, path);
upsertQueue(path, data);
} catch (KeeperException e) {
LOG.warn("Unexpected exception handling nodeCreated event", e);
}
}
@Override
public void nodeDataChanged(String path) {
if (validate(path)) {
nodeCreated(path);
}
}
@Override
public synchronized void nodeDeleted(String path) {
if (validate(path)) {
try {
if (ZKUtil.watchAndCheckExists(watcher, path)) {
nodeCreated(path);
}
} catch (KeeperException e) {
LOG.warn("Unexpected exception handling nodeDeleted event for path: " + path, e);
}
}
}
/**
* Validate whether a znode path is watched by us
* @param path the path to validate
* @return true if the znode is watched by us
*/
abstract boolean validate(String path);
/**
* @return the znode(s) to watch
*/
abstract Collection getNodesToWatch();
/**
* Thread to synchronize znode data to client ZK cluster
*/
class ClientZkUpdater extends Thread {
final String znode;
final BlockingQueue queue;
public ClientZkUpdater(String znode, BlockingQueue queue) {
this.znode = znode;
this.queue = queue;
setName("ClientZKUpdater-" + znode);
}
@Override
public void run() {
while (!server.isStopped()) {
try {
byte[] data = queue.take();
setDataForClientZkUntilSuccess(znode, data);
} catch (InterruptedException e) {
if (LOG.isDebugEnabled()) {
LOG.debug(
"Interrupted while checking whether need to update meta location to client zk");
}
Thread.currentThread().interrupt();
break;
}
}
}
}
}