/** * Copyright 2012 Comcast Corporation * * 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.comcast.cns.tools; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import org.apache.log4j.Logger; import org.jfree.util.Log; import com.comcast.cmb.common.util.CMBProperties; import com.comcast.cmb.common.util.PersistenceException; import com.comcast.cmb.common.util.RollingWindowCapture; import com.comcast.cns.io.HTTPEndpointSyncPublisher; import com.comcast.cns.tools.CNSEndpointPublisherJobConsumer; import com.comcast.cns.tools.CNSPublisher; /** * The implementation of monitoring for CNS. * @author aseem * Class is thread-safe */ public class CNSWorkerMonitor implements CNSWorkerMonitorMBean { private static Logger logger = Logger.getLogger(CNSWorkerMonitorMBean.class); private static final CNSWorkerMonitor Inst = new CNSWorkerMonitor(); private CNSWorkerMonitor() { } public static CNSWorkerMonitor getInstance() { return Inst; } public ConcurrentHashMap<String, AtomicInteger> messageIdToSendRemaining = new ConcurrentHashMap<String, AtomicInteger>(); private class BadEndpointInfo { AtomicInteger errors = new AtomicInteger(); AtomicInteger tries = new AtomicInteger(); ConcurrentHashMap<String, Boolean> topics = new ConcurrentHashMap<String, Boolean>(); } private ConcurrentHashMap<String, BadEndpointInfo> badEndpoints = new ConcurrentHashMap<String, CNSWorkerMonitor.BadEndpointInfo>(); private boolean cqsServiceAvailable = true; private class GenericEvent extends RollingWindowCapture.PayLoad{ final AtomicInteger num = new AtomicInteger(1); final long timeStamp = System.currentTimeMillis(); } private class CountMessagesVisitor implements RollingWindowCapture.Visitor<GenericEvent> { int num = 0; public void processNode(GenericEvent n){ num += n.num.get(); } } private RollingWindowCapture<GenericEvent> publishMsgRW = new RollingWindowCapture<GenericEvent>(CMBProperties.getInstance().getRollingWindowTimeSec(), 10000); @Override public int getRecentNumberOfPublishedMessages() { CountMessagesVisitor c = new CountMessagesVisitor(); publishMsgRW.visitAllNodes(c); return c.num; } @Override public int getPublishHttpPoolSize() { return HTTPEndpointSyncPublisher.getNumConnectionsInPool(); } public void registerPublishMessage() { GenericEvent currentHead = publishMsgRW.getLatestPayload(); if (currentHead == null || System.currentTimeMillis() - currentHead.timeStamp > 60000L) { publishMsgRW.addNow(new GenericEvent()); } else { currentHead.num.incrementAndGet(); } } public void registerSendsRemaining(String messageId, int remaining) { AtomicInteger newVal = new AtomicInteger(); AtomicInteger oldVal = messageIdToSendRemaining.putIfAbsent(messageId, newVal); if (oldVal != null) { newVal = oldVal; } newVal.addAndGet(remaining); if (newVal.intValue() == 0) { messageIdToSendRemaining.remove(messageId); } } @Override public int getPendingPublishJobs() { int numNonZero = 0; for (Map.Entry<String, AtomicInteger> entry : messageIdToSendRemaining.entrySet()) { if (entry.getValue().get() != 0) { numNonZero++; } } return numNonZero; } @Override public boolean getCNSWorkerStatus() { return CMBProperties.getInstance().isCNSPublisherEnabled(); } @Override public void startCNSWorkers() throws Exception{ if (CMBProperties.getInstance().isCNSPublisherEnabled()){ return; } CMBProperties.getInstance().setCNSPublisherEnabled(true); try{ CNSPublisher.start(CMBProperties.getInstance().getCNSPublisherMode()); } catch (Exception e){ logger.error("event=start_cnsworker_error exception="+e); throw e; } } @Override public void stopCNSWorkers() { CMBProperties.getInstance().setCNSPublisherEnabled(false); } public void clearBadEndpointsState() { badEndpoints.clear(); } /** * Only capture stats for endpoints that have previous errors. * @param endpoint * @param errors * @param numTries * @param topic */ public void registerBadEndpoint(String endpoint, int errors, int numTries, String topic) { if (!badEndpoints.containsKey(endpoint) && errors == 0) { return; //no errors for this endpoint. Its good. return } BadEndpointInfo newVal = new BadEndpointInfo(); BadEndpointInfo oldVal = badEndpoints.putIfAbsent(endpoint, newVal); if (oldVal != null) { newVal = oldVal; } newVal.errors.addAndGet(errors); newVal.tries.addAndGet(numTries); newVal.topics.putIfAbsent(topic, Boolean.TRUE); } /** * @return map of endpointURL -> failure-rate, numTries, List<Topics> */ @Override public Map<String, String> getErrorRateForEndpoints() { HashMap<String, String> ret = new HashMap<String, String>(); for (Map.Entry<String, BadEndpointInfo> entry : badEndpoints.entrySet()) { String endpoint = entry.getKey(); BadEndpointInfo val = entry.getValue(); StringBuffer sb = new StringBuffer(); int failureRate = (val.tries.get() > 0) ? (int) (((float) val.errors.get() / (float) val.tries.get()) * 100) : 0; sb.append("failureRate=").append(failureRate).append(",numTries=").append(val.tries.get()).append(",topics="); Enumeration<String> topics = val.topics.keys(); if (topics.hasMoreElements()) { sb.append(topics.nextElement()); } while (topics.hasMoreElements()) { sb.append(",").append(topics.nextElement()); } ret.put(endpoint, sb.toString()); } return ret; } @Override public Map<String, Integer> getRecentErrorCountForEndpoints() { return CNSEndpointPublisherJobConsumer.getBadResponseCounts(); } @Override public int getDeliveryQueueSize() { return CNSEndpointPublisherJobConsumer.MonitoringInterface.getDeliveryHandlersQueueSize(); } @Override public int getRedeliveryQueueSize() { return CNSEndpointPublisherJobConsumer.MonitoringInterface.getReDeliveryHandlersQueueSize(); } @Override public boolean isConsumerOverloaded() { return CNSEndpointPublisherJobConsumer.isOverloaded(); } @Override public boolean clearWorkerQueues() { try { CNSPublisher.clearQueues(); clearBadEndpointsState(); return true; } catch (PersistenceException ex) { Log.error("event=clear_queue_failure", ex); } return false; } @Override public boolean isCQSServiceAvailable() { return cqsServiceAvailable; } public void registerCQSServiceAvailable(boolean available) { cqsServiceAvailable = available; } }