package com.linkedin.databus2.core.container.monitoring.mbean; /* * * Copyright 2013 LinkedIn Corp. All rights reserved * * 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. * */ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import javax.management.MBeanServer; import javax.management.ObjectName; import org.apache.log4j.Logger; import com.linkedin.databus.core.Checkpoint; import com.linkedin.databus.core.monitoring.mbean.AbstractMonitoringMBean; import com.linkedin.databus.core.util.ConfigApplier; import com.linkedin.databus.core.util.ConfigBuilder; import com.linkedin.databus.core.util.InvalidConfigException; import com.linkedin.databus.core.util.JmxUtil; import com.linkedin.databus.core.util.ReadWriteSyncedObject; import com.linkedin.databus2.core.container.request.RegisterResponseEntry; /** * Container for all monitoring mbeans */ public class HttpStatisticsCollector extends ReadWriteSyncedObject implements HttpStatisticsCollectorMBean { public static final String MODULE = HttpStatisticsCollector.class.getName(); public static final Logger LOG = Logger.getLogger(MODULE); private final static String NO_PEER = "NONE"; private final DbusHttpTotalStats _totalStats; private final HashMap<Integer, DbusHttpTotalStats> _perSourceStats; private final HashMap<String, DbusHttpTotalStats> _perClientStats; private final MBeanServer _mbeanServer; private final ObjectName _collectorObjName; private final int _id; private final String _name; private final String _perSourceNamePrefix; private final String _perPeerPrefix; private final String _curPeer; private final AtomicBoolean _enabled; public HttpStatisticsCollector(int id, String name, boolean enabled, boolean threadSafe, MBeanServer mbeanServer) { this(id, name, enabled, threadSafe, NO_PEER, mbeanServer); } private HttpStatisticsCollector(int relayId, String name, boolean enabled, boolean threadSafe, String peer, MBeanServer mbeanServer) { super(threadSafe); _mbeanServer = mbeanServer; _curPeer = peer; _enabled = new AtomicBoolean(enabled); _id = relayId; _name = name; _perSourceNamePrefix = _name + ".source."; _perPeerPrefix = _name + ".peer."; _totalStats = new DbusHttpTotalStats(_id, _name + ".total", enabled, threadSafe, null); _perSourceStats = new HashMap<Integer, DbusHttpTotalStats>(100); _perClientStats = new HashMap<String, DbusHttpTotalStats>(1000); ObjectName jmxName = null; try { Hashtable<String, String> mbeanProps = new Hashtable<String, String>(5); mbeanProps.put("name", _name); mbeanProps.put("type", HttpStatisticsCollector.class.getSimpleName()); mbeanProps.put("relay", Integer.toString(relayId)); jmxName = new ObjectName(AbstractMonitoringMBean.JMX_DOMAIN, mbeanProps); } catch (Exception e) { LOG.error("Error creating JMX object name", e); } _collectorObjName = jmxName; registerAsMBeans(); } protected void registerAsMBeans() { if (null != _mbeanServer && null != _collectorObjName) { try { if (_mbeanServer.isRegistered(_collectorObjName)) { LOG.warn("unregistering stale mbean: " + _collectorObjName); _mbeanServer.unregisterMBean(_collectorObjName); } _mbeanServer.registerMBean(this, _collectorObjName); _totalStats.registerAsMbean(_mbeanServer); if (LOG.isDebugEnabled()) { LOG.debug("MBean registered " + _collectorObjName); } } catch (Exception e) { LOG.error("JMX registration failed", e); } } } public void unregisterMBeans() { if (null != _mbeanServer && null != _collectorObjName) { try { JmxUtil.unregisterMBeanSafely(_mbeanServer, _collectorObjName, LOG); _totalStats.unregisterMbean(_mbeanServer); for (String clientName: _perClientStats.keySet()) { _perClientStats.get(clientName).unregisterMbean(_mbeanServer); } for (Integer srcId: _perSourceStats.keySet()) { _perSourceStats.get(srcId).unregisterMbean(_mbeanServer); } if (LOG.isDebugEnabled()) { LOG.debug("MBean unregistered " + _collectorObjName); } } catch (Exception e) { LOG.error("JMX deregistration failed", e); } } } /** Creates a copy */ public HttpStatisticsCollector createForClientConnection(String client) { return new HttpStatisticsCollector(_id, _name, true, false, client, null); } @Override public DbusHttpTotalStats getTotalStats() { return _totalStats; } @Override public List<Integer> getSources() { Lock readLock = acquireReadLock(); try { List<Integer> result = new ArrayList<Integer>(_perSourceStats.keySet()); return result; } finally { releaseLock(readLock); } } @Override public DbusHttpTotalStats getSourceStats(int srcId) { DbusHttpTotalStats result = getOrAddPerSourceCollector(srcId, null); return result; } @Override public List<String> getPeers() { Lock readLock = acquireReadLock(); try { ArrayList<String> result = new ArrayList<String>(_perClientStats.keySet()); return result; } finally { releaseLock(readLock); } } @Override public DbusHttpTotalStats getPeerStats(String peer) { Lock readLock = acquireReadLock(); try { DbusHttpTotalStats result = _perClientStats.get(peer); return result; } finally { releaseLock(readLock); } } @Override public void registerRegisterCall(List<RegisterResponseEntry> sources) { if (!_enabled.get()) return; _totalStats.registerRegisterCall(_curPeer); for (RegisterResponseEntry respEntry: sources) { DbusHttpTotalStats perSourceCollector = getOrAddPerSourceCollector((int)respEntry.getId(), null); perSourceCollector.registerRegisterCall(_curPeer); } if (NO_PEER != _curPeer) { DbusHttpTotalStats clientStats = getOrAddPerPeerCollector(_curPeer, null); clientStats.registerRegisterCall(_curPeer); } } @Override public void registerSourcesCall() { if (!_enabled.get()) return; _totalStats.registerSourcesCall(_curPeer); if (NO_PEER != _curPeer) { DbusHttpTotalStats clientStats = getOrAddPerPeerCollector(_curPeer, null); clientStats.registerSourcesCall(_curPeer); } } @Override public void registerStreamResponse(long streamCallDuration) { if (!_enabled.get()) return; Lock writeLock = acquireWriteLock(); try { _totalStats.registerStreamResponse(streamCallDuration); if (NO_PEER != _curPeer) { DbusHttpTotalStats clientStats = getOrAddPerPeerCollector(_curPeer, writeLock); clientStats.registerStreamResponse(streamCallDuration); } //NOTE: we don't update per-source stats because for multi-source stream calls the latency //is not meaningful on per-source basis } finally { releaseLock(writeLock); } } @Override public void registerStreamRequest(Checkpoint cp, Collection<Integer> sourceIds) { if (!_enabled.get()) return; _totalStats.registerStreamRequest(_curPeer, cp); Lock writeLock = acquireWriteLock(); try { for (Integer srcId: sourceIds) { DbusHttpTotalStats curBean = getOrAddPerSourceCollector(srcId, writeLock); curBean.registerStreamRequest(_curPeer, cp); } if (NO_PEER != _curPeer) { DbusHttpTotalStats clientStats = getOrAddPerPeerCollector(_curPeer, writeLock); clientStats.registerStreamRequest(_curPeer, cp); } } finally { releaseLock(writeLock); } } public void merge(HttpStatisticsCollector other) { _totalStats.mergeStats(other._totalStats); Lock otherReadLock = other.acquireReadLock(); Lock writeLock = acquireWriteLock(otherReadLock); try { for (Integer id: other._perSourceStats.keySet()) { DbusHttpTotalStats bean = other._perSourceStats.get(id); mergePerSource(id, bean, writeLock); } for (String peerName: other._perClientStats.keySet()) { DbusHttpTotalStats bean = other._perClientStats.get(peerName); mergePerPeer(peerName, bean, writeLock); } } finally { releaseLock(writeLock); releaseLock(otherReadLock); } } @Override public void reset() { _totalStats.reset(); Lock readLock = acquireReadLock(); try { for (Integer sourceId: _perSourceStats.keySet()) { DbusHttpTotalStats sourceStats = _perSourceStats.get(sourceId); sourceStats.reset(); } for (String peer: _perClientStats.keySet()) { _perClientStats.get(peer).reset(); } } finally { releaseLock(readLock); } } @Override public boolean isEnabled() { return _enabled.get(); } @Override public void setEnabled(boolean enabled) { _enabled.set(enabled); } private DbusHttpTotalStats getOrAddPerSourceCollector(Integer srcId, Lock writeLock) { Lock myWriteLock = null; if (null == writeLock) myWriteLock = acquireWriteLock(); try { DbusHttpTotalStats data = _perSourceStats.get(srcId); if (null == data) { data = new DbusHttpTotalStats(_id, _perSourceNamePrefix + srcId, true, isThreadSafe(), null); _perSourceStats.put(srcId, data); if (null != _mbeanServer) { data.registerAsMbean(_mbeanServer); } } return data; } finally { releaseLock(myWriteLock); } } private DbusHttpTotalStats getOrAddPerPeerCollector(String client, Lock writeLock) { Lock myWriteLock = null; if (null == writeLock) myWriteLock = acquireWriteLock(); try { DbusHttpTotalStats clientStats = _perClientStats.get(client); if (null == clientStats) { clientStats = new DbusHttpTotalStats(_id, _perPeerPrefix + client, true, isThreadSafe(), null); _perClientStats.put(client, clientStats); if (null != _mbeanServer) { clientStats.registerAsMbean(_mbeanServer); } } return clientStats; } finally { releaseLock(myWriteLock); } } private void mergePerSource(Integer srcId, DbusHttpTotalStats other, Lock writeLock) { DbusHttpTotalStats curBean = getOrAddPerSourceCollector(srcId, writeLock); curBean.mergeStats(other); } private void mergePerPeer(String peer, DbusHttpTotalStats other, Lock writeLock) { DbusHttpTotalStats curBean = getOrAddPerPeerCollector(peer, writeLock); curBean.mergeStats(other); } public class RuntimeConfig implements ConfigApplier<RuntimeConfig> { private final boolean _enabled; public RuntimeConfig(boolean enabled) { _enabled = enabled; } /** A flag if the statistics collector is enabled and it will update the stats counters */ public boolean isEnabled() { return _enabled; } @Override public void applyNewConfig(RuntimeConfig oldConfig) { HttpStatisticsCollector.this.setEnabled(this.isEnabled()); } @Override public boolean equals(Object other) { if (null == other || !(other instanceof RuntimeConfig)) return false; return equalsConfig((RuntimeConfig)other); } @Override public boolean equalsConfig(RuntimeConfig otherConfig) { if (null == otherConfig) return false; return isEnabled() == otherConfig.isEnabled(); } @Override public int hashCode() { return _enabled ? 0 : 1; } } public static class RuntimeConfigBuilder implements ConfigBuilder<RuntimeConfig> { private boolean _enabled = true; private HttpStatisticsCollector _managedInstance = null; public RuntimeConfigBuilder() { super(); } public boolean isEnabled() { return _enabled; } public void setEnabled(boolean enabled) { _enabled = enabled; } @Override public RuntimeConfig build() throws InvalidConfigException { if (null != _managedInstance) return _managedInstance.new RuntimeConfig(_enabled); throw new InvalidConfigException("No ContainerStatisticsCollector instance assigned"); } public HttpStatisticsCollector getManagedInstance() { return _managedInstance; } public void setManagedInstance(HttpStatisticsCollector managedInstance) { _managedInstance = managedInstance; } } public static class StaticConfig { private final RuntimeConfigBuilder _runtime; private final HttpStatisticsCollector _existingStatsCollector; public StaticConfig(RuntimeConfigBuilder runtime, HttpStatisticsCollector existingStatsCollector) { _runtime = runtime; _existingStatsCollector = existingStatsCollector; } /** Runtime configuration */ public RuntimeConfigBuilder getRuntime() { return _runtime; } public HttpStatisticsCollector getExistingStatsCollector() { return _existingStatsCollector; } } public static class Config implements ConfigBuilder<StaticConfig> { private final RuntimeConfigBuilder _runtime; private HttpStatisticsCollector _existingStatsCollector = null; public Config() { super(); _runtime = new RuntimeConfigBuilder(); } public RuntimeConfigBuilder getRuntime() { return _runtime; } public HttpStatisticsCollector getExisting() { return _existingStatsCollector; } public void setExisting(HttpStatisticsCollector existingStatsCollector) { _existingStatsCollector = existingStatsCollector; } @Override public StaticConfig build() throws InvalidConfigException { return new StaticConfig(_runtime, _existingStatsCollector); } } @Override public String getName() { return _name; } @Override public void registerInvalidStreamRequest() { if (!_enabled.get()) return; _totalStats.registerInvalidStreamRequest(_curPeer); Lock writeLock = acquireWriteLock(); try { if (NO_PEER != _curPeer) { DbusHttpTotalStats clientStats = getOrAddPerPeerCollector(_curPeer, writeLock); clientStats.registerInvalidStreamRequest(_curPeer); } } finally { releaseLock(writeLock); } } @Override public void registerScnNotFoundStreamResponse() { if (!_enabled.get()) return; _totalStats.registerScnNotFoundStreamResponse(_curPeer); Lock writeLock = acquireWriteLock(); try { if (NO_PEER != _curPeer) { DbusHttpTotalStats clientStats = getOrAddPerPeerCollector(_curPeer, writeLock); clientStats.registerScnNotFoundStreamResponse(_curPeer); } } finally { releaseLock(writeLock); } } @Override public void registerInvalidSourceRequest() { if (!_enabled.get()) return; _totalStats.registerInvalidSourceRequest(_curPeer); Lock writeLock = acquireWriteLock(); try { if (NO_PEER != _curPeer) { DbusHttpTotalStats clientStats = getOrAddPerPeerCollector(_curPeer, writeLock); clientStats.registerInvalidSourceRequest(_curPeer); } } finally { releaseLock(writeLock); } } @Override public void registerInvalidRegisterCall() { if (!_enabled.get()) return; _totalStats.registerInvalidRegisterCall(_curPeer); Lock writeLock = acquireWriteLock(); try { if (NO_PEER != _curPeer) { DbusHttpTotalStats clientStats = getOrAddPerPeerCollector(_curPeer, writeLock); clientStats.registerInvalidRegisterCall(_curPeer); } } finally { releaseLock(writeLock); } } @Override public void registerMastershipStatus(int i) { //only global stats make sense; _totalStats.registerMastershipStatus(i); } }