1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package goldengate.common.cpu;
22
23 import goldengate.common.database.DbAdmin;
24 import goldengate.common.logging.GgInternalLogger;
25 import goldengate.common.logging.GgInternalLoggerFactory;
26
27 import java.util.LinkedList;
28 import java.util.Random;
29 import java.util.concurrent.ScheduledThreadPoolExecutor;
30 import java.util.concurrent.TimeUnit;
31
32 import org.jboss.netty.handler.traffic.GlobalTrafficShapingHandler;
33
34
35
36
37
38
39
40 public abstract class GgConstraintLimitHandler implements Runnable {
41
42
43
44 private static final GgInternalLogger logger = GgInternalLoggerFactory
45 .getLogger(GgConstraintLimitHandler.class);
46
47 private static final String NOALERT = "noAlert";
48 public String lastAlert = NOALERT;
49 private boolean constraintInactive = true;
50 private boolean useCpuLimits = false;
51
52 private final Random random = new Random();
53 private CpuManagementInterface cpuManagement;
54 private double cpuLimit = 0.8;
55 private int channelLimit = 1000;
56 private boolean isServer = false;
57 private double lastLA = 0.0;
58 private long lastTime;
59
60
61 private long WAITFORNETOP = 1000;
62 private long TIMEOUTCON = 10000;
63 private double highCpuLimit = 0.8;
64 private double lowCpuLimit = 0.5;
65 private double percentageDecreaseRatio = 0.25;
66 private long delay = 1000;
67 private long limitLowBandwidth = 10000;
68 private GlobalTrafficShapingHandler handler;
69 private ScheduledThreadPoolExecutor executor = null;
70 private static class CurLimits {
71 long read;
72 long write;
73 private CurLimits(long read, long write) {
74 this.read = read;
75 this.write = write;
76 }
77 }
78 private final LinkedList<CurLimits> curLimits = new LinkedList<GgConstraintLimitHandler.CurLimits>();
79 private int nbSinceLastDecrease = 0;
80 private static final int payload = 5;
81
82
83
84 public GgConstraintLimitHandler() {
85
86 if (cpuManagement == null)
87 cpuManagement = new CpuManagementNoInfo();
88 }
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 public GgConstraintLimitHandler(long WAITFORNETOP2, long TIMEOUTCON2, boolean useJdkCpuLimit,
104 double lowcpuLimit, double highcpuLimit, double percentageDecrease,
105 GlobalTrafficShapingHandler handler, long delay, long limitLowBandwidth) {
106 this(WAITFORNETOP2, TIMEOUTCON2,
107 true, useJdkCpuLimit, 0, 0,
108 lowcpuLimit, highcpuLimit, percentageDecrease,
109 handler, delay, limitLowBandwidth);
110 }
111
112
113
114
115
116
117
118
119 public GgConstraintLimitHandler(long WAITFORNETOP2, long TIMEOUTCON2, boolean useCpuLimit,
120 boolean useJdKCpuLimit, double cpulimit, int channellimit) {
121 this(WAITFORNETOP2, TIMEOUTCON2, useCpuLimit, useJdKCpuLimit, cpulimit, channellimit,
122 0,0,0.01,null,1000000,4096);
123 }
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140 public GgConstraintLimitHandler(long WAITFORNETOP2, long TIMEOUTCON2,
141 boolean useCpuLimit,
142 boolean useJdKCpuLimit, double cpulimit, int channellimit,
143 double lowcpuLimit, double highcpuLimit, double percentageDecrease,
144 GlobalTrafficShapingHandler handler, long delay, long limitLowBandwidth) {
145 useCpuLimits = useCpuLimit;
146 WAITFORNETOP = WAITFORNETOP2;
147 TIMEOUTCON = TIMEOUTCON2;
148 lowCpuLimit = lowcpuLimit;
149 highCpuLimit = highcpuLimit;
150 this.limitLowBandwidth = limitLowBandwidth;
151 if (this.limitLowBandwidth < 4096) {
152 this.limitLowBandwidth = 4096;
153 }
154 this.delay = delay;
155 if (lowCpuLimit <= 0) {
156 lowCpuLimit = highCpuLimit / 2;
157 }
158 percentageDecreaseRatio = percentageDecrease;
159 if (percentageDecreaseRatio <=0) {
160 percentageDecreaseRatio = 0.25;
161 } else if (percentageDecreaseRatio >= 1) {
162 percentageDecreaseRatio /= 100;
163 }
164 if (delay < WAITFORNETOP >> 1) {
165 this.delay = WAITFORNETOP;
166 }
167 this.handler = handler;
168 if (useCpuLimits || highCpuLimit > 0) {
169 constraintInactive = false;
170 if (useJdKCpuLimit) {
171 try {
172 cpuManagement = new CpuManagement();
173 } catch (IllegalArgumentException e) {
174 cpuManagement = new CpuManagementNoInfo();
175 }
176 } else {
177 cpuManagement = new CpuManagementSysmon();
178 }
179 } else {
180
181 constraintInactive = true;
182 cpuManagement = new CpuManagementNoInfo();
183 }
184 cpuLimit = cpulimit;
185 channelLimit = channellimit;
186 lastTime = System.currentTimeMillis();
187 if (this.handler != null && (!constraintInactive)) {
188 executor = new ScheduledThreadPoolExecutor(1);
189 executor.scheduleWithFixedDelay(this, this.delay, this.delay, TimeUnit.MILLISECONDS);
190 }
191 }
192
193
194
195 public void release() {
196 if (this.executor != null) {
197 this.executor.shutdownNow();
198 }
199 }
200
201
202
203
204 public void setServer(boolean isServer) {
205 this.isServer = isServer;
206 }
207 private double getLastLA() {
208 long newTime = System.currentTimeMillis();
209
210 if ((newTime - lastTime) < (WAITFORNETOP >> 1)) {
211
212 if (lastLA <= cpuLimit) {
213
214 return lastLA;
215 }
216 }
217 lastTime = newTime;
218 lastLA = cpuManagement.getLoadAverage();
219 return lastLA;
220 }
221
222
223
224
225 public boolean checkConstraints() {
226 if (! isServer)
227 return false;
228 if ((useCpuLimits) && cpuLimit < 1 && cpuLimit > 0) {
229 getLastLA();
230 if (lastLA <= cpuLimit) {
231 lastAlert = NOALERT;
232 return false;
233 }
234 if (lastLA > cpuLimit) {
235 lastAlert = "CPU Constraint: "+lastLA+" > "+cpuLimit;
236 logger.debug(lastAlert);
237 return true;
238 }
239 }
240 if (channelLimit > 0) {
241 int nb = DbAdmin.getNbConnection()-DbAdmin.nbHttpSession;
242 if (channelLimit < nb) {
243 lastAlert = "Network Constraint: "+nb+" > "+channelLimit;
244 logger.debug(lastAlert);
245 return true;
246 }
247 nb = getNumberLocalChannel();
248 if (channelLimit < nb) {
249 lastAlert = "LocalNetwork Constraint: "+nb+" > "+channelLimit;
250 logger.debug(lastAlert);
251 return true;
252 }
253 }
254 lastAlert = NOALERT;
255 return false;
256 }
257
258
259
260
261 protected abstract int getNumberLocalChannel();
262
263
264
265
266
267
268
269 public boolean checkConstraintsSleep(int step) {
270 if (! isServer)
271 return false;
272 long delay = WAITFORNETOP >> 1;
273 if ((useCpuLimits) && cpuLimit < 1 && cpuLimit > 0) {
274 long newTime = System.currentTimeMillis();
275
276 if ((newTime - lastTime) < delay) {
277
278 if (lastLA > cpuLimit) {
279 double sleep = lastLA * delay * (step+1) * random.nextFloat();
280 long shorttime = (((long) sleep)/10)*10;
281 try {
282 Thread.sleep(shorttime);
283 } catch (InterruptedException e) {
284 }
285 } else {
286
287 lastAlert = NOALERT;
288 return false;
289 }
290 }
291 }
292 if (checkConstraints()) {
293 delay = getSleepTime()*(step+1);
294 try {
295 Thread.sleep(delay);
296 } catch (InterruptedException e) {
297 }
298 return true;
299 } else {
300 lastAlert = NOALERT;
301 return false;
302 }
303 }
304
305
306
307
308
309 public long getSleepTime() {
310 return (((long) (TIMEOUTCON*random.nextFloat())+5000)/10)*10;
311 }
312
313
314
315 public double getCpuLimit() {
316 return cpuLimit;
317 }
318
319
320
321
322 public void setCpuLimit(double cpuLimit) {
323 this.cpuLimit = cpuLimit;
324 }
325
326
327
328
329 public int getChannelLimit() {
330 return channelLimit;
331 }
332
333
334
335
336 public void setChannelLimit(int channelLimit) {
337 this.channelLimit = channelLimit;
338 }
339
340
341
342
343 protected abstract long getReadLimit();
344
345
346
347
348 protected abstract long getWriteLimit();
349
350
351
352
353 public void setHandler(GlobalTrafficShapingHandler handler) {
354 this.handler = handler;
355 if ((!constraintInactive) && this.handler != null && highCpuLimit > 0) {
356 if (executor != null) {
357 executor.shutdownNow();
358 }
359 logger.debug("Activate Throttle bandwidth according to CPU usage");
360 executor = new ScheduledThreadPoolExecutor(1);
361 executor.scheduleWithFixedDelay(this, this.delay, this.delay, TimeUnit.MILLISECONDS);
362 } else {
363 if (executor != null) {
364 executor.shutdownNow();
365 executor = null;
366 }
367 }
368 }
369
370
371
372 public void run() {
373 if (constraintInactive)
374 return;
375 double curLA = getLastLA();
376 if (curLA > highCpuLimit) {
377 CurLimits curlimit = null;
378 if (curLimits.isEmpty()) {
379
380 curlimit = new CurLimits(getReadLimit(), getWriteLimit());
381 if (curlimit.read == 0) {
382
383 curlimit.read = handler.getTrafficCounter().getLastReadThroughput();
384 if (curlimit.read < limitLowBandwidth){
385 curlimit.read = 0;
386 }
387 }
388 if (curlimit.write == 0) {
389
390 curlimit.write = handler.getTrafficCounter().getLastWriteThroughput();
391 if (curlimit.write < limitLowBandwidth){
392 curlimit.write = 0;
393 }
394 }
395 } else {
396 curlimit = curLimits.getLast();
397 }
398 long newread = (long) (curlimit.read*(1-percentageDecreaseRatio));
399 if (newread < limitLowBandwidth) {
400 newread = limitLowBandwidth;
401 }
402 long newwrite = (long)(curlimit.write*(1-percentageDecreaseRatio));
403 if (newwrite < limitLowBandwidth) {
404 newwrite = limitLowBandwidth;
405 }
406 CurLimits newlimit = new CurLimits(newread, newwrite);
407 if (curLimits.isEmpty() || curlimit.read != newread || curlimit.write != newwrite) {
408
409 curLimits.add(newlimit);
410 logger.debug("Set new low limit since CPU = "+curLA+" "+newwrite+":"+newread);
411 handler.configure(newlimit.write, newlimit.read);
412 nbSinceLastDecrease += payload;
413 }
414 } else if (curLA < lowCpuLimit) {
415 if (curLimits.isEmpty()) {
416
417 return;
418 }
419 if (nbSinceLastDecrease > 0) {
420 nbSinceLastDecrease--;
421
422 return;
423 }
424 nbSinceLastDecrease = 0;
425 curLimits.pollLast();
426 CurLimits newlimit = null;
427 if (curLimits.isEmpty()) {
428
429 long newread = getReadLimit();
430 long newwrite = getWriteLimit();
431 logger.debug("restore limit since CPU = "+curLA+" "+newwrite+":"+newread);
432 handler.configure(newwrite, newread);
433 } else {
434
435 newlimit = curLimits.getLast();
436 long newread = newlimit.read;
437 long newwrite = newlimit.write;
438 logger.debug("Set new upper limit since CPU = "+curLA+" "+newwrite+":"+newread);
439 handler.configure(newwrite, newread);
440
441 nbSinceLastDecrease = payload;
442 }
443 }
444 }
445 }