1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.server;
20
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.concurrent.Executor;
25 import java.util.concurrent.TimeUnit;
26 import java.util.concurrent.atomic.AtomicBoolean;
27
28 import org.eclipse.jetty.io.EndPoint;
29 import org.eclipse.jetty.util.annotation.ManagedAttribute;
30 import org.eclipse.jetty.util.annotation.ManagedObject;
31 import org.eclipse.jetty.util.annotation.Name;
32 import org.eclipse.jetty.util.component.AbstractLifeCycle;
33 import org.eclipse.jetty.util.log.Log;
34 import org.eclipse.jetty.util.log.Logger;
35 import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
36 import org.eclipse.jetty.util.thread.Scheduler;
37 import org.eclipse.jetty.util.thread.ThreadPool;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64 @ManagedObject ("Monitor for low resource conditions and activate a low resource mode if detected")
65 public class LowResourceMonitor extends AbstractLifeCycle
66 {
67 private static final Logger LOG = Log.getLogger(LowResourceMonitor.class);
68 private final Server _server;
69 private Scheduler _scheduler;
70 private Connector[] _monitoredConnectors;
71 private int _period=1000;
72 private int _maxConnections;
73 private long _maxMemory;
74 private int _lowResourcesIdleTimeout=1000;
75 private int _maxLowResourcesTime=0;
76 private boolean _monitorThreads=true;
77 private final AtomicBoolean _low = new AtomicBoolean();
78 private String _cause;
79 private String _reasons;
80 private long _lowStarted;
81
82 private final Runnable _monitor = new Runnable()
83 {
84 @Override
85 public void run()
86 {
87 if (isRunning())
88 {
89 monitor();
90 _scheduler.schedule(_monitor,_period,TimeUnit.MILLISECONDS);
91 }
92 }
93 };
94
95 public LowResourceMonitor(@Name("server") Server server)
96 {
97 _server=server;
98 }
99
100 @ManagedAttribute("Are the monitored connectors low on resources?")
101 public boolean isLowOnResources()
102 {
103 return _low.get();
104 }
105
106 @ManagedAttribute("The reason(s) the monitored connectors are low on resources")
107 public String getLowResourcesReasons()
108 {
109 return _reasons;
110 }
111
112 @ManagedAttribute("Get the timestamp in ms since epoch that low resources state started")
113 public long getLowResourcesStarted()
114 {
115 return _lowStarted;
116 }
117
118 @ManagedAttribute("The monitored connectors. If null then all server connectors are monitored")
119 public Collection<Connector> getMonitoredConnectors()
120 {
121 if (_monitoredConnectors==null)
122 return Collections.emptyList();
123 return Arrays.asList(_monitoredConnectors);
124 }
125
126
127
128
129 public void setMonitoredConnectors(Collection<Connector> monitoredConnectors)
130 {
131 if (monitoredConnectors==null || monitoredConnectors.size()==0)
132 _monitoredConnectors=null;
133 else
134 _monitoredConnectors = monitoredConnectors.toArray(new Connector[monitoredConnectors.size()]);
135 }
136
137 @ManagedAttribute("The monitor period in ms")
138 public int getPeriod()
139 {
140 return _period;
141 }
142
143
144
145
146 public void setPeriod(int periodMS)
147 {
148 _period = periodMS;
149 }
150
151 @ManagedAttribute("True if low available threads status is monitored")
152 public boolean getMonitorThreads()
153 {
154 return _monitorThreads;
155 }
156
157
158
159
160
161 public void setMonitorThreads(boolean monitorThreads)
162 {
163 _monitorThreads = monitorThreads;
164 }
165
166 @ManagedAttribute("The maximum connections allowed for the monitored connectors before low resource handling is activated")
167 public int getMaxConnections()
168 {
169 return _maxConnections;
170 }
171
172
173
174
175 public void setMaxConnections(int maxConnections)
176 {
177 _maxConnections = maxConnections;
178 }
179
180 @ManagedAttribute("The maximum memory (in bytes) that can be used before low resources is triggered. Memory used is calculated as (totalMemory-freeMemory).")
181 public long getMaxMemory()
182 {
183 return _maxMemory;
184 }
185
186
187
188
189 public void setMaxMemory(long maxMemoryBytes)
190 {
191 _maxMemory = maxMemoryBytes;
192 }
193
194 @ManagedAttribute("The idletimeout in ms to apply to all existing connections when low resources is detected")
195 public int getLowResourcesIdleTimeout()
196 {
197 return _lowResourcesIdleTimeout;
198 }
199
200
201
202
203 public void setLowResourcesIdleTimeout(int lowResourcesIdleTimeoutMS)
204 {
205 _lowResourcesIdleTimeout = lowResourcesIdleTimeoutMS;
206 }
207
208 @ManagedAttribute("The maximum time in ms that low resources condition can persist before lowResourcesIdleTimeout is applied to new connections as well as existing connections")
209 public int getMaxLowResourcesTime()
210 {
211 return _maxLowResourcesTime;
212 }
213
214
215
216
217 public void setMaxLowResourcesTime(int maxLowResourcesTimeMS)
218 {
219 _maxLowResourcesTime = maxLowResourcesTimeMS;
220 }
221
222 @Override
223 protected void doStart() throws Exception
224 {
225 _scheduler = _server.getBean(Scheduler.class);
226
227 if (_scheduler==null)
228 {
229 _scheduler=new LRMScheduler();
230 _scheduler.start();
231 }
232 super.doStart();
233
234 _scheduler.schedule(_monitor,_period,TimeUnit.MILLISECONDS);
235 }
236
237 @Override
238 protected void doStop() throws Exception
239 {
240 if (_scheduler instanceof LRMScheduler)
241 _scheduler.stop();
242 super.doStop();
243 }
244
245 protected Connector[] getMonitoredOrServerConnectors()
246 {
247 if (_monitoredConnectors!=null && _monitoredConnectors.length>0)
248 return _monitoredConnectors;
249 return _server.getConnectors();
250 }
251
252 protected void monitor()
253 {
254 String reasons=null;
255 String cause="";
256 int connections=0;
257
258 ThreadPool serverThreads = _server.getThreadPool();
259 if (_monitorThreads && serverThreads.isLowOnThreads())
260 {
261 reasons=low(reasons,"Server low on threads: "+serverThreads);
262 cause+="S";
263 }
264
265 for(Connector connector : getMonitoredOrServerConnectors())
266 {
267 connections+=connector.getConnectedEndPoints().size();
268
269 Executor executor = connector.getExecutor();
270 if (executor instanceof ThreadPool && executor!=serverThreads)
271 {
272 ThreadPool connectorThreads=(ThreadPool)executor;
273 if (_monitorThreads && connectorThreads.isLowOnThreads())
274 {
275 reasons=low(reasons,"Connector low on threads: "+connectorThreads);
276 cause+="T";
277 }
278 }
279 }
280
281 if (_maxConnections>0 && connections>_maxConnections)
282 {
283 reasons=low(reasons,"Max Connections exceeded: "+connections+">"+_maxConnections);
284 cause+="C";
285 }
286
287 long memory=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
288 if (_maxMemory>0 && memory>_maxMemory)
289 {
290 reasons=low(reasons,"Max memory exceeded: "+memory+">"+_maxMemory);
291 cause+="M";
292 }
293
294 if (reasons!=null)
295 {
296
297 if (!cause.equals(_cause))
298 {
299 LOG.warn("Low Resources: {}",reasons);
300 _cause=cause;
301 }
302
303
304 if (_low.compareAndSet(false,true))
305 {
306 _reasons=reasons;
307 _lowStarted=System.currentTimeMillis();
308 setLowResources();
309 }
310
311
312 if (_maxLowResourcesTime>0 && (System.currentTimeMillis()-_lowStarted)>_maxLowResourcesTime)
313 setLowResources();
314 }
315 else
316 {
317 if (_low.compareAndSet(true,false))
318 {
319 LOG.info("Low Resources cleared");
320 _reasons=null;
321 _lowStarted=0;
322 _cause=null;
323 clearLowResources();
324 }
325 }
326 }
327
328 protected void setLowResources()
329 {
330 for(Connector connector : getMonitoredOrServerConnectors())
331 {
332 for (EndPoint endPoint : connector.getConnectedEndPoints())
333 endPoint.setIdleTimeout(_lowResourcesIdleTimeout);
334 }
335 }
336
337 protected void clearLowResources()
338 {
339 for(Connector connector : getMonitoredOrServerConnectors())
340 {
341 for (EndPoint endPoint : connector.getConnectedEndPoints())
342 endPoint.setIdleTimeout(connector.getIdleTimeout());
343 }
344 }
345
346 private String low(String reasons, String newReason)
347 {
348 if (reasons==null)
349 return newReason;
350 return reasons+", "+newReason;
351 }
352
353
354 private static class LRMScheduler extends ScheduledExecutorScheduler
355 {
356 }
357 }