/* *************************************************************************************** * Copyright (C) 2006 EsperTech, Inc. All rights reserved. * * http://www.espertech.com/esper * * http://www.espertech.com * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the GPL license * * a copy of which has been included with this distribution in the license.txt file. * *************************************************************************************** */ package com.espertech.esper.example.benchmark.server; import com.espertech.esper.example.benchmark.Symbols; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.Properties; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.*; /** * The main Esper Server thread listens on the given port. * It bootstrap an ESP/CEP engine (defaults to Esper) and registers EPL statement(s) into it based * on the given -mode argument. * Statements are read from an statements.properties file in the classpath * If statements contains '$' the '$' is replaced by a symbol string, so as to register one statement per symbol. * <p/> * Based on -queue, the server implements a direct handoff to the ESP/CEP engine, or uses a Syncrhonous queue * (somewhat an indirect direct handoff), or uses a FIFO queue where each events is put/take one by one from the queue. * Usually with few clients sending a lot of events, use the direct handoff, else consider using queues. Consumer thread * can be configured using -thread (it will range up to #processor x #thread). * When queues is full, overload policy triggers execution on the caller side. * <p/> * To simulate an ESP/CEP listener work, use -sleep. * <p/> * Use -stat to control how often percentile stats are displayed. At each display stats are reset. * <p/> * If you use -rate nxM (n threads, M event/s), the server will simulate the load for a standalone simulation without * any remote client(s). * <p/> * By default the benchmark registers a subscriber to the statement(s). Use -Desper.benchmark.ul to use * an UpdateListener instead. Note that the subscriber contains suitable update(..) methods for the default * proposed statement in the statements.properties files but might not be suitable if you change statements due * to the strong binding with statement results. * * @author Alexandre Vasseur http://avasseur.blogspot.com */ public class Server extends Thread { private int port; private int threadCore; private int queueMax; private int sleepListenerMillis; private int statSec; private int simulationRate; private int simulationThread; private String mode; public static final int DEFAULT_PORT = 6789; public static final int DEFAULT_THREADCORE = Runtime.getRuntime().availableProcessors(); public static final int DEFAULT_QUEUEMAX = -1; public static final int DEFAULT_SLEEP = 0; public static final int DEFAULT_SIMULATION_RATE = -1; //-1: no simulation public static final int DEFAULT_SIMULATION_THREAD = -1; //-1: no simulation public static final int DEFAULT_STAT = 5; public static final String DEFAULT_MODE = "NOOP"; public static final Properties MODES = new Properties(); private ThreadPoolExecutor executor; //can be null private CEPProvider.ICEPProvider cepProvider; public Server(String mode, int port, int threads, int queueMax, int sleep, final int statSec, int simulationThread, final int simulationRate) { super("EsperServer-main"); this.mode = mode; this.port = port; this.threadCore = threads; this.queueMax = queueMax; this.sleepListenerMillis = sleep; this.statSec = statSec; this.simulationThread = simulationThread; this.simulationRate = simulationRate; // turn on stat dump Timer t = new Timer("EsperServer-stats", true); t.scheduleAtFixedRate(new TimerTask() { public void run() { StatsHolder.dump("engine"); StatsHolder.dump("server"); StatsHolder.dump("endToEnd"); StatsHolder.reset(); if (simulationRate <= 0) { ClientConnection.dumpStats(statSec); } else { SimulateClientConnection.dumpStats(statSec); } } }, 0L, statSec * 1000); } public void setCEPProvider(CEPProvider.ICEPProvider cepProvider) { this.cepProvider = cepProvider; } public synchronized void start() { // register ESP/CEP engine cepProvider = CEPProvider.getCEPProvider(); cepProvider.init(sleepListenerMillis); // register statements String suffix = Server.MODES.getProperty("_SUFFIX"); if ("NOOP".equals(mode)) { // no action } else { String stmtString = Server.MODES.getProperty(mode) + " " + suffix; System.out.println("Using " + mode + " : " + stmtString); if (Server.MODES.getProperty(mode).indexOf('$') < 0) { cepProvider.registerStatement(stmtString, mode); System.out.println("\nStatements registered # 1 only"); } else { // create a stmt for each symbol for (int i = 0; i < Symbols.SYMBOLS.length; i++) { if (i % 100 == 0) System.out.print("."); String ticker = Symbols.SYMBOLS[i]; cepProvider.registerStatement(stmtString.replaceAll("\\$", ticker), mode + "-" + ticker); } System.out.println("\nStatements registered # " + Symbols.SYMBOLS.length); } } // start thread pool if any if (queueMax < 0) { executor = null; System.out.println("Using direct handoff, cpu#" + Runtime.getRuntime().availableProcessors()); } else { // executor System.out.println("Using ThreadPoolExecutor, cpu#" + Runtime.getRuntime().availableProcessors() + ", threadCore#" + threadCore + " queue#" + queueMax); BlockingQueue<Runnable> queue; if (queueMax == 0) { queue = new SynchronousQueue<Runnable>(true); //enforce fairness } else { queue = new LinkedBlockingQueue<Runnable>(queueMax); } executor = new ThreadPoolExecutor( threadCore, Runtime.getRuntime().availableProcessors() * threadCore, 10, TimeUnit.SECONDS, queue, new ThreadFactory() { long count = 0; public Thread newThread(Runnable r) { System.out.println("Create EsperServer thread " + (count + 1)); return new Thread(r, "EsperServer-" + count++); } }, new ThreadPoolExecutor.CallerRunsPolicy() { public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { super.rejectedExecution(r, e); } } ); executor.prestartAllCoreThreads(); } super.start(); } public void run() { if (simulationRate <= 0) { runServer(); } else { runSimulation(); } } public void runServer() { try { System.out.println((new StringBuilder("Server accepting connections on port ")).append(port).append(".").toString()); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(port)); do { SocketChannel socketChannel = serverSocketChannel.accept(); System.out.println("Client connected to server."); (new ClientConnection(socketChannel, executor, cepProvider, statSec)).start(); } while (true); } catch (IOException e) { throw new RuntimeException(e); } } public void runSimulation() { System.out.println("Server in sumulation mode with event/s " + simulationThread + " x " + simulationRate + " = " + simulationThread * simulationRate ); SimulateClientConnection[] sims = new SimulateClientConnection[simulationThread]; for (int i = 0; i < sims.length; i++) { sims[i] = new SimulateClientConnection(simulationRate, executor, cepProvider, statSec); sims[i].start(); } try { for (SimulateClientConnection sim : sims) { sim.join(); } } catch (InterruptedException e) { } } public static void main(String[] argv) throws IOException { // load modes MODES.load(Server.class.getClassLoader().getResourceAsStream("statements.properties")); MODES.put("NOOP", ""); int port = DEFAULT_PORT; int threadCore = DEFAULT_THREADCORE; int queueMax = DEFAULT_QUEUEMAX; int sleep = DEFAULT_SLEEP; int simulationRate = DEFAULT_SIMULATION_RATE; int simulationThread = DEFAULT_SIMULATION_THREAD; String mode = DEFAULT_MODE; int stats = DEFAULT_STAT; for (int i = 0; i < argv.length; i++) if ("-port".equals(argv[i])) { i++; port = Integer.parseInt(argv[i]); } else if ("-thread".equals(argv[i])) { i++; threadCore = Integer.parseInt(argv[i]); } else if ("-queue".equals(argv[i])) { i++; queueMax = Integer.parseInt(argv[i]); } else if ("-sleep".equals(argv[i])) { i++; sleep = Integer.parseInt(argv[i]); } else if ("-stat".equals(argv[i])) { i++; stats = Integer.parseInt(argv[i]); } else if ("-mode".equals(argv[i])) { i++; mode = argv[i]; if (MODES.getProperty(mode) == null) { System.err.println("Unknown mode"); printUsage(); } } else if ("-rate".equals(argv[i])) { i++; int xIndex = argv[i].indexOf('x'); simulationThread = Integer.parseInt(argv[i].substring(0, xIndex)); simulationRate = Integer.parseInt(argv[i].substring(xIndex + 1)); } else { printUsage(); } Server bs = new Server(mode, port, threadCore, queueMax, sleep, stats, simulationThread, simulationRate); bs.start(); try { bs.join(); } catch (InterruptedException e) { } } private static void printUsage() { System.err.println("usage: com.espertech.esper.example.benchmark.server.Server <-port #> <-thread #> <-queue #> <-sleep #> <-stat #> <-rate #x#> <-mode xyz>"); System.err.println("defaults:"); System.err.println(" -port: " + DEFAULT_PORT); System.err.println(" -thread: " + DEFAULT_THREADCORE); System.err.println(" -queue: " + DEFAULT_QUEUEMAX + "(-1: no executor, 0: SynchronousQueue, n: LinkedBlockingQueue"); System.err.println(" -sleep: " + DEFAULT_SLEEP + "(no sleep)"); System.err.println(" -stat: " + DEFAULT_STAT + "(s)"); System.err.println(" -rate: " + DEFAULT_SIMULATION_RATE + "(no standalone simulation, else <n>x<evt/s> such as 2x1000)"); System.err.println(" -mode: " + "(default " + DEFAULT_MODE + ", choose from " + MODES.keySet().toString() + ")"); System.err.println("Modes are read from statements.properties in the classpath"); System.exit(1); } }