View Javadoc

1   /*
2    * Copyright (c) 1998-2002 The Jgroup Team.
3    *
4    * This program is free software; you can redistribute it and/or modify
5    * it under the terms of the GNU Lesser General Public License version 2 as
6    * published by the Free Software Foundation.
7    *
8    * This program is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   * GNU Lesser General Public License for more details.
12   *
13   * You should have received a copy of the GNU Lesser General Public License
14   * along with this program; if not, write to the Free Software
15   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16   *
17   */
18  
19  package jgroup.test.performance;
20  
21  import java.rmi.RemoteException;
22  import java.util.Timer;
23  import java.util.TimerTask;
24  
25  import jgroup.core.GroupUnreachableException;
26  import jgroup.core.registry.DependableRegistry;
27  import jgroup.core.registry.RegistryFactory;
28  import jgroup.util.Network;
29  import jgroup.util.log.Eventlogger;
30  
31  /**
32   *  Test class for measuring the performance of GMI invocations.  This client
33   *  can run tests with several different servers, but only one at a time.
34   *
35   *  @author Hein Meling
36   *  @since Jgroup 1.2
37   */
38  public class PerformanceClient
39  {
40  
41    ////////////////////////////////////////////////////////////////////////////////////////////
42    // Constants
43    ////////////////////////////////////////////////////////////////////////////////////////////
44  
45    /** The possible 'measurement.type' property values */
46    private static final String DELAY_MEASUREMENT      = "delay";
47    private static final String THROUGHPUT_MEASUREMENT = "throughput";
48  
49    /**
50     * Supported properties: Note that if additional properties are to
51     * be added here, recall to also add them to the dev-build.xml file;
52     * the 'perfclient' target.
53     */
54    public static final String[] properties = new String[] {
55        "calibration.period",
56        "request.load.duration",
57        "measurement.type",
58        "requests.per.second",
59        "invocation.type",
60        "buffer.size",
61    };
62  
63    /**
64     *  Set of servers supported for running tests through
65     *  the <code>SpeedTest</code> interface.
66     */
67    private static final String[] supportedServers = new String[] {
68      "Jgroup/PerformanceServer",
69      "ARM/ReplicatedServer",
70      "Jgroup/SpeedTest",
71      "UpgradeServer"
72    };
73  
74    /** Number of milliseconds in a second */
75    private static final int TIME_UNIT = 1000;
76  
77  
78    ////////////////////////////////////////////////////////////////////////////////////////////
79    // Fields
80    ////////////////////////////////////////////////////////////////////////////////////////////
81  
82    /** The speed test server reference */
83    private static SpeedTest test = null;
84  
85    /** The delay between each request performed by this client */
86    private static int requestPeriod;
87  
88    /** The invocation type to be used in the measurements */
89    private static String invocationType;
90  
91    /** The buffer used with buffer invocations */
92    private static byte[] buffer;
93  
94    /** Used to indicate that the calibration period has completed */
95    private static volatile boolean trafficGenerationCompleted = false;
96  
97    /**
98     * Used to indicate that logging has completed,
99     * and that the next generation sequence can commence.
100    */
101   private static volatile boolean loggingCompleted = false;
102 
103   /**
104    *  True when the invocation delays are to be logged, false otherwise.
105    *  Typically, we only log the invocation delay after calibration and
106    *  stop logging as soon as the actual measurement period is complete,
107    *  even though we perform additional invocations for a small period
108    *  to maintain a constant load on the servers.
109    */
110   private static volatile boolean logDelays = false;
111 
112 
113   ////////////////////////////////////////////////////////////////////////////////////////////
114   // Main
115   ////////////////////////////////////////////////////////////////////////////////////////////
116 
117   public static void main(String[] argv)
118     throws Exception
119   {
120     String localHost = Network.getLocalMachineName();
121     System.out.println("Running PerformanceClient: " + localHost);
122     /*
123      * Undefine/clear properties for which ant did not receive any input;
124      * there were no value to assign to the property on the input line.
125      */
126     for (int i = 0; i < properties.length; i++) {
127       String prop = System.getProperty(properties[i]);
128       if (prop != null && prop.startsWith("${")) {
129         System.clearProperty(properties[i]);
130       }
131     }
132     DependableRegistry reg = RegistryFactory.getRegistry();
133     String[] servers = reg.list();
134     for (int i = 0; test == null && i < servers.length; i++) {
135       for (int j = 0; test == null && j < supportedServers.length; j++) {
136         if (servers[i].equals(supportedServers[j])) {
137           test = (SpeedTest) reg.lookup(servers[i]);
138           System.out.println("Found supported server: " + servers[i]);
139         }
140       }
141     }
142     if (test == null) {
143       System.out.println("No supported servers found");
144       System.exit(0);
145     }
146 
147     /*
148      * Retreive system property values for use by the performance client
149      */
150     int calibrationPeriod = Integer.getInteger("calibration.period", 20).intValue();
151     int requestDuration = Integer.getInteger("request.load.duration", 60).intValue();
152     String measurementType = System.getProperty("measurement.type", DELAY_MEASUREMENT);
153     if (measurementType.equalsIgnoreCase(DELAY_MEASUREMENT)) {
154       int requestRate = Integer.getInteger("requests.per.second", 2).intValue();
155       requestPeriod = TIME_UNIT / requestRate;
156     } else if (measurementType.equalsIgnoreCase(THROUGHPUT_MEASUREMENT)) {
157       requestPeriod = -1;
158     }
159     invocationType = System.getProperty("invocation.type", "anycast");
160     String invType = invocationType;
161     if (invocationType.endsWith("-buffer")) {
162       int bufSize = Integer.getInteger("buffer.size", 1000).intValue();
163       buffer = new byte[bufSize];
164       invType = invocationType + "(" + bufSize + ")";
165     }
166 
167     /*
168      * Generate traffic to calibrate the clients.  That is, give all clients
169      * the chance to start up and begin to perform request invocations, and
170      * reduce the impact of JVM initialization, such as class loading etc.
171      */
172     System.out.println("Calibration: " + localHost);
173     generateTraffic(calibrationPeriod);
174     // Calibration is complete; perform the actual traffic generation
175     logDelays = true;
176     System.out.println("Begin logging: " + localHost);
177     Eventlogger.logEvent("BeginTrafficGeneration: " + invType);
178     generateTraffic(requestDuration);
179     Eventlogger.logEvent("EndTrafficGeneration: " + invType);
180     System.out.println("End logging: " + localHost);
181     logDelays = false;
182     Eventlogger.close();
183     /*
184      * Traffic generation complete; generate some addtional traffic
185      * to avoid that some clients experience a less load at the
186      * end of the experiment.
187      */
188     generateTraffic(5);
189     System.out.println("Exiting: " + localHost);
190   }
191 
192 
193   /**
194    * Generate traffic at a <i>fixed-rate</i> for the given duration.
195    * @param duration
196    *   The duration to generate traffic at a fixed-rate.
197    */
198   public static void generateTraffic(final int duration)
199   {
200     final Timer timer = new Timer("GenerateTraffic", true);
201     if (requestPeriod > 0) {
202       TimerTask timerTask = getDelayTimerTask();
203       timer.scheduleAtFixedRate(timerTask, 0, requestPeriod);
204     } else {
205       TimerTask timerTask = getThroughputTimerTask();
206       timer.schedule(timerTask, 0);
207     }
208     new Thread("StopTrafficGeneration") {
209       public void run() {
210         try { sleep(duration*TIME_UNIT);
211         } catch (InterruptedException e) { }
212         // Indicate the end of the current traffic generation period
213         trafficGenerationCompleted = true;
214       }
215     }.start();
216     /*
217      * Await the completion of the current traffic generation period.
218      */
219     while (!trafficGenerationCompleted || !loggingCompleted) {
220       Thread.yield();
221     }
222     // Stop scheduling client requests
223     timer.cancel();
224     // Reset the thread synchronization variables
225     trafficGenerationCompleted = false;
226     loggingCompleted = false;
227   }
228 
229 
230   /**
231    * Returns a <code>TimerTask</code> that should be scheduled only once,
232    * as the run method of the timer task will perform repeated invocations
233    * and measure the duration, and count the number of invocations to
234    * determine the throughput measure (Inovcations / Second).
235    */
236   public static TimerTask getThroughputTimerTask()
237   {
238     return new TimerTask() {
239       public void run() {
240         long invocationCounter = 0;
241         long start = System.currentTimeMillis();
242         try {
243           if (invocationType.equalsIgnoreCase("anycast")) {
244             while (!trafficGenerationCompleted) {
245               test.test();
246               invocationCounter++;
247             }
248           } else if (invocationType.equalsIgnoreCase("anycast-buffer")) {
249             while (!trafficGenerationCompleted) {
250               test.test(buffer);
251               invocationCounter++;
252             }
253           } else if (invocationType.equalsIgnoreCase("multicast")) {
254             while (!trafficGenerationCompleted) {
255               test.mtest();
256               invocationCounter++;
257             }
258           } else if (invocationType.equalsIgnoreCase("multicast-buffer")) {
259             while (!trafficGenerationCompleted) {
260               test.mtest(buffer);
261               invocationCounter++;
262             }
263           } else {
264             throw new IllegalStateException("Unknown invocation type: " + invocationType);
265           }
266         } catch (RemoteException e) {
267           trafficGenerationCompleted = true;
268           e.printStackTrace();
269           Eventlogger.logEvent(e.getMessage());
270         } catch (GroupUnreachableException e) {
271           /*
272            * The group has become unreachable;
273            * stop traffic generation and log the event.
274            */
275           trafficGenerationCompleted = true;
276           Eventlogger.logEvent("GroupUnreachable");
277         }
278         double invocDuration = (System.currentTimeMillis() - start) / TIME_UNIT;
279         double throughput = invocationCounter / invocDuration;
280         // log the throughput value
281         if (logDelays) {
282           Eventlogger.logEventNoSysTime(Double.toString(throughput));
283         }
284         loggingCompleted = true;
285       }
286     };
287   }
288 
289 
290   /**
291    * Returns a <code>TimerTask</code> that can be scheduled at 
292    * <i>fixed-rate</i> intervals, as required by the performance
293    * measurements.
294    */
295   public static TimerTask getDelayTimerTask()
296   {
297     return new TimerTask() {
298       public void run() {
299         try {
300           long invocationDelay = 0;
301           if (invocationType.equalsIgnoreCase("anycast")) {
302             long start = System.nanoTime();
303             test.test();
304             invocationDelay = System.nanoTime() - start;
305           } else if (invocationType.equalsIgnoreCase("anycast-buffer")) {
306             long start = System.nanoTime();
307             test.test(buffer);
308             invocationDelay = System.nanoTime() - start;
309           } else if (invocationType.equalsIgnoreCase("multicast")) {
310             long start = System.nanoTime();
311             test.mtest();
312             invocationDelay = System.nanoTime() - start;
313           } else if (invocationType.equalsIgnoreCase("multicast-buffer")) {
314             long start = System.nanoTime();
315             test.mtest(buffer);
316             invocationDelay = System.nanoTime() - start;
317           } else {
318             throw new IllegalStateException("Unknown invocation type: " + invocationType);
319           }
320           // log the invocation delay
321           if (logDelays) {
322             Eventlogger.logEvent(Long.toString(invocationDelay));
323           }
324         } catch (RemoteException e) {
325           trafficGenerationCompleted = true;
326           e.printStackTrace();
327           Eventlogger.logEvent(e.getMessage());
328         } catch (GroupUnreachableException e) {
329           /*
330            * The group has become unreachable;
331            * stop traffic generation and log the event.
332            */
333           trafficGenerationCompleted = true;
334           Eventlogger.logEvent("GroupUnreachable");
335         } finally {
336           loggingCompleted = true;
337         }
338       }
339     };
340   }
341 
342 } // END PerformanceClient