/* * JBoss, Home of Professional Open Source * Copyright 2011, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.mobicents.tools.sip.balancer; import gov.nist.javax.sip.stack.StatsRetreiver; import java.io.File; import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.LogManager; import javax.management.MBeanServer; import javax.management.ObjectName; import org.apache.log4j.ConsoleAppender; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.apache.log4j.PatternLayout; import org.apache.log4j.xml.DOMConfigurator; import org.mobicents.tools.configuration.LoadBalancerConfiguration; import org.mobicents.tools.configuration.XmlConfigurationLoader; import org.mobicents.tools.heartbeat.api.HeartbeatConfig; import org.mobicents.tools.heartbeat.api.Node; import org.mobicents.tools.heartbeat.impl.HeartbeatConfigHttp; import org.mobicents.tools.http.balancer.HttpBalancerForwarder; import org.mobicents.tools.smpp.balancer.core.SmppBalancerRunner; import org.restcomm.commons.statistics.reporter.RestcommStatsReporter; import com.codahale.metrics.Counter; import com.codahale.metrics.MetricRegistry; /** * @author jean.deruelle@gmail.com * */ public class BalancerRunner implements BalancerRunnerMBean { public static final String SIP_BALANCER_JMX_NAME = "mobicents:type=LoadBalancer,name=LoadBalancer"; public static final String HTML_ADAPTOR_JMX_NAME = "mobicents:name=htmladapter,port="; protected static final String STATISTICS_SERVER = "statistics.server"; protected static final String DEFAULT_STATISTICS_SERVER = "https://statistics.restcomm.com/rest/"; private SipBalancerShutdownHook shutdownHook=null; RestcommStatsReporter statsReporter = new RestcommStatsReporter(); MetricRegistry metrics = RestcommStatsReporter.getMetricRegistry(); //define metric name Counter counterCalls = metrics.counter("calls"); //Counter counterSeconds = metrics.counter("seconds"); Counter counterMessages = metrics.counter("messages"); ConcurrentHashMap<String, InvocationContext> contexts = new ConcurrentHashMap<String, InvocationContext>(); static { String logLevel = System.getProperty("logLevel", "INFO"); String logConfigFile = System.getProperty("logConfigFile"); if (logConfigFile != null) { DOMConfigurator.configure(logConfigFile); } else if(!LogManager.getLogManager().getLoggerNames().hasMoreElements()) { Logger.getRootLogger().addAppender(new ConsoleAppender( new PatternLayout("%r (%t) %p [%c{1}%x] %m%n"))); Logger.getRootLogger().setLevel(Level.toLevel(logLevel)); } } public InvocationContext getInvocationContext(String version) { if(version == null) version = "0"; InvocationContext ct = contexts.get(version); if(ct==null) { ct = new InvocationContext(version, balancerContext); contexts.put(version, ct); } return ct; } public InvocationContext getLatestInvocationContext() { return getInvocationContext(reg.getLatestVersion()); } private static Logger logger = Logger.getLogger(BalancerRunner.class .getCanonicalName()); protected SIPBalancerForwarder sipForwarder = null; protected NodeRegisterImpl reg = null; HttpBalancerForwarder httpBalancerForwarder; public SmppBalancerRunner smppBalancerRunner; public BalancerContext balancerContext = new BalancerContext(); /** * @param args */ public static void main(String[] args) { if (args.length < 1) { logger.error("Please specify mobicents-balancer-config argument. Usage is : java -DlogConfigFile=./lb-log4j.xml -jar sip-balancer-jar-with-dependencies.jar -mobicents-balancer-config=lb-configuration.xml"); return; } if(!args[0].startsWith("-mobicents-balancer-config=")) { logger.error("Impossible to find the configuration file since you didn't specify the mobicents-balancer-config argument. Usage is : java -DlogConfigFile=./lb-log4j.xml -jar sip-balancer-jar-with-dependencies.jar -mobicents-balancer-config=lb-configuration.xml"); return; } // Configuration file Location String configurationFileLocation = args[0].substring("-mobicents-balancer-config=".length()); BalancerRunner balancerRunner = new BalancerRunner(); balancerRunner.start(configurationFileLocation); } public void start(LoadBalancerConfiguration lbConfig) { if(statsReporter==null) statsReporter = new RestcommStatsReporter(); String ipAddress = lbConfig.getCommonConfiguration().getHost(); if(ipAddress == null) { ipAddress = lbConfig.getSipConfiguration().getInternalLegConfiguration().getHost(); } if(ipAddress == null) { ipAddress = lbConfig.getSipConfiguration().getExternalLegConfiguration().getHost(); } InetAddress addr = null; try { addr = InetAddress.getByName(ipAddress); } catch (UnknownHostException e) { logger.error("Couldn't get the InetAddress from the host " + ipAddress, e); return; } balancerContext.securityRequired = lbConfig.getCommonConfiguration().getSecurityRequired(); if(balancerContext.securityRequired) { balancerContext.login = lbConfig.getCommonConfiguration().getLogin(); balancerContext.password = lbConfig.getCommonConfiguration().getPassword(); } balancerContext.algorithmClassName = lbConfig.getSipConfiguration().getAlgorithmConfiguration().getAlgorithmClass(); balancerContext.terminateTLSTraffic = lbConfig.getSslConfiguration().getTerminateTLSTraffic(); balancerContext.smppToProviderAlgorithmClassName = lbConfig.getSmppConfiguration().getSmppToProviderAlgorithmClass(); if(lbConfig.getSmppConfiguration().isMuxMode()) balancerContext.smppToNodeAlgorithmClassName = lbConfig.getSmppConfiguration().getSmppToNodeAlgorithmClass(); balancerContext.shutdownTimeout = lbConfig.getCommonConfiguration().getShutdownTimeout(); try { MBeanServer server = ManagementFactory.getPlatformMBeanServer(); RouterImpl.setRegister(reg); reg = new NodeRegisterImpl(addr); reg.balancerRunner = this; reg.setNodeExpirationTaskInterval(lbConfig.getCommonConfiguration().getHeartbeatInterval()); reg.setNodeExpiration(lbConfig.getCommonConfiguration().getNodeTimeout()); if(logger.isInfoEnabled()) { logger.info("Node timeout" + " = " + reg.getNodeExpiration()); logger.info("Heartbeat interval" + " = " + reg.getNodeExpirationTaskInterval()); } if(logger.isDebugEnabled()) { logger.debug("LB will use next class for registry nodes : " + lbConfig.getHeartbeatConfigurationClass()); } HeartbeatConfig heartbeatConfig = lbConfig.getHeartbeatConfiguration(); if(heartbeatConfig ==null) { logger.warn("Configuration of heartbeat is not set, we will use http heartbeat protocol default values"); heartbeatConfig = new HeartbeatConfigHttp(); } balancerContext.nodeCommunicationProtocolClassName = heartbeatConfig.getProtocolClassName(); reg.startRegistry(heartbeatConfig); if(logger.isDebugEnabled()) { logger.debug("adding shutdown hook"); } String statisticsServer = Version.getVersionProperty(STATISTICS_SERVER); if(statisticsServer == null || !statisticsServer.contains("http")) { statisticsServer = DEFAULT_STATISTICS_SERVER; } //define remote server address (optionally) statsReporter.setRemoteServer(statisticsServer); String projectName = System.getProperty("RestcommProjectName", "loadbalancer"); String projectType = System.getProperty("RestcommProjectType", "community"); String projectVersion = System.getProperty("RestcommProjectVersion", Version.getVersionProperty(Version.RELEASE_VERSION)); if(logger.isDebugEnabled()) { logger.debug("Restcomm Stats " + projectName + " " + projectType + " " + projectVersion); } statsReporter.setProjectName(projectName); statsReporter.setProjectType(projectType); statsReporter.setVersion(projectVersion); //define periodicy - default to once a day statsReporter.start(86400, TimeUnit.SECONDS); Version.printVersion(); sipForwarder = new SIPBalancerForwarder(lbConfig, this, reg); sipForwarder.start(); if(lbConfig.getHttpConfiguration().getHttpPort()!=null) { httpBalancerForwarder = new HttpBalancerForwarder(); httpBalancerForwarder.balancerRunner = this; try { httpBalancerForwarder.start(); } catch (org.jboss.netty.channel.ChannelException e) { logger.warn("HTTP forwarder could not be restarted."); } } //register the sip balancer ObjectName on = new ObjectName(SIP_BALANCER_JMX_NAME); if (server.isRegistered(on)) { server.unregisterMBean(on); } server.registerMBean(this, on); shutdownHook=new SipBalancerShutdownHook(this); Runtime.getRuntime().addShutdownHook(shutdownHook); } catch (Exception e) { logger.error("An unexpected error occurred while starting the load balancer", e); return; } if(lbConfig.getSmppConfiguration().getSmppPort()!=null) { smppBalancerRunner = new SmppBalancerRunner(this); smppBalancerRunner.start(); } } //start Jboss cache // String cacheConfigFile = lbConfig.getCommonConfiguration().getCacheConfigFile(); // if(balancerContext.cacheListener==null&&cacheConfigFile!=null&&!cacheConfigFile.equals("")) // balancerContext.cacheListener = new HttpCacheListener(this); Timer timer; long lastupdate = 0; /** * @param configurationFileLocation */ public void start(final String configurationFileLocation) { File file = new File(configurationFileLocation); lastupdate = file.lastModified(); final XmlConfigurationLoader configLoader = new XmlConfigurationLoader(); LoadBalancerConfiguration lbConfig = configLoader.load(file); timer = new Timer(); timer.scheduleAtFixedRate(new TimerTask() { public void run() { File conf = new File(configurationFileLocation); if(lastupdate < conf.lastModified()) { lastupdate = conf.lastModified(); logger.info("Configuration file changed, applying changes."); try { for(InvocationContext ctx : contexts.values()) { balancerContext.lbConfig = configLoader.load(conf); ctx.balancerAlgorithm.configurationChanged(); if(ctx.smppToNodeBalancerAlgorithm!=null) ctx.smppToNodeBalancerAlgorithm.configurationChanged(); ctx.smppToProviderBalancerAlgorithm.configurationChanged(); } } catch (Exception e) { logger.warn("Problem reloading configuration " + e); } } } }, 3000, 2000); start(lbConfig); } public void stop() { if(shutdownHook==null) return; try { Runtime.getRuntime().removeShutdownHook(shutdownHook); } catch(IllegalStateException ex) { //may occure due to shutdown already in progress } shutdownHook=null; if(timer != null) { timer.cancel(); timer = null; } statsReporter.stop(); statsReporter = null; if(sipForwarder!=null) { logger.info("Stopping the sip forwarder"); sipForwarder.stop(); sipForwarder=null; } if(httpBalancerForwarder!=null) { logger.info("Stopping the http forwarder"); httpBalancerForwarder.stop(); httpBalancerForwarder=null; } if(smppBalancerRunner != null) { logger.info("Stopping the SMPP balancer"); smppBalancerRunner.stop(); smppBalancerRunner=null; } MBeanServer server = ManagementFactory.getPlatformMBeanServer(); try { if(reg!=null) { ObjectName on = new ObjectName(BalancerRunner.SIP_BALANCER_JMX_NAME); if (server.isRegistered(on)) { logger.info("Unregistering the node registry"); server.unregisterMBean(on); } } } catch (Exception e) { logger.error("An unexpected error occurred while stopping the load balancer", e); } try { for(InvocationContext ctx : contexts.values()) { ctx.balancerAlgorithm.stop(); if(ctx.smppToNodeBalancerAlgorithm!=null) ctx.smppToNodeBalancerAlgorithm.stop(); ctx.smppToProviderBalancerAlgorithm.stop(); } logger.info("Stopping the node registry"); reg.stopRegistry(); reg = null; System.gc(); } catch (Exception e) { logger.error("An unexpected error occurred while stopping the load balancer", e); } } //JMX //SIP Balancer public long getNodeExpiration() { return reg.getNodeExpiration(); } public long getNodeExpirationTaskInterval() { return reg.getNodeExpirationTaskInterval(); } public long getNumberOfRequestsProcessed() { return sipForwarder.getNumberOfRequestsProcessed(); } public long getNumberOfResponsesProcessed() { return sipForwarder.getNumberOfResponsesProcessed(); } public long getNumberOfBytesTransferred() { return sipForwarder.getNumberOfBytesTransferred(); } public Map<String, AtomicLong> getNumberOfRequestsProcessedByMethod() { return sipForwarder.getNumberOfRequestsProcessedByMethod(); } public Map<String, AtomicLong> getNumberOfResponsesProcessedByStatusCode() { return sipForwarder.getNumberOfResponsesProcessedByStatusCode(); } public long getRequestsProcessedByMethod(String method) { return sipForwarder.getRequestsProcessedByMethod(method); } public long getResponsesProcessedByStatusCode(String statusCode) { return sipForwarder.getResponsesProcessedByStatusCode(statusCode); } public int getNumberOfActiveSipConnections() { return StatsRetreiver.getOpenConnections(balancerContext.sipStack); } //HTTP balancer public long getNumberOfHttpRequests() { return httpBalancerForwarder.getNumberOfHttpRequests(); } public long getNumberOfHttpBytesToServer() { return httpBalancerForwarder.getNumberOfHttpBytesToServer(); } public long getNumberOfHttpBytesToClient() { return httpBalancerForwarder.getNumberOfHttpBytesToClient(); } public long getHttpRequestsProcessedByMethod(String method) { return httpBalancerForwarder.getHttpRequestsProcessedByMethod(method); } public long getHttpResponseProcessedByCode(String code) { return httpBalancerForwarder.getHttpResponseProcessedByCode(code); } public int getNumberOfActiveHttpConnections() { return httpBalancerForwarder.getNumberOfActiveHttpConnections(); } //SMPP balancer public long getNumberOfSmppRequestsToServer() { if(smppBalancerRunner!=null) return smppBalancerRunner.getNumberOfSmppRequestsToServer(); else return 0; } public long getNumberOfSmppRequestsToClient() { if(smppBalancerRunner!=null) return smppBalancerRunner.getNumberOfSmppRequestsToClient(); else return 0; } public long getNumberOfSmppBytesToServer() { if(smppBalancerRunner!=null) return smppBalancerRunner.getNumberOfSmppBytesToServer(); else return 0; } public long getNumberOfSmppBytesToClient() { if(smppBalancerRunner!=null) return smppBalancerRunner.getNumberOfSmppBytesToClient(); else return 0; } public long getSmppRequestsProcessedById(Integer id) { if(smppBalancerRunner!=null) return smppBalancerRunner.getSmppRequestsProcessedById(id); else return 0; } public long getSmppResponsesProcessedById(Integer id) { if(smppBalancerRunner!=null) return smppBalancerRunner.getSmppResponsesProcessedById(id); else return 0; } public int getNumberOfActiveSmppConnections() { if(smppBalancerRunner!=null) return smppBalancerRunner.getNumberOfActiveSmppConnections(); else return 0; } public void incCalls() { counterCalls.inc(); } public void incMessages() { counterMessages.inc(); } // public void incSeconds(long seconds) { // counterSeconds.inc(seconds); // } public void setNodeExpiration(long value) { reg.setNodeExpiration(value); } public void setNodeExpirationTaskInterval(long value) { reg.setNodeExpirationTaskInterval(value); } public List<Node> getNodes() { return new LinkedList<Node>(balancerContext.aliveNodes); } public String[] getNodeList() { List<Node> nodes = getNodes(); String[] nodeList = new String[nodes.size()]; int i = 0; for (Node node : nodes) { nodeList[i] = node.toString(); i++; } return nodeList; } public LoadBalancerConfiguration getConfiguration() { return balancerContext.lbConfig; } //TODO!!!!!!!!! // public String getProperty(String key) { // return properties.getProperty(key); // } // // public void setProperty(String key, String value) { // balancerContext.properties.setProperty(key, value); // for(InvocationContext ctx : contexts.values()) { // ctx.balancerAlgorithm.configurationChanged(); // } // } @SuppressWarnings("restriction") @Override public double getJvmCpuUsage() { return ((com.sun.management.OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean()).getProcessCpuLoad(); } @Override public long getJvmHeapSize() { return ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed(); } } class SipBalancerShutdownHook extends Thread { BalancerRunner balancerRunner; public SipBalancerShutdownHook(BalancerRunner balancerRunner) { this.balancerRunner = balancerRunner; } @Override public void run() { balancerRunner.stop(); } }