1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.monitoring;
20
21 import java.io.PrintWriter;
22 import java.lang.ref.WeakReference;
23 import java.lang.reflect.InvocationHandler;
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Proxy;
26 import java.util.ArrayList;
27 import java.util.Iterator;
28 import java.util.List;
29
30 import org.apache.commons.collections.buffer.CircularFifoBuffer;
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.hadoop.conf.Configuration;
34 import org.apache.hadoop.hbase.HBaseConfiguration;
35 import org.apache.hadoop.hbase.classification.InterfaceAudience;
36 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
37 import org.apache.hadoop.hbase.util.Threads;
38
39 import com.google.common.collect.Lists;
40
41
42
43
44
45
46 @InterfaceAudience.Private
47 public class TaskMonitor {
48 private static final Log LOG = LogFactory.getLog(TaskMonitor.class);
49
50 public static final String MAX_TASKS_KEY = "hbase.taskmonitor.max.tasks";
51 public static final int DEFAULT_MAX_TASKS = 1000;
52 public static final String RPC_WARN_TIME_KEY = "hbase.taskmonitor.rpc.warn.time";
53 public static final long DEFAULT_RPC_WARN_TIME = 0;
54 public static final String EXPIRATION_TIME_KEY = "hbase.taskmonitor.expiration.time";
55 public static final long DEFAULT_EXPIRATION_TIME = 60*1000;
56 public static final String MONITOR_INTERVAL_KEY = "hbase.taskmonitor.monitor.interval";
57 public static final long DEFAULT_MONITOR_INTERVAL = 10*1000;
58
59 private static TaskMonitor instance;
60
61 private final int maxTasks;
62 private final long rpcWarnTime;
63 private final long expirationTime;
64 private final CircularFifoBuffer tasks;
65 private final long monitorInterval;
66 private Thread monitorThread;
67
68 TaskMonitor() {
69 this(HBaseConfiguration.create());
70 }
71
72 TaskMonitor(Configuration conf) {
73 maxTasks = conf.getInt(MAX_TASKS_KEY, DEFAULT_MAX_TASKS);
74 expirationTime = conf.getLong(EXPIRATION_TIME_KEY, DEFAULT_EXPIRATION_TIME);
75 rpcWarnTime = conf.getLong(RPC_WARN_TIME_KEY, DEFAULT_RPC_WARN_TIME);
76 tasks = new CircularFifoBuffer(maxTasks);
77 monitorInterval = conf.getLong(MONITOR_INTERVAL_KEY, DEFAULT_MONITOR_INTERVAL);
78 monitorThread = new Thread(new MonitorRunnable());
79 Threads.setDaemonThreadRunning(monitorThread, "Monitor thread for TaskMonitor");
80 }
81
82
83
84
85
86 public static synchronized TaskMonitor get() {
87 if (instance == null) {
88 instance = new TaskMonitor(HBaseConfiguration.create());
89 }
90 return instance;
91 }
92
93 public synchronized MonitoredTask createStatus(String description) {
94 MonitoredTask stat = new MonitoredTaskImpl();
95 stat.setDescription(description);
96 MonitoredTask proxy = (MonitoredTask) Proxy.newProxyInstance(
97 stat.getClass().getClassLoader(),
98 new Class<?>[] { MonitoredTask.class },
99 new PassthroughInvocationHandler<MonitoredTask>(stat));
100 TaskAndWeakRefPair pair = new TaskAndWeakRefPair(stat, proxy);
101 tasks.add(pair);
102 return proxy;
103 }
104
105 public synchronized MonitoredRPCHandler createRPCStatus(String description) {
106 MonitoredRPCHandler stat = new MonitoredRPCHandlerImpl();
107 stat.setDescription(description);
108 MonitoredRPCHandler proxy = (MonitoredRPCHandler) Proxy.newProxyInstance(
109 stat.getClass().getClassLoader(),
110 new Class<?>[] { MonitoredRPCHandler.class },
111 new PassthroughInvocationHandler<MonitoredRPCHandler>(stat));
112 TaskAndWeakRefPair pair = new TaskAndWeakRefPair(stat, proxy);
113 tasks.add(pair);
114 return proxy;
115 }
116
117 private synchronized void warnStuckTasks() {
118 if (rpcWarnTime > 0) {
119 final long now = EnvironmentEdgeManager.currentTime();
120 for (Iterator<TaskAndWeakRefPair> it = tasks.iterator();
121 it.hasNext();) {
122 TaskAndWeakRefPair pair = it.next();
123 MonitoredTask stat = pair.get();
124 if ((stat instanceof MonitoredRPCHandler) &&
125 (stat.getState() == MonitoredTaskImpl.State.RUNNING) &&
126 (now >= stat.getWarnTime() + rpcWarnTime)) {
127 LOG.warn("Task may be stuck: " + stat);
128 stat.setWarnTime(now);
129 }
130 }
131 }
132 }
133
134 private synchronized void purgeExpiredTasks() {
135 for (Iterator<TaskAndWeakRefPair> it = tasks.iterator();
136 it.hasNext();) {
137 TaskAndWeakRefPair pair = it.next();
138 MonitoredTask stat = pair.get();
139
140 if (pair.isDead()) {
141
142
143 if (stat.getState() == MonitoredTaskImpl.State.RUNNING) {
144 LOG.warn("Status " + stat + " appears to have been leaked");
145 stat.cleanup();
146 }
147 }
148
149 if (canPurge(stat)) {
150 it.remove();
151 }
152 }
153 }
154
155
156
157
158
159
160 public synchronized List<MonitoredTask> getTasks() {
161 purgeExpiredTasks();
162 ArrayList<MonitoredTask> ret = Lists.newArrayListWithCapacity(tasks.size());
163 for (Iterator<TaskAndWeakRefPair> it = tasks.iterator();
164 it.hasNext();) {
165 TaskAndWeakRefPair pair = it.next();
166 MonitoredTask t = pair.get();
167 ret.add(t.clone());
168 }
169 return ret;
170 }
171
172 private boolean canPurge(MonitoredTask stat) {
173 long cts = stat.getCompletionTimestamp();
174 return (cts > 0 && EnvironmentEdgeManager.currentTime() - cts > expirationTime);
175 }
176
177 public void dumpAsText(PrintWriter out) {
178 long now = EnvironmentEdgeManager.currentTime();
179
180 List<MonitoredTask> tasks = getTasks();
181 for (MonitoredTask task : tasks) {
182 out.println("Task: " + task.getDescription());
183 out.println("Status: " + task.getState() + ":" + task.getStatus());
184 long running = (now - task.getStartTime())/1000;
185 if (task.getCompletionTimestamp() != -1) {
186 long completed = (now - task.getCompletionTimestamp()) / 1000;
187 out.println("Completed " + completed + "s ago");
188 out.println("Ran for " +
189 (task.getCompletionTimestamp() - task.getStartTime())/1000
190 + "s");
191 } else {
192 out.println("Running for " + running + "s");
193 }
194 out.println();
195 }
196 }
197
198 public synchronized void shutdown() {
199 if (this.monitorThread != null) {
200 monitorThread.interrupt();
201 }
202 }
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222 private static class TaskAndWeakRefPair {
223 private MonitoredTask impl;
224 private WeakReference<MonitoredTask> weakProxy;
225
226 public TaskAndWeakRefPair(MonitoredTask stat,
227 MonitoredTask proxy) {
228 this.impl = stat;
229 this.weakProxy = new WeakReference<MonitoredTask>(proxy);
230 }
231
232 public MonitoredTask get() {
233 return impl;
234 }
235
236 public boolean isDead() {
237 return weakProxy.get() == null;
238 }
239 }
240
241
242
243
244
245 private static class PassthroughInvocationHandler<T> implements InvocationHandler {
246 private T delegatee;
247
248 public PassthroughInvocationHandler(T delegatee) {
249 this.delegatee = delegatee;
250 }
251
252 @Override
253 public Object invoke(Object proxy, Method method, Object[] args)
254 throws Throwable {
255 return method.invoke(delegatee, args);
256 }
257 }
258
259 private class MonitorRunnable implements Runnable {
260 private boolean running = true;
261
262 @Override
263 public void run() {
264 while (running) {
265 try {
266 Thread.sleep(monitorInterval);
267 if (tasks.isFull()) {
268 purgeExpiredTasks();
269 }
270 warnStuckTasks();
271 } catch (InterruptedException e) {
272 running = false;
273 }
274 }
275 }
276 }
277 }