/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.bigdata.rdf.sail.webapp.lbs.policy.ganglia;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import org.apache.log4j.Logger;
import com.bigdata.ganglia.GangliaListener;
import com.bigdata.ganglia.GangliaService;
import com.bigdata.ganglia.IHostReport;
import com.bigdata.journal.GangliaPlugIn;
import com.bigdata.journal.IIndexManager;
import com.bigdata.journal.Journal;
import com.bigdata.rdf.sail.webapp.lbs.AbstractHostLBSPolicy;
import com.bigdata.rdf.sail.webapp.lbs.IHALoadBalancerPolicy;
import com.bigdata.rdf.sail.webapp.lbs.IHostMetrics;
import com.bigdata.rdf.sail.webapp.lbs.IHostScoringRule;
import com.bigdata.rdf.sail.webapp.lbs.ServiceScore;
/**
* Stochastically proxy the request to the services based on their load.
* <p>
* Note: This {@link IHALoadBalancerPolicy} has a dependency on the
* {@link GangliaPlugIn}. The {@link GangliaPlugIn} must be setup to listen to
* the Ganglia protocol and build up an in-memory model of the load on each
* host. Ganglia MUST be reporting metrics for each host running an
* {@link HAJournalServer} instance. This can be achieved either using the
* <code>gmond</code> utility from the ganglia distribution or using the
* {@link GangliaPlugIn}.
* <p>
* Note: The actual performance metrics will be reported by a given ganglia
* daemon on some host depend on the ganglia implementation, ganglia
* configuration, and the host OS. Some key performance metrics may not be
* available on all hosts. For example, IO Wait is not available under OSX. The
* available performance metrics constraints the {@link IHostScoringRule}s that
* you can utilize on a given cluster. You can monitor the available performance
* metrics by running the {@link GangliaListener} utility.
* <p>
* Note: The ganglia updates are not synchronized across a cluster. They pour in
* ever N seconds from each host. However, the hosts do not begin to report on
* the same N second boundary. All you know is that (on average) all hosts
* should have reported in within N seconds.
*
* @see GangliaService#getDefaultHostReportOn()
* @see GangliaListener#main(String[])
* @see <a
* href="https://sourceforge.net/apps/trac/ganglia/wiki/Ganglia%203.1.x%20Installation%20and%20Configuration"
* >Ganglia Wiki (has a section on the defined metics and the platform
* where they are available)</a>
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
*/
public class GangliaLBSPolicy extends AbstractHostLBSPolicy {
@SuppressWarnings("unused")
private static final Logger log = Logger.getLogger(GangliaLBSPolicy.class);
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Servlet <code>init-param</code> values understood by the
* {@link GangliaLBSPolicy}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan
* Thompson</a>
*/
public interface InitParams extends AbstractHostLBSPolicy.InitParams {
}
// /**
// * Place into descending order by load_one.
// * <p>
// * Note: We do not rely on the ordering imposed by this comparator. Instead,
// * we filter the hosts for those that correspond to the joined services in
// * the met quorum, compute a score for each such host, and then normalize
// * those scores.
// */
// private final static Comparator<IHostReport> comparator = new HostReportComparator(
// "load_one", false/* asc */);
/**
* The ganglia service - it must be configured at least as a listener.
*/
private final AtomicReference<GangliaService> gangliaServiceRef = new AtomicReference<GangliaService>();
@Override
protected void toString(final StringBuilder sb) {
super.toString(sb);
sb.append(",gangliaService=" + gangliaServiceRef.get());
}
@Override
public void init(final ServletConfig servletConfig,
final IIndexManager indexManager) throws ServletException {
super.init(servletConfig, indexManager);
gangliaServiceRef.set((GangliaService) ((Journal) indexManager)
.getGangliaService());
if (gangliaServiceRef.get() == null) {
// LBS requires ganglia to load balance requests.
throw new ServletException("LBS requires "
+ GangliaPlugIn.class.getName());
}
}
@Override
public void destroy() {
// comparator = null;
gangliaServiceRef.set(null);
super.destroy();
}
/**
* {@inheritDoc}
* <p>
* This implementation queries the in-memory model of the cluster that is
* built up and maintained by the integrated {@link GangliaService}.
*/
protected Map<String, IHostMetrics> getHostReportForKnownServices(
final IHostScoringRule scoringRule,
final ServiceScore[] serviceScores) {
final GangliaService gangliaService = gangliaServiceRef.get();
if(gangliaService == null) {
// No ganglia.
return null;
}
/*
* The set of hosts having services that are joined with the met quorum.
*/
final String[] hosts;
{
final List<String> tmp = new LinkedList<String>();
for (ServiceScore serviceScore : serviceScores) {
if (serviceScore == null) // should never be null.
continue;
final String hostname = serviceScore.getHostname();
if (hostname == null) // should never be null.
continue;
tmp.add(hostname);
}
// dense array of hosts names for services.
hosts = tmp.toArray(new String[tmp.size()]);
}
final String[] reportOn = scoringRule.getMetricNames();
final IHostReport[] hostReport = gangliaService.getHostReport(//
hosts,// the hosts for our joined services.
reportOn,// metrics to be reported.
null // comparator (unused)
);
final Map<String/* hostname */, IHostMetrics> hostMetricsMap = new HashMap<String, IHostMetrics>();
// Consider each host report from ganglia.
for (int i = 0; i < hostReport.length; i++) {
final IHostReport theHostReport = hostReport[i];
// Consider each service in turn.
for (int j = 0; j < serviceScores.length; j++) {
final ServiceScore theServiceScore = serviceScores[j];
// The hostname as understood by a service.
final String hostname = theServiceScore.getHostname();
if(hostname == null)
continue;
final int index = hostname.indexOf('.');
// The local name of the host (w/o the domain).
final String localname = (index == -1) ? null : hostname
.substring(0/* beginIndex */, index/* endIndex */);
if (hostname.equals(theHostReport.getHostName())) {
// Store under the canonical host name.
hostMetricsMap.put(hostname, new GangliaHostMetricWrapper(theHostReport));
break;
}
/*
* Note: This recognizes the local host name as well as the
* canonical hostname. ganglia defaults to reporting the local
* name of the host. bigdata defaults to using the canonical
* name of the host. To make things line up, we check for a
* match on the local name if we do not find any match on the
* hostname as self-reported by the service.
*/
if (localname.equals(theHostReport.getHostName())) {
// Note: We ALWAYS use the canonical host name here.
hostMetricsMap.put(hostname, new GangliaHostMetricWrapper(theHostReport));
break;
}
}
}
return hostMetricsMap;
}
@Override
protected String getDefaultScoringRule() {
return DefaultHostScoringRule.class.getName();
}
}