1 /***
2 *
3 * Copyright 2004 Hiram Chirino
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.activeio.net.benchmark;
18
19 import java.io.IOException;
20 import java.lang.reflect.InvocationTargetException;
21 import java.net.URI;
22 import java.net.URISyntaxException;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.Iterator;
26
27 import org.activeio.ChannelFactory;
28 import org.activeio.Packet;
29 import org.activeio.SynchChannel;
30 import org.activeio.packet.ByteArrayPacket;
31 import org.activeio.packet.EOSPacket;
32 import org.activeio.stats.CountStatisticImpl;
33 import org.activeio.stats.TimeStatisticImpl;
34 import org.apache.commons.beanutils.BeanUtils;
35
36 import EDU.oswego.cs.dl.util.concurrent.Latch;
37 import EDU.oswego.cs.dl.util.concurrent.PooledExecutor;
38
39 /***
40 * Simulates multiple a simple tcp echo clients for use in benchmarking activeio
41 * channel implementations.
42 *
43 * @version $Revision$
44 */
45 public class ClientLoadSimulator implements Runnable {
46
47 private URI url;
48
49
50 private int concurrentClients = 10;
51 private long rampUpTime = 1000 * concurrentClients;
52
53
54 private long requestDelay = 500;
55 private int requestIterations = Integer.MAX_VALUE;
56 private int requestSize = 1024;
57
58 PooledExecutor threadPool = new PooledExecutor();
59
60
61 private Packet requestPacket;
62 private long sampleInterval = 1000;
63 private ChannelFactory factory = new ChannelFactory();
64 private Latch shutdownLatch;
65
66 private final CountStatisticImpl activeConnectionsCounter = new CountStatisticImpl("activeConnectionsCounter",
67 "The number of active connection attached to the server.");
68 private final CountStatisticImpl echoedBytesCounter = new CountStatisticImpl("echoedBytesCounter",
69 "The number of bytes that have been echoed by the server.");
70 private final TimeStatisticImpl requestLatency = new TimeStatisticImpl("requestLatency",
71 "The amount of time that is spent waiting for a request to be serviced");
72
73 public static void main(String[] args) throws URISyntaxException, IllegalAccessException, InvocationTargetException {
74
75 ClientLoadSimulator client = new ClientLoadSimulator();
76
77 HashMap options = new HashMap();
78 for (int i = 0; i < args.length; i++) {
79
80 String option = args[i];
81 if (!option.startsWith("-") || option.length() < 2 || i + 1 >= args.length) {
82 System.out.println("Invalid usage");
83 return;
84 }
85
86 option = option.substring(1);
87 options.put(option, args[++i]);
88 }
89
90 BeanUtils.populate(client, options);
91
92 System.out.println();
93 System.out.println("Server starting with the following options: ");
94 System.out.println(" url="+client.getUrl());
95 System.out.println(" sampleInterval="+client.getSampleInterval());
96 System.out.println(" concurrentClients="+client.getConcurrentClients());
97 System.out.println(" rampUpTime="+client.getRampUpTime());
98 System.out.println(" requestIterations="+client.getRequestIterations());
99 System.out.println(" requestSize="+client.getRequestSize());
100 System.out.println(" requestDelay="+client.getRequestDelay());
101 System.out.println();
102 client.run();
103
104 }
105 private void printSampleData() {
106 long now = System.currentTimeMillis();
107 float runDuration = (now - activeConnectionsCounter.getStartTime()) / 1000f;
108 System.out.println("Active connections: " + activeConnectionsCounter.getCount());
109 System.out.println("Echoed bytes: " + (echoedBytesCounter.getCount()/1024f) + " kb"
110 + ", Request latency: " + requestLatency.getAverageTime()+" ms");
111 echoedBytesCounter.reset();
112 requestLatency.reset();
113 }
114
115 public void run() {
116 ArrayList clients = new ArrayList();
117 try {
118
119 shutdownLatch = new Latch();
120 activeConnectionsCounter.reset();
121 echoedBytesCounter.reset();
122
123 new Thread("Sampler") {
124 public void run() {
125 System.out.println("Sampler started.");
126 try {
127 while (!shutdownLatch.attempt(sampleInterval)) {
128 printSampleData();
129 }
130 } catch (InterruptedException e) {
131 }
132 System.out.println("Sampler stopped.");
133 }
134 }.start();
135
136 byte data[] = new byte[requestSize];
137 for (int i = 0; i < data.length; i++) {
138 data[i] = (byte) i;
139 }
140 requestPacket = new ByteArrayPacket(data);
141
142
143
144 long clientActivationDelay = rampUpTime / concurrentClients;
145 for (int i = 0; i < concurrentClients && !shutdownLatch.attempt(clientActivationDelay); i++) {
146 System.out.println("Adding Client: " + i);
147 Client client = new Client();
148 clients.add(client);
149 new Thread(client, "Client: " + i).start();
150 }
151
152 shutdownLatch.acquire();
153
154 } catch (InterruptedException e) {
155 } finally {
156 System.out.println("Shutting down clients.");
157 for (Iterator iter = clients.iterator(); iter.hasNext();) {
158 Client client = (Client) iter.next();
159 client.dispose();
160 }
161 }
162 }
163
164 public String getUrl() {
165 return url.toString();
166 }
167
168 public void setUrl(String url) throws URISyntaxException {
169 this.url = new URI(url);
170 }
171
172 class Client implements Runnable {
173
174 private Latch shutdownLatch = new Latch();
175
176 Packet packet = requestPacket.duplicate();
177
178 private SynchChannel synchChannel;
179
180 public void run() {
181 try {
182 System.out.println("Client started.");
183
184 activeConnectionsCounter.increment();
185 synchChannel = factory.openSynchChannel(url);
186 for (int i = 0; i < requestIterations && !shutdownLatch.attempt(1) ; i++) {
187
188 long start = System.currentTimeMillis();
189 sendRequest();
190 long end = System.currentTimeMillis();
191
192 requestLatency.addTime(end - start);
193 echoedBytesCounter.add(packet.remaining());
194
195 if( requestDelay > 0 ) {
196 Thread.sleep(requestDelay);
197 }
198 }
199
200 } catch (IOException e) {
201 e.printStackTrace();
202 } catch (InterruptedException e) {
203 e.printStackTrace();
204 } finally {
205 System.out.println("Client stopped.");
206
207 activeConnectionsCounter.decrement();
208 if( synchChannel!=null ) {
209 synchChannel.dispose();
210 synchChannel = null;
211 }
212 }
213 }
214
215 private void sendRequest() throws IOException, InterruptedException {
216
217 final Latch done = new Latch();
218
219
220
221 threadPool.execute(new Runnable() {
222 public void run() {
223 try {
224 int c = 0;
225 while (c < packet.remaining()) {
226 Packet p = synchChannel.read(1000*5);
227 if( p==null ) {
228 continue;
229 }
230 if( p == EOSPacket.EOS_PACKET ) {
231 System.out.println("Peer disconnected.");
232 dispose();
233 }
234 c += p.remaining();
235 }
236 done.release();
237 } catch (IOException e) {
238 e.printStackTrace();
239 }
240 }
241 });
242
243 synchChannel.write(packet.duplicate());
244 synchChannel.flush();
245 done.acquire();
246
247 }
248
249 public void dispose() {
250 shutdownLatch.release();
251 }
252 }
253
254 /***
255 * @return Returns the concurrentClients.
256 */
257 public int getConcurrentClients() {
258 return concurrentClients;
259 }
260
261 /***
262 * @param concurrentClients
263 * The concurrentClients to set.
264 */
265 public void setConcurrentClients(int concurrentClients) {
266 this.concurrentClients = concurrentClients;
267 }
268
269 /***
270 * @return Returns the rampUpTime.
271 */
272 public long getRampUpTime() {
273 return rampUpTime;
274 }
275
276 /***
277 * @param rampUpTime
278 * The rampUpTime to set.
279 */
280 public void setRampUpTime(long rampUpTime) {
281 this.rampUpTime = rampUpTime;
282 }
283
284 /***
285 * @return Returns the requestDelay.
286 */
287 public long getRequestDelay() {
288 return requestDelay;
289 }
290
291 /***
292 * @param requestDelay
293 * The requestDelay to set.
294 */
295 public void setRequestDelay(long requestDelay) {
296 this.requestDelay = requestDelay;
297 }
298
299 /***
300 * @return Returns the requestIterations.
301 */
302 public int getRequestIterations() {
303 return requestIterations;
304 }
305
306 /***
307 * @param requestIterations
308 * The requestIterations to set.
309 */
310 public void setRequestIterations(int requestIterations) {
311 this.requestIterations = requestIterations;
312 }
313
314 /***
315 * @return Returns the requestSize.
316 */
317 public int getRequestSize() {
318 return requestSize;
319 }
320
321 /***
322 * @param requestSize
323 * The requestSize to set.
324 */
325 public void setRequestSize(int requestSize) {
326 this.requestSize = requestSize;
327 }
328
329 /***
330 * @return Returns the sampleInterval.
331 */
332 public long getSampleInterval() {
333 return sampleInterval;
334 }
335 /***
336 * @param sampleInterval The sampleInterval to set.
337 */
338 public void setSampleInterval(long sampleInterval) {
339 this.sampleInterval = sampleInterval;
340 }
341 }