001: /******************************************************************************
002: * LoadCount.java
003: * ****************************************************************************/package org.openlaszlo.servlets;
004:
005: import java.util.Date;
006: import java.util.LinkedList;
007: import java.text.DecimalFormat;
008:
009: /**
010: * Thread-safe class to monitor load count. Displays:
011: *
012: * <ul>
013: * <li>...
014: * </ul>
015: */
016: public class LoadCount {
017: public class LoadInfo {
018: // number of requests processed
019: public int m01Requests = 0;
020: public int m05Requests = 0;
021: public int m15Requests = 0;
022:
023: // request avg. (requests / second)
024: public float m01RequestAvg = 0;
025: public float m05RequestAvg = 0;
026: public float m15RequestAvg = 0;
027:
028: // avg. request time
029: public float m01AvgResTime = 0;
030: public float m05AvgResTime = 0;
031: public float m15AvgResTime = 0;
032:
033: // max. request time
034: public float m01MaxResTime = 0;
035: public float m05MaxResTime = 0;
036: public float m15MaxResTime = 0;
037:
038: // active requests
039: public int mActiveRequestCount = 0;
040:
041: // total count for current bucket
042: public int mCurrentRequestCount = 0;
043: public int mCurrentResponseCount = 0;
044: public int mCurrentResponseTimes = 0;
045:
046: // total request/response count since object instantiation
047: public int mTotalRequestCount = 0;
048: public int mTotalResponseCount = 0;
049: }
050:
051: private static final int SECONDS_PER_MINUTE = 60;
052: private static final int LOAD_01_MINUTES = 1;
053: private static final int LOAD_05_MINUTES = 5;
054: private static final int LOAD_15_MINUTES = 15;
055:
056: /** Display pattern to apply for floats. */
057: private DecimalFormat mFormat = new DecimalFormat("#.##");
058:
059: /** The time when the next sample snapshot should happen. */
060: private long mNext;
061:
062: /** Count of active request count. */
063: private int mActiveRequestCount;
064:
065: /** Total request count since object creation. */
066: private int mTotalRequestCount;
067:
068: /** Total response count since object creation. */
069: private int mTotalResponseCount;
070:
071: /** Count of current sample load count. */
072: private int mCurrentRequestCount;
073:
074: /** Each bucket represents requests per sample. */
075: private int[] mRequestCount;
076:
077: /** Sum of current request times (in milliseconds). */
078: private int mCurrentResponseTimes;
079:
080: /** Each bucket represents the sum of request times (in milliseconds) for a
081: * sample interval. */
082: private int[] mResponseTimes;
083:
084: /** Current max response time (in milliseconds). */
085: private int mCurrentMaxResponseTime;
086:
087: /** Each bucket represents the max response time (in milliseconds) for a
088: * sample interval. */
089: private int[] mMaxResponseTime;
090:
091: /** Sum of current request times (in milliseconds). */
092: private int mCurrentResponseCount;
093:
094: /** Each bucket represents the sum of request times (in milliseconds) per
095: * sample interval. */
096: private int[] mResponseCount;
097:
098: /** Location of array bucket to place the current sample count. */
099: private int mPtr;
100:
101: /** Max number of sample slots for array. */
102: private int mMaxSamples;
103:
104: /** Seconds per sample. */
105: private int mSecsPerSample;
106:
107: /** Millisconds per sample. */
108: private int mMilliSecsPerSample;
109:
110: /** Last response time. */
111: private int mLastResponseTime;
112:
113: /** Number of buckets used by 1 minute. */
114: private int m01;
115:
116: /** Number of buckets used by 5 minutes. */
117: private int m05;
118:
119: /** Number of buckets used by 15 minutes. */
120: private int m15;
121:
122: /**
123: * Construct a load count with 60 second sampling.
124: */
125: public LoadCount() {
126: this (SECONDS_PER_MINUTE);
127: }
128:
129: /**
130: * Construct a load count with a user-defined sampling interval. If the
131: * sampling interval is not divisble by 60 seconds, the following values are
132: * used:
133: *
134: * <ul>
135: * <li>less than 1 => default to 15 secs</li>
136: * <li>greater than a minute => default to 60 secs</li>
137: * <li>not divisible into 60 secs => find the next higher divisible
138: * number</li>
139: * </ul>
140: *
141: * @param secsPerSample the time granularity on how often a load sample
142: * should be taken.
143: */
144: public LoadCount(int secsPerSample) {
145: // Make sure sample interval is a divisor of 60 seconds.
146: if (secsPerSample < 1) {
147: // less than 1 => default to 15 secs
148: secsPerSample = 15;
149: } else if (SECONDS_PER_MINUTE < secsPerSample) {
150: // greater than a minute => default to 60 secs
151: secsPerSample = SECONDS_PER_MINUTE;
152: } else if (SECONDS_PER_MINUTE % secsPerSample != 0) {
153: // not divisible into 60 secs => find the next higher divisible
154: // number
155: while (SECONDS_PER_MINUTE % ++secsPerSample != 0)
156: ;
157: }
158:
159: mSecsPerSample = secsPerSample;
160: mMilliSecsPerSample = mSecsPerSample * 1000;
161:
162: // N sample slots for 15 minutes.
163: mMaxSamples = (LOAD_15_MINUTES * SECONDS_PER_MINUTE)
164: / mSecsPerSample;
165: mRequestCount = new int[mMaxSamples];
166: mResponseCount = new int[mMaxSamples];
167: mResponseTimes = new int[mMaxSamples];
168: mMaxResponseTime = new int[mMaxSamples];
169:
170: m01 = (LOAD_01_MINUTES * SECONDS_PER_MINUTE) / mSecsPerSample;
171: m05 = (LOAD_05_MINUTES * SECONDS_PER_MINUTE) / mSecsPerSample;
172: m15 = (LOAD_15_MINUTES * SECONDS_PER_MINUTE) / mSecsPerSample;
173:
174: // reset all values
175: reset();
176: }
177:
178: /**
179: * Clear and reset all values.
180: */
181: synchronized public void reset() {
182: mNext = System.currentTimeMillis() + mSecsPerSample;
183: mPtr = 0;
184: mActiveRequestCount = 0;
185: mCurrentRequestCount = 0;
186: mCurrentResponseTimes = 0;
187: mCurrentResponseCount = 0;
188: mCurrentMaxResponseTime = 0;
189: mTotalRequestCount = 0;
190: mTotalResponseCount = 0;
191: mLastResponseTime = -1;
192:
193: for (int i = 0; i < mMaxSamples; i++) {
194: mRequestCount[i] = 0;
195: mResponseCount[i] = 0;
196: mResponseTimes[i] = 0;
197: mMaxResponseTime[i] = 0;
198: }
199: }
200:
201: /**
202: * Check if it's time to count the next sample interval.
203: */
204: private void check() {
205: long now = System.currentTimeMillis();
206: if (mNext <= now) {
207:
208: // Ptr to current ptr.
209: int prevPtr = mPtr;
210:
211: // (1) find out how many sample intervals it's been. at least one
212: // interval has elapsed or we wouldn't be in here.
213: int n = (int) ((now - mNext) / mMilliSecsPerSample) + 1;
214:
215: // (2.a) calculate and place request time avg
216: // (2.b) add last count to sample bucket
217: // (2.c) check if we have to wrap around arrays
218: mRequestCount[mPtr] = mCurrentRequestCount;
219: mResponseCount[mPtr] = mCurrentResponseCount;
220: mResponseTimes[mPtr] = mCurrentResponseTimes;
221: mMaxResponseTime[mPtr] = mCurrentMaxResponseTime;
222: if (--mPtr < 0)
223: mPtr = mMaxSamples - 1;
224:
225: // (3) zero out rest of buckets during silent interval
226: int count = n - 1;
227: while (0 < count--) {
228: mRequestCount[mPtr] = 0;
229: mResponseCount[mPtr] = 0;
230: mResponseTimes[mPtr] = 0;
231: mMaxResponseTime[mPtr] = 0;
232:
233: // We've zeroed out everything. Don't need to clear things more
234: // than once.
235: if (prevPtr == mPtr)
236: break;
237:
238: if (--mPtr < 0)
239: mPtr = mMaxSamples - 1;
240: }
241:
242: // (4) set next time we need to snap
243: mNext += (mMilliSecsPerSample * n);
244:
245: // (5) clear count
246: mCurrentRequestCount = 0;
247: mCurrentResponseCount = 0;
248: mCurrentResponseTimes = 0;
249:
250: // (6) Clear max response time
251: mCurrentMaxResponseTime = 0;
252: }
253: }
254:
255: /**
256: * Increment load count for the current sample interval giving it a time.
257: * @param t time it took for request to finish.
258: */
259: synchronized public void decrement(int t) {
260: check();
261: --mActiveRequestCount;
262: ++mCurrentResponseCount;
263: mCurrentResponseTimes += t;
264: ++mTotalResponseCount;
265: mLastResponseTime = t;
266:
267: if (t > mCurrentMaxResponseTime) {
268: mCurrentMaxResponseTime = t;
269: }
270: }
271:
272: /**
273: * Increment load count for the current sample interval.
274: */
275: synchronized public void increment() {
276: check();
277: ++mActiveRequestCount;
278: ++mCurrentRequestCount;
279: ++mTotalRequestCount;
280: }
281:
282: synchronized public LoadInfo getLoadInfo() {
283: check();
284:
285: LoadInfo li = new LoadInfo();
286:
287: int p = mPtr + 1;
288: int n = 0;
289: int reqCountSum = 0;
290: int resCountSum = 0;
291: int resTimeSum = 0;
292:
293: int maxResponseTime = 0;
294:
295: while (n++ < m15) {
296: p %= m15;
297: reqCountSum += mRequestCount[p];
298: resCountSum += mResponseCount[p];
299: resTimeSum += mResponseTimes[p];
300: if (mMaxResponseTime[p] > maxResponseTime) {
301: maxResponseTime = mMaxResponseTime[p];
302: }
303: p++;
304: if (n == m01) {
305: li.m01Requests = reqCountSum;
306: if (reqCountSum != 0)
307: li.m01RequestAvg = (float) reqCountSum
308: / (float) (LOAD_01_MINUTES * SECONDS_PER_MINUTE);
309: if (resCountSum != 0)
310: li.m01AvgResTime = (float) resTimeSum
311: / (float) resCountSum;
312: li.m01MaxResTime = maxResponseTime;
313: }
314: if (n == m05) {
315: li.m05Requests = reqCountSum;
316: if (reqCountSum != 0)
317: li.m05RequestAvg = (float) reqCountSum
318: / (float) (LOAD_05_MINUTES * SECONDS_PER_MINUTE);
319: if (resCountSum != 0)
320: li.m05AvgResTime = (float) resTimeSum
321: / (float) resCountSum;
322: li.m05MaxResTime = maxResponseTime;
323: }
324: if (n == m15) {
325: li.m15Requests = reqCountSum;
326: if (reqCountSum != 0)
327: li.m15RequestAvg = (float) reqCountSum
328: / (float) (LOAD_15_MINUTES * SECONDS_PER_MINUTE);
329: if (resCountSum != 0)
330: li.m15AvgResTime = (float) resTimeSum
331: / (float) resCountSum;
332: li.m15MaxResTime = maxResponseTime;
333: }
334: }
335:
336: li.mActiveRequestCount = mActiveRequestCount;
337: li.mCurrentRequestCount = mCurrentRequestCount;
338: li.mCurrentResponseCount = mCurrentResponseCount;
339: li.mCurrentResponseTimes = mCurrentResponseTimes;
340:
341: li.mTotalRequestCount = mTotalRequestCount;
342: li.mTotalResponseCount = mTotalResponseCount;
343:
344: return li;
345: }
346:
347: /**
348: * Return load information in XML w/o sample interval view.
349: * @param tag tag name
350: */
351: public String toXML(String tag) {
352: return toXML(tag, false);
353: }
354:
355: /**
356: * Return load information in XML.
357: * @param tag tag name
358: * @param verbose if true, returns information with all samples.
359: */
360: synchronized public String toXML(String tag, boolean verbose) {
361: StringBuffer buf = new StringBuffer();
362: LoadInfo load = getLoadInfo();
363:
364: // req: total requests for interval
365: // rps: requests per second during interval
366: // art: average response time (in milliseconds)
367: buf.append("<").append(tag).append(" sample-seconds=\"")
368: .append(mSecsPerSample).append("\"").append(
369: " active-requests=\"").append(
370: load.mActiveRequestCount).append("\"").append(
371: " last-response-time=\"").append(
372: mLastResponseTime).append("\"").append(">\n");
373:
374: buf.append("<one").append(" requests=\"").append(
375: load.m01Requests).append("\"").append(
376: " requests-per-second=\"").append(
377: mFormat.format(load.m01RequestAvg)).append("\"")
378: .append(" avg-response-time=\"").append(
379: mFormat.format(load.m01AvgResTime))
380: .append("\"").append(" max-response-time=\"").append(
381: mFormat.format(load.m01MaxResTime))
382: .append("\"").append(" />\n");
383:
384: buf.append("<five").append(" requests=\"").append(
385: load.m05Requests).append("\"").append(
386: " requests-per-second=\"").append(
387: mFormat.format(load.m05RequestAvg)).append("\"")
388: .append(" avg-response-time=\"").append(
389: mFormat.format(load.m05AvgResTime))
390: .append("\"").append(" max-response-time=\"").append(
391: mFormat.format(load.m05MaxResTime))
392: .append("\"").append(" />\n");
393:
394: buf.append("<fifteen").append(" requests=\"").append(
395: load.m15Requests).append("\"").append(
396: " requests-per-second=\"").append(
397: mFormat.format(load.m15RequestAvg)).append("\"")
398: .append(" avg-response-time=\"").append(
399: mFormat.format(load.m15AvgResTime))
400: .append("\"").append(" max-response-time=\"").append(
401: mFormat.format(load.m15MaxResTime))
402: .append("\"").append(" />\n");
403:
404: buf.append("<total").append(" requests=\"").append(
405: load.mTotalRequestCount).append("\"").append(
406: " responses=\"").append(load.mTotalResponseCount)
407: .append("\"").append(" />\n");
408:
409: // sample information
410: // req: total requests for sample
411: // time: sum of requests times during sample (in milliseconds)
412: if (verbose) {
413: buf.append("<samples>\n");
414: int p = mPtr + 1;
415: int n = 0;
416: while (n++ < m15) {
417: p %= m15;
418: buf.append("<interval").append(" n=\"").append(n)
419: .append("\"").append(" requests=\"").append(
420: mRequestCount[p]).append("\"").append(
421: " responses=\"").append(
422: mResponseCount[p]).append("\"").append(
423: " response-times-sum=\"").append(
424: mResponseTimes[p]).append("\"").append(
425: " />");
426: p++;
427: }
428: buf.append("</samples>\n");
429: }
430:
431: buf.append("</").append(tag).append(">\n");
432:
433: return buf.toString();
434: }
435:
436: /**
437: * See toXML().
438: */
439: public String toString() {
440: return toXML("load");
441: }
442: }
|