package net.spy.memcached.protocol.binary;
import java.lang.management.ManagementFactory;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.netflix.config.DynamicBooleanProperty;
import com.netflix.evcache.pool.ServerGroup;
import com.netflix.evcache.util.EVCacheConfig;
import com.netflix.servo.annotations.DataSourceType;
import com.netflix.servo.monitor.BasicCounter;
import com.netflix.servo.monitor.CompositeMonitor;
import com.netflix.servo.monitor.LongGauge;
import com.netflix.servo.monitor.Monitor;
import com.netflix.servo.monitor.MonitorConfig;
import com.netflix.servo.monitor.Monitors;
import com.netflix.servo.tag.BasicTagList;
import com.netflix.servo.tag.TagList;
import net.spy.memcached.ConnectionFactory;
import net.spy.memcached.ops.Operation;
import sun.misc.Cleaner;
import sun.nio.ch.DirectBuffer;
@edu.umd.cs.findbugs.annotations.SuppressFBWarnings({ "FCBL_FIELD_COULD_BE_LOCAL", "EXS_EXCEPTION_SOFTENING_NO_CHECKED",
"REC_CATCH_EXCEPTION",
"SCII_SPOILED_CHILD_INTERFACE_IMPLEMENTATOR" })
public class EVCacheNodeImpl extends BinaryMemcachedNodeImpl implements EVCacheNodeImplMBean, CompositeMonitor<Long> {
private static final Logger log = LoggerFactory.getLogger(EVCacheNodeImpl.class);
protected long stTime;
protected final AtomicLong opCount = new AtomicLong(0);
protected final AtomicInteger reconnectCount = new AtomicInteger(0);
protected final String _appName;
protected final String hostName;
protected final ServerGroup _serverGroup;
protected final int id;
protected final BlockingQueue<Operation> readQ;
protected final BlockingQueue<Operation> inputQueue;
protected final String metricPrefix;
protected final DynamicBooleanProperty sendMetrics;
protected final MonitorConfig baseConfig;
protected final TagList baseTags;
protected final TagList tags;
private long timeoutStartTime;
public EVCacheNodeImpl(SocketAddress sa, SocketChannel c, int bufSize, BlockingQueue<Operation> rq,
BlockingQueue<Operation> wq, BlockingQueue<Operation> iq,
long opQueueMaxBlockTimeMillis, boolean waitForAuth, long dt, long at, ConnectionFactory fa, String appName,
int id, ServerGroup serverGroup, long stTime) {
super(sa, c, bufSize, rq, wq, iq, Long.valueOf(opQueueMaxBlockTimeMillis), waitForAuth, dt, at, fa);
this.id = id;
this._appName = appName;
this._serverGroup = serverGroup;
setConnectTime(stTime);
this.readQ = rq;
this.inputQueue = iq;
this.sendMetrics = EVCacheConfig.getInstance().getDynamicBooleanProperty("EVCacheNodeImpl." + appName + ".sendMetrics", false);
this.tags = BasicTagList.of("ServerGroup", _serverGroup.getName(), "APP", appName, "Id", String.valueOf(id));
this.hostName = ((InetSocketAddress) getSocketAddress()).getHostName();
this.metricPrefix = "EVCacheNode";
this.baseConfig = MonitorConfig.builder(metricPrefix).build();
baseTags = BasicTagList.concat(tags, BasicTagList.of("HOST", hostName));
setupMonitoring(appName, serverGroup);
}
private String getMonitorName() {
return "com.netflix.evcache:Group=" + _appName + ",SubGroup=pool" + ",SubSubGroup=" + _serverGroup.getName()
+ ",SubSubSubGroup=" + id + ",SubSubSubSubGroup=" + hostName
+ "_" + stTime;
}
private void setupMonitoring(String appName, ServerGroup serverGroup) {
try {
final ObjectName mBeanName = ObjectName.getInstance(getMonitorName());
final MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
if (mbeanServer.isRegistered(mBeanName)) {
if (log.isDebugEnabled()) log.debug("MBEAN with name " + mBeanName
+ " has been registered. Will unregister the previous instance and register a new one.");
mbeanServer.unregisterMBean(mBeanName);
}
mbeanServer.registerMBean(this, mBeanName);
Monitors.registerObject(this);
} catch (Exception e) {
if (log.isDebugEnabled()) log.debug("Exception while setting up the monitoring.", e);
}
}
public boolean isAvailable() {
return isActive();
}
public int getWriteQueueSize() {
return writeQ.size();
}
public int getReadQueueSize() {
return readQ.size();
}
public int getInputQueueSize() {
return inputQueue.size();
}
public long incrOps() {
return opCount.incrementAndGet();
}
public long getNumOfOps() {
return opCount.get();
}
public void flushInputQueue() {
inputQueue.clear();
}
public long getStartTime() {
return stTime;
}
public long getTimeoutStartTime() {
return timeoutStartTime;
}
public void removeMonitoring() {
try {
final ObjectName mBeanName = ObjectName.getInstance(getMonitorName());
final MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
if (mbeanServer.isRegistered(mBeanName)) {
if (log.isDebugEnabled()) log.debug("MBEAN with name " + mBeanName
+ " has been registered. Will unregister the previous instance and register a new one.");
mbeanServer.unregisterMBean(mBeanName);
}
} catch (Exception e) {
if (log.isDebugEnabled()) log.debug("Exception while setting up the monitoring.", e);
}
}
public void shutdown() {
removeMonitoring();
writeQ.clear();
readQ.clear();
inputQueue.clear();
try {
// Cleanup the ByteBuffers only if they are sun.nio.ch.DirectBuffer
// If we don't cleanup then we will leak 16K of memory
if (getRbuf() instanceof DirectBuffer) {
Cleaner cleaner = ((DirectBuffer) getRbuf()).cleaner();
if (cleaner != null) cleaner.clean();
cleaner = ((DirectBuffer) getWbuf()).cleaner();
if (cleaner != null) cleaner.clean();
}
} catch (Throwable t) {
getLogger().error("Exception cleaning ByteBuffer.", t);
}
}
@Override
public Long getValue() {
return getValue(0);
}
@Override
public Long getValue(int pollerIndex) {
return (sendMetrics.get()) ? Long.valueOf(3) : Long.valueOf(0);
}
@Override
public MonitorConfig getConfig() {
return baseConfig;
}
@Override
public List<Monitor<?>> getMonitors() {
if (!sendMetrics.get() && getContinuousTimeout() == 0) return Collections.<Monitor<?>> emptyList();
try {
final List<Monitor<?>> metrics = new ArrayList<Monitor<?>>();
if(getContinuousTimeout() > 0) {
MonitorConfig monitorConfig = EVCacheConfig.getInstance().getMonitorConfig(metricPrefix + "_ContinuousTimeout",
DataSourceType.GAUGE, baseTags);
final LongGauge cTimeouts = new LongGauge(monitorConfig);
cTimeouts.set(Long.valueOf(getContinuousTimeout()));
metrics.add(cTimeouts);
}
if (sendMetrics.get()) {
MonitorConfig monitorConfig = EVCacheConfig.getInstance().getMonitorConfig(metricPrefix + "_WriteQ",
DataSourceType.GAUGE, baseTags);
final LongGauge wQueue = new LongGauge(monitorConfig);
wQueue.set(Long.valueOf(writeQ.size()));
metrics.add(wQueue);
monitorConfig = EVCacheConfig.getInstance().getMonitorConfig(metricPrefix + "_ReadQ", DataSourceType.GAUGE,
baseTags);
final LongGauge rQueue = new LongGauge(monitorConfig);
rQueue.set(Long.valueOf(readQ.size()));
metrics.add(rQueue);
monitorConfig = EVCacheConfig.getInstance().getMonitorConfig(metricPrefix + "_NumOfOps",
DataSourceType.COUNTER, baseTags);
final BasicCounter counter = new BasicCounter(monitorConfig);
counter.increment(opCount.get());
metrics.add(counter);
}
return metrics;
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return Collections.<Monitor<?>> emptyList();
}
public long getCreateTime() {
return stTime;
}
public void setConnectTime(long cTime) {
this.stTime = cTime;
reconnectCount.incrementAndGet();
}
public String getAppName() {
return _appName;
}
public String getHostName() {
return hostName;
}
public ServerGroup getServerGroup() {
return _serverGroup;
}
public int getId() {
return id;
}
public TagList getBaseTags() {
return baseTags;
}
public int getTotalReconnectCount() {
return reconnectCount.get();
}
}