Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* 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.curator.ensemble.exhibitor;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.curator.RetryLoop;
import org.apache.curator.RetryPolicy;
import org.apache.curator.ensemble.EnsembleProvider;
import org.apache.curator.utils.ThreadUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* Ensemble provider that polls a cluster of Exhibitor (https://github.com/Netflix/exhibitor)
* instances for the connection string.
* If the set of instances should change, new ZooKeeper connections will use the new connection
* string.
*/
public class ExhibitorEnsembleProvider implements EnsembleProvider
{
private final Logger log = LoggerFactory.getLogger(getClass());
private final AtomicReference exhibitors = new AtomicReference();
private final AtomicReference masterExhibitors = new AtomicReference();
private final ExhibitorRestClient restClient;
private final String restUriPath;
private final int pollingMs;
private final RetryPolicy retryPolicy;
private final ScheduledExecutorService service = ThreadUtils.newSingleThreadScheduledExecutor("ExhibitorEnsembleProvider");
private final Random random = new Random();
private final AtomicReference connectionString = new AtomicReference("");
private final AtomicReference state = new AtomicReference(State.LATENT);
private static final String MIME_TYPE = "application/x-www-form-urlencoded";
private static final String VALUE_PORT = "port";
private static final String VALUE_COUNT = "count";
private static final String VALUE_SERVER_PREFIX = "server";
private enum State
{
LATENT,
STARTED,
CLOSED
}
/**
* @param exhibitors the current set of exhibitor instances (can be changed later via {@link #setExhibitors(Exhibitors)})
* @param restClient the rest client to use (use {@link DefaultExhibitorRestClient} for most cases)
* @param restUriPath the path of the REST call used to get the server set. Usually: /exhibitor/v1/cluster/list
* @param pollingMs how ofter to poll the exhibitors for the list
* @param retryPolicy retry policy to use when connecting to the exhibitors
*/
public ExhibitorEnsembleProvider(Exhibitors exhibitors, ExhibitorRestClient restClient, String restUriPath, int pollingMs, RetryPolicy retryPolicy)
{
this.exhibitors.set(exhibitors);
this.masterExhibitors.set(exhibitors);
this.restClient = restClient;
this.restUriPath = restUriPath;
this.pollingMs = pollingMs;
this.retryPolicy = retryPolicy;
}
/**
* Change the set of exhibitors to poll
*
* @param newExhibitors new set
*/
public void setExhibitors(Exhibitors newExhibitors)
{
exhibitors.set(newExhibitors);
masterExhibitors.set(newExhibitors);
}
/**
* Can be called prior to starting the Curator instance to set the current connection string
*
* @throws Exception errors
*/
public void pollForInitialEnsemble() throws Exception
{
Preconditions.checkState(state.get() == State.LATENT, "Cannot be called after start()");
poll();
}
@Override
public void start() throws Exception
{
Preconditions.checkState(state.compareAndSet(State.LATENT, State.STARTED), "Cannot be started more than once");
service.scheduleWithFixedDelay
(
new Runnable()
{
@Override
public void run()
{
poll();
}
},
pollingMs,
pollingMs,
TimeUnit.MILLISECONDS
);
}
@Override
public void close() throws IOException
{
Preconditions.checkState(state.compareAndSet(State.STARTED, State.CLOSED), "Already closed or has not been started");
service.shutdownNow();
}
@Override
public String getConnectionString()
{
return connectionString.get();
}
@VisibleForTesting
protected void poll()
{
Exhibitors localExhibitors = exhibitors.get();
Map values = queryExhibitors(localExhibitors);
int count = getCountFromValues(values);
if ( count == 0 )
{
log.warn("0 count returned from Exhibitors. Using backup connection values.");
values = useBackup(localExhibitors);
count = getCountFromValues(values);
}
if ( count > 0 )
{
int port = Integer.parseInt(values.get(VALUE_PORT));
StringBuilder newConnectionString = new StringBuilder();
List newHostnames = Lists.newArrayList();
for ( int i = 0; i < count; ++i )
{
if ( newConnectionString.length() > 0 )
{
newConnectionString.append(",");
}
String server = values.get(VALUE_SERVER_PREFIX + i);
newConnectionString.append(server).append(":").append(port);
newHostnames.add(server);
}
String newConnectionStringValue = newConnectionString.toString();
if ( !newConnectionStringValue.equals(connectionString.get()) )
{
log.info(String.format("Connection string has changed. Old value (%s), new value (%s)", connectionString.get(), newConnectionStringValue));
}
Exhibitors newExhibitors = new Exhibitors
(
newHostnames,
localExhibitors.getRestPort(),
new Exhibitors.BackupConnectionStringProvider()
{
@Override
public String getBackupConnectionString() throws Exception
{
return masterExhibitors.get().getBackupConnectionString(); // this may be overloaded by clients. Make sure there is always a method call to get the value.
}
}
);
connectionString.set(newConnectionStringValue);
exhibitors.set(newExhibitors);
}
}
private int getCountFromValues(Map values)
{
try
{
return Integer.parseInt(values.get(VALUE_COUNT));
}
catch ( NumberFormatException e )
{
// ignore
}
return 0;
}
private Map useBackup(Exhibitors localExhibitors)
{
Map values = newValues();
try
{
String backupConnectionString = localExhibitors.getBackupConnectionString();
int thePort = -1;
int count = 0;
for ( String spec : backupConnectionString.split(",") )
{
spec = spec.trim();
String[] parts = spec.split(":");
if ( parts.length == 2 )
{
String hostname = parts[0];
int port = Integer.parseInt(parts[1]);
if ( thePort < 0 )
{
thePort = port;
}
else if ( port != thePort )
{
log.warn("Inconsistent port in connection component: " + spec);
}
values.put(VALUE_SERVER_PREFIX + count, hostname);
++count;
}
else
{
log.warn("Bad backup connection component: " + spec);
}
}
values.put(VALUE_COUNT, Integer.toString(count));
values.put(VALUE_PORT, Integer.toString(thePort));
}
catch ( Exception e )
{
log.error("Couldn't get backup connection string", e);
}
return values;
}
private Map newValues()
{
Map values = Maps.newHashMap();
values.put(VALUE_COUNT, "0");
return values;
}
private static Map decodeExhibitorList(String str) throws UnsupportedEncodingException
{
Map values = Maps.newHashMap();
for ( String spec : str.split("&") )
{
String[] parts = spec.split("=");
if ( parts.length == 2 )
{
values.put(parts[0], URLDecoder.decode(parts[1], "UTF-8"));
}
}
return values;
}
private Map queryExhibitors(Exhibitors localExhibitors)
{
Map values = newValues();
long start = System.currentTimeMillis();
int retries = 0;
boolean done = false;
while ( !done )
{
List hostnames = Lists.newArrayList(localExhibitors.getHostnames());
if ( hostnames.size() == 0 )
{
done = true;
}
else
{
String hostname = hostnames.get(random.nextInt(hostnames.size()));
try
{
String encoded = restClient.getRaw(hostname, localExhibitors.getRestPort(), restUriPath, MIME_TYPE);
values.putAll(decodeExhibitorList(encoded));
done = true;
}
catch ( Throwable e )
{
if ( retryPolicy.allowRetry(retries++, System.currentTimeMillis() - start, RetryLoop.getDefaultRetrySleeper()) )
{
log.warn("Couldn't get servers from Exhibitor. Retrying.", e);
}
else
{
log.error("Couldn't get servers from Exhibitor. Giving up.", e);
done = true;
}
}
}
}
return values;
}
}