/* * Sun Public License * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is available at http://www.sun.com/ * * The Original Code is the SLAMD Distributed Load Generation Engine. * The Initial Developer of the Original Code is Neil A. Wilson. * Portions created by Neil A. Wilson are Copyright (C) 2004-2010. * Some preexisting portions Copyright (C) 2002-2006 Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): Neil A. Wilson */ package com.slamd.jobs; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import com.slamd.job.JobClass; import com.slamd.job.UnableToRunException; import com.slamd.parameter.IntegerParameter; import com.slamd.parameter.Parameter; import com.slamd.parameter.ParameterList; import com.slamd.parameter.StringParameter; import com.slamd.stat.LongValueTracker; import com.slamd.stat.StatTracker; /** * This class defines a SLAMD job that may be used in conjunction with the * throughput test server to measure the network throughput between two or more * systems (i.e., one server and one or more clients). * * * @author Neil A. Wilson */ public class ThroughputTestJobClass extends JobClass { /** * The display name of the stat tracker used to track the number of bits read * per second. */ public static final String STAT_TRACKER_BITS_PER_SECOND = "Average Bits Read per Second"; /** * The display name of the stat tracker used to track the number of bytes read * per second. */ public static final String STAT_TRACKER_BYTES_PER_SECOND = "Average Bytes Read per Second"; /** * The display name of the stat tracker used to track the number of kilobits * read per second. */ public static final String STAT_TRACKER_KILOBITS_PER_SECOND = "Average Kilobits Read per Second"; /** * The display name of the stat tracker used to track the number of kilobytes * read per second. */ public static final String STAT_TRACKER_KILOBYTES_PER_SECOND = "Average Kilobytes Read per Second"; /** * The display name of the stat tracker used to track the number of megabits * read per second. */ public static final String STAT_TRACKER_MEGABITS_PER_SECOND = "Average Megabits Read per Second"; /** * The display name of the stat tracker used to track the number of megabytes * read per second. */ public static final String STAT_TRACKER_MEGABYTES_PER_SECOND = "Average Megabytes Read per Second"; // The buffer size to use in bytes. private IntegerParameter bufferSizeParameter = new IntegerParameter("buffer_size", "Read Buffer Size", "The buffer size to use when reading data from " + "the server.", true, 8192, true, 1, false, 0); // The parameter specifying the amount of data to read. private IntegerParameter maxMegabytesParameter = new IntegerParameter("max_megabytes", "Megabytes to Transfer", "The number of megabytes that each client thread " + "should read from the server before " + "disconnecting. A negative value indicates " + "that there should be no limit.", false, -1); // The parameter specifying the port of the throughput test server. private IntegerParameter portParameter = new IntegerParameter("server_port", "Server Port", "The port on which the throughput test server is " + "listening.", true, 3333, true, 1, true, 65535); // The parameter specifying the address of the throughput test server. private StringParameter addressParameter = new StringParameter("server_address", "Server Address", "The address of the system running the throughput " + "test server.", true, ""); // Static variables corresponding to the parameter values. private static int bufferSize; private static int serverPort; private static long maxBytes; private static String serverAddress; // The buffer that will be used to read data from the server. private ByteBuffer readBuffer; // The total number of bytes read so far by this thread. private long totalBytesRead; // The stat tracker used to actually measure the throughput. private LongValueTracker bytesRead; /** * The default constructor used to create a new instance of the job class. * The only thing it should do is to invoke the superclass constructor. All * other initialization should be performed in the <CODE>initialize</CODE> * method. */ public ThroughputTestJobClass() { super(); } /** * {@inheritDoc} */ @Override() public String getJobName() { return "Throughput Test"; } /** * {@inheritDoc} */ @Override() public String getShortDescription() { return "Measure network throughput between client and server systems"; } /** * {@inheritDoc} */ @Override() public String[] getLongDescription() { return new String[] { "This job can be used to measure network throughput between a given " + "server system and one or more client systems. The server system must " + "be running an instance of the throughput test server tool." }; } /** * {@inheritDoc} */ @Override() public String getJobCategoryName() { return "Utility"; } /** * {@inheritDoc} */ @Override() public ParameterList getParameterStubs() { Parameter[] params = new Parameter[] { addressParameter, portParameter, bufferSizeParameter, maxMegabytesParameter }; return new ParameterList(params); } /** * {@inheritDoc} */ @Override() public StatTracker[] getStatTrackerStubs(String clientID, String threadID, int collectionInterval) { return new StatTracker[] { new LongValueTracker(clientID, threadID, STAT_TRACKER_MEGABYTES_PER_SECOND, collectionInterval), new LongValueTracker(clientID, threadID, STAT_TRACKER_MEGABITS_PER_SECOND, collectionInterval), new LongValueTracker(clientID, threadID, STAT_TRACKER_KILOBYTES_PER_SECOND, collectionInterval), new LongValueTracker(clientID, threadID, STAT_TRACKER_KILOBITS_PER_SECOND, collectionInterval), new LongValueTracker(clientID, threadID, STAT_TRACKER_BYTES_PER_SECOND, collectionInterval), new LongValueTracker(clientID, threadID, STAT_TRACKER_BITS_PER_SECOND, collectionInterval), }; } /** * {@inheritDoc} */ @Override() public StatTracker[] getStatTrackers() { LongValueTracker bitsRead = bytesRead.multiplyValues(STAT_TRACKER_BITS_PER_SECOND, 8); LongValueTracker kilobytesRead = bytesRead.divideValues(STAT_TRACKER_KILOBYTES_PER_SECOND, 1024); LongValueTracker kilobitsRead = bitsRead.divideValues(STAT_TRACKER_KILOBITS_PER_SECOND, 1024); LongValueTracker megabytesRead = bytesRead.divideValues(STAT_TRACKER_MEGABYTES_PER_SECOND, (1024*1024)); LongValueTracker megabitsRead = bitsRead.divideValues(STAT_TRACKER_MEGABITS_PER_SECOND, (1024*1024)); return new StatTracker[] { megabytesRead, megabitsRead, kilobytesRead, kilobitsRead, bytesRead, bitsRead }; } /** * {@inheritDoc} */ @Override() public void initializeClient(String clientID, ParameterList parameters) throws UnableToRunException { addressParameter = parameters.getStringParameter(addressParameter.getName()); if (addressParameter != null) { serverAddress = addressParameter.getStringValue(); } portParameter = parameters.getIntegerParameter(portParameter.getName()); if (portParameter != null) { serverPort = portParameter.getIntValue(); } maxBytes = -1; maxMegabytesParameter = parameters.getIntegerParameter(maxMegabytesParameter.getName()); if ((maxMegabytesParameter != null) && maxMegabytesParameter.hasValue()) { maxBytes = 1024 * 1024 * maxMegabytesParameter.getIntValue(); } bufferSize = 8192; bufferSizeParameter = parameters.getIntegerParameter(bufferSizeParameter.getName()); if ((bufferSizeParameter != null) && bufferSizeParameter.hasValue()) { bufferSize = bufferSizeParameter.getIntValue(); } } /** * {@inheritDoc} */ @Override() public void initializeThread(String clientID, String threadID, int collectionInterval, ParameterList parameters) throws UnableToRunException { // Create the stat tracker that we will actually use. bytesRead = new LongValueTracker(clientID, threadID, STAT_TRACKER_BYTES_PER_SECOND, collectionInterval); // Initialize the remaining variables we will use for this thread. totalBytesRead = 0; readBuffer = ByteBuffer.allocateDirect(bufferSize); } /** * {@inheritDoc} */ @Override() public void runJob() { // Establish the connection to the throughput test server. SocketChannel socketChannel; try { socketChannel = SocketChannel.open(new InetSocketAddress(serverAddress, serverPort)); } catch (IOException ioe) { logMessage("Unable to connect to throughput test server " + serverAddress + ':' + serverPort + " -- " + ioe); indicateStoppedDueToError(); return; } bytesRead.startTracker(); // Loop until it is determined that the job should stop. try { while (! shouldStop()) { long numBytesRead = socketChannel.read(readBuffer); readBuffer.clear(); if (numBytesRead < 0) { logMessage("Unexpected end of input stream from server"); indicateStoppedDueToError(); break; } bytesRead.addValue(numBytesRead); totalBytesRead += numBytesRead; if ((maxBytes > 0) && (totalBytesRead > maxBytes)) { break; } } } catch (Exception e) { logMessage("Caught an exception while reading data from the server -- " + stackTraceToString(e)); indicateStoppedDueToError(); } bytesRead.stopTracker(); // Close the connection to the server. try { socketChannel.close(); } catch (Exception e) {} } }