com.nesscomputing.service.discovery.client.internal.ServiceDiscoveryReader Maven / Gradle / Ivy
/**
* Copyright (C) 2012 Ness Computing, Inc.
*
* Licensed 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 com.nesscomputing.service.discovery.client.internal;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Charsets;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.zookeeper.AsyncCallback.DataCallback;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import com.nesscomputing.logging.Log;
import com.nesscomputing.service.discovery.client.DiscoveryClientConfig;
import com.nesscomputing.service.discovery.client.ServiceInformation;
/**
* Reads the current list of service announcements from Zookeeper and updates the
* state of the world accordingly.
*/
public class ServiceDiscoveryReader extends ServiceDiscoveryTask
{
private static final Log LOG = Log.findLog();
/** Internal list of bad announcement nodes that could not be read for whatever reason. */
private final Map badNodes = new ConcurrentHashMap();
private final StateOfTheWorldHolder stateHolder;
private final long penaltyTime;
ServiceDiscoveryReader(final DiscoveryClientConfig discoveryConfig,
final ObjectMapper objectMapper,
final StateOfTheWorldHolder stateHolder)
{
super(discoveryConfig, objectMapper);
this.stateHolder = stateHolder;
this.penaltyTime = discoveryConfig.getPenaltyTime().getMillis() * 1000000L;
}
@Override
void visit(final List childNodes, final ZooKeeper zookeeper, final long tick) throws InterruptedException
{
final Map> serviceMap = new HashMap>();
if (childNodes.size() > 0) {
final List rawServices = new ArrayList(childNodes.size());
final CountDownLatch latch = new CountDownLatch(childNodes.size());
final long now = System.nanoTime();
for (final String child : childNodes) {
final String childPath = getNodePath(child);
if (badNodes.containsKey(childPath)) {
final Long penaltyEndsTime = badNodes.get(childPath);
if (penaltyEndsTime != null && penaltyEndsTime > now) {
// Decrement the countdown latch, because there will be no callback for this
// node.
latch.countDown();
// Ignore a bad node for a while.
continue;
}
LOG.info("Unmarking %s as a bad node!", childPath);
badNodes.remove(childPath);
}
zookeeper.getData(childPath, false, new DataCallback() {
@Override
public void processResult(final int rc, final String path, final Object ctx, final byte[] data, final Stat stat) {
ServiceInformation si = null;
try {
if (data != null && data.length > 0) {
si = objectMapper.readValue(data, ServiceInformation.class);
LOG.trace("%s contains %s", path, si);
}
else {
// This can sometimes happen if a node that we want to inspect
// disappears between callback post and callback processing.
LOG.trace("Got callback but no data!");
}
}
catch (IOException ioe) {
LOG.debug(ioe, "While deserializing %s", new String(data, Charsets.UTF_8));
LOG.info("Marking %s as a bad node!", path);
// Put a bad node into the penalty box.
badNodes.put(path, now + penaltyTime);
}
finally {
synchronized (rawServices) {
if (si != null) {
rawServices.add(si);
}
}
latch.countDown();
}
}
}, null);
}
if (!latch.await(discoveryConfig.getZookeeperTimeout().getMillis(), TimeUnit.MILLISECONDS)) {
LOG.warn("Timeout waiting for callbacks, some nodes were not parsed.");
}
// Make sure that even with late callbacks, this will not throw spurious ConcurrentModificationExceptions
synchronized (rawServices) {
for (final ServiceInformation si : rawServices) {
List services = serviceMap.get(si.getServiceName());
if (services == null) {
services = new ArrayList();
serviceMap.put(si.getServiceName(), services);
}
services.add(si);
}
}
}
Map serviceGroups = Maps.newHashMap();
for (Map.Entry> entry: serviceMap.entrySet()) {
ConsistentRingGroup currentGroup = stateHolder.getState().get(entry.getKey());
//Rebuilding a group is kind of expensive, so reuse the old group if it hasn't changed
if (currentGroup != null && Sets.newHashSet(entry.getValue()).equals(Sets.newHashSet(currentGroup.getAll()))) {
serviceGroups.put(entry.getKey(), currentGroup);
} else {
serviceGroups.put(entry.getKey(), new ConsistentRingGroup(entry.getValue()));
}
}
stateHolder.setState(serviceGroups);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy