/hudson-core/src/main/java/hudson/node_monitors/ResponseTimeMonitor.java

http://github.com/hudson/hudson · Java · 173 lines · 98 code · 17 blank · 58 comment · 10 complexity · 88c4bf44657df41c32de8197552d9348 MD5 · raw file

  1. /*
  2. * The MIT License
  3. *
  4. * Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Red Hat, Inc.
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. package hudson.node_monitors;
  25. import hudson.Util;
  26. import hudson.Extension;
  27. import hudson.model.Descriptor.FormException;
  28. import hudson.slaves.OfflineCause;
  29. import hudson.model.Computer;
  30. import hudson.remoting.Callable;
  31. import hudson.remoting.Future;
  32. import hudson.util.TimeUnit2;
  33. import hudson.util.IOException2;
  34. import net.sf.json.JSONObject;
  35. import org.kohsuke.stapler.StaplerRequest;
  36. import java.io.IOException;
  37. import java.util.concurrent.ExecutionException;
  38. import java.util.concurrent.TimeUnit;
  39. import java.util.concurrent.TimeoutException;
  40. import java.util.logging.Logger;
  41. import org.kohsuke.stapler.export.Exported;
  42. import org.kohsuke.stapler.export.ExportedBean;
  43. /**
  44. * Monitors the round-trip response time to this slave.
  45. *
  46. * @author Kohsuke Kawaguchi
  47. */
  48. public class ResponseTimeMonitor extends NodeMonitor {
  49. @Extension
  50. public static final AbstractNodeMonitorDescriptor<Data> DESCRIPTOR = new AbstractNodeMonitorDescriptor<Data>() {
  51. protected Data monitor(Computer c) throws IOException, InterruptedException {
  52. Data old = get(c);
  53. Data d;
  54. long start = System.nanoTime();
  55. Future<String> f = c.getChannel().callAsync(new NoopTask());
  56. try {
  57. f.get(TIMEOUT, TimeUnit.MILLISECONDS);
  58. long end = System.nanoTime();
  59. d = new Data(old,TimeUnit2.NANOSECONDS.toMillis(end-start));
  60. } catch (ExecutionException e) {
  61. throw new IOException2(e.getCause()); // I don't think this is possible
  62. } catch (TimeoutException e) {
  63. // special constant to indicate that the processing timed out.
  64. d = new Data(old,-1L);
  65. }
  66. if(d.hasTooManyTimeouts() && !isIgnored()) {
  67. // unlike other monitors whose failure still allow us to communicate with the slave,
  68. // the failure in this monitor indicates that we are just unable to make any requests
  69. // to this slave. So we should severe the connection, as opposed to marking it temporarily
  70. // off line, which still keeps the underlying channel open.
  71. c.disconnect(d);
  72. LOGGER.warning(Messages.ResponseTimeMonitor_MarkedOffline(c.getName()));
  73. }
  74. return d;
  75. }
  76. public String getDisplayName() {
  77. return Messages.ResponseTimeMonitor_DisplayName();
  78. }
  79. @Override
  80. public NodeMonitor newInstance(StaplerRequest req, JSONObject formData) throws FormException {
  81. return new ResponseTimeMonitor();
  82. }
  83. };
  84. /**
  85. * Immutable representation of the monitoring data.
  86. */
  87. @ExportedBean
  88. public static final class Data extends OfflineCause {
  89. /**
  90. * Record of the past 5 times. -1 if time out. Otherwise in milliseconds.
  91. * Old ones first.
  92. */
  93. private final long[] past5;
  94. private Data(Data old, long newDataPoint) {
  95. if(old==null)
  96. past5 = new long[] {newDataPoint};
  97. else {
  98. past5 = new long[Math.min(5,old.past5.length+1)];
  99. int copyLen = past5.length - 1;
  100. System.arraycopy(old.past5, old.past5.length-copyLen, this.past5, 0, copyLen);
  101. past5[past5.length-1] = newDataPoint;
  102. }
  103. }
  104. /**
  105. * Computes the recurrence of the time out
  106. */
  107. private int failureCount() {
  108. int cnt=0;
  109. for(int i=past5.length-1; i>=0 && past5[i]<0; i--, cnt++)
  110. ;
  111. return cnt;
  112. }
  113. /**
  114. * Computes the average response time, by taking the time out into account.
  115. */
  116. @Exported
  117. public long getAverage() {
  118. long total=0;
  119. for (long l : past5) {
  120. if(l<0) total += TIMEOUT;
  121. else total += l;
  122. }
  123. return total/past5.length;
  124. }
  125. public boolean hasTooManyTimeouts() {
  126. return failureCount()>=5;
  127. }
  128. /**
  129. * HTML rendering of the data
  130. */
  131. @Override
  132. public String toString() {
  133. // StringBuilder buf = new StringBuilder();
  134. // for (long l : past5) {
  135. // if(buf.length()>0) buf.append(',');
  136. // buf.append(l);
  137. // }
  138. // return buf.toString();
  139. int fc = failureCount();
  140. if(fc>0)
  141. return Util.wrapToErrorSpan(Messages.ResponseTimeMonitor_TimeOut(fc));
  142. return getAverage()+"ms";
  143. }
  144. }
  145. private static class NoopTask implements Callable<String,RuntimeException> {
  146. public String call() {
  147. return null;
  148. }
  149. private static final long serialVersionUID = 1L;
  150. }
  151. /**
  152. * Time out interval in milliseconds.
  153. */
  154. private static final long TIMEOUT = 5000;
  155. private static final Logger LOGGER = Logger.getLogger(ResponseTimeMonitor.class.getName());
  156. }