1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.executor;
20
21 import com.google.common.collect.Lists;
22 import com.google.common.collect.Maps;
23 import com.google.common.util.concurrent.ThreadFactoryBuilder;
24 import java.io.IOException;
25 import java.io.Writer;
26 import java.lang.management.ThreadInfo;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.concurrent.BlockingQueue;
31 import java.util.concurrent.ConcurrentHashMap;
32 import java.util.concurrent.ConcurrentMap;
33 import java.util.concurrent.LinkedBlockingQueue;
34 import java.util.concurrent.ThreadPoolExecutor;
35 import java.util.concurrent.TimeUnit;
36 import java.util.concurrent.atomic.AtomicLong;
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39 import org.apache.hadoop.hbase.classification.InterfaceAudience;
40 import org.apache.hadoop.hbase.executor.EventHandler.EventHandlerListener;
41 import org.apache.hadoop.hbase.monitoring.ThreadMonitoring;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 @InterfaceAudience.Private
59 public class ExecutorService {
60 private static final Log LOG = LogFactory.getLog(ExecutorService.class);
61
62
63 private final ConcurrentHashMap<String, Executor> executorMap =
64 new ConcurrentHashMap<String, Executor>();
65
66
67 private ConcurrentHashMap<EventType, EventHandlerListener> eventHandlerListeners =
68 new ConcurrentHashMap<EventType, EventHandlerListener>();
69
70
71 private final String servername;
72
73
74
75
76
77 public ExecutorService(final String servername) {
78 super();
79 this.servername = servername;
80 }
81
82
83
84
85
86
87 public void startExecutorService(String name, int maxThreads) {
88 if (this.executorMap.get(name) != null) {
89 throw new RuntimeException("An executor service with the name " + name +
90 " is already running!");
91 }
92 Executor hbes = new Executor(name, maxThreads, this.eventHandlerListeners);
93 if (this.executorMap.putIfAbsent(name, hbes) != null) {
94 throw new RuntimeException("An executor service with the name " + name +
95 " is already running (2)!");
96 }
97 LOG.debug("Starting executor service name=" + name +
98 ", corePoolSize=" + hbes.threadPoolExecutor.getCorePoolSize() +
99 ", maxPoolSize=" + hbes.threadPoolExecutor.getMaximumPoolSize());
100 }
101
102 boolean isExecutorServiceRunning(String name) {
103 return this.executorMap.containsKey(name);
104 }
105
106 public void shutdown() {
107 for(Entry<String, Executor> entry: this.executorMap.entrySet()) {
108 List<Runnable> wasRunning =
109 entry.getValue().threadPoolExecutor.shutdownNow();
110 if (!wasRunning.isEmpty()) {
111 LOG.info(entry.getValue() + " had " + wasRunning + " on shutdown");
112 }
113 }
114 this.executorMap.clear();
115 }
116
117 Executor getExecutor(final ExecutorType type) {
118 return getExecutor(type.getExecutorName(this.servername));
119 }
120
121 Executor getExecutor(String name) {
122 Executor executor = this.executorMap.get(name);
123 return executor;
124 }
125
126 public ThreadPoolExecutor getExecutorThreadPool(final ExecutorType type) {
127 return getExecutor(type).getThreadPoolExecutor();
128 }
129
130 public void startExecutorService(final ExecutorType type, final int maxThreads) {
131 String name = type.getExecutorName(this.servername);
132 if (isExecutorServiceRunning(name)) {
133 LOG.debug("Executor service " + toString() + " already running on " +
134 this.servername);
135 return;
136 }
137 startExecutorService(name, maxThreads);
138 }
139
140 public void submit(final EventHandler eh) {
141 Executor executor = getExecutor(eh.getEventType().getExecutorServiceType());
142 if (executor == null) {
143
144
145
146 LOG.error("Cannot submit [" + eh + "] because the executor is missing." +
147 " Is this process shutting down?");
148 } else {
149 executor.submit(eh);
150 }
151 }
152
153
154
155
156
157
158
159
160 public void registerListener(final EventType type,
161 final EventHandlerListener listener) {
162 this.eventHandlerListeners.put(type, listener);
163 }
164
165
166
167
168
169
170
171 public EventHandlerListener unregisterListener(final EventType type) {
172 return this.eventHandlerListeners.remove(type);
173 }
174
175 public Map<String, ExecutorStatus> getAllExecutorStatuses() {
176 Map<String, ExecutorStatus> ret = Maps.newHashMap();
177 for (Map.Entry<String, Executor> e : executorMap.entrySet()) {
178 ret.put(e.getKey(), e.getValue().getStatus());
179 }
180 return ret;
181 }
182
183
184
185
186 static class Executor {
187
188 static final long keepAliveTimeInMillis = 1000;
189
190 final TrackingThreadPoolExecutor threadPoolExecutor;
191
192 final BlockingQueue<Runnable> q = new LinkedBlockingQueue<Runnable>();
193 private final String name;
194 private final Map<EventType, EventHandlerListener> eventHandlerListeners;
195 private static final AtomicLong seqids = new AtomicLong(0);
196 private final long id;
197
198 protected Executor(String name, int maxThreads,
199 final Map<EventType, EventHandlerListener> eventHandlerListeners) {
200 this.id = seqids.incrementAndGet();
201 this.name = name;
202 this.eventHandlerListeners = eventHandlerListeners;
203
204 this.threadPoolExecutor = new TrackingThreadPoolExecutor(
205 maxThreads, maxThreads,
206 keepAliveTimeInMillis, TimeUnit.MILLISECONDS, q);
207
208 ThreadFactoryBuilder tfb = new ThreadFactoryBuilder();
209 tfb.setNameFormat(this.name + "-%d");
210 this.threadPoolExecutor.setThreadFactory(tfb.build());
211 }
212
213
214
215
216
217 void submit(final EventHandler event) {
218
219
220 EventHandlerListener listener =
221 this.eventHandlerListeners.get(event.getEventType());
222 if (listener != null) {
223 event.setListener(listener);
224 }
225 this.threadPoolExecutor.execute(event);
226 }
227
228 TrackingThreadPoolExecutor getThreadPoolExecutor() {
229 return threadPoolExecutor;
230 }
231
232 @Override
233 public String toString() {
234 return getClass().getSimpleName() + "-" + id + "-" + name;
235 }
236
237 public ExecutorStatus getStatus() {
238 List<EventHandler> queuedEvents = Lists.newArrayList();
239 for (Runnable r : q) {
240 if (!(r instanceof EventHandler)) {
241 LOG.warn("Non-EventHandler " + r + " queued in " + name);
242 continue;
243 }
244 queuedEvents.add((EventHandler)r);
245 }
246
247 List<RunningEventStatus> running = Lists.newArrayList();
248 for (Map.Entry<Thread, Runnable> e :
249 threadPoolExecutor.getRunningTasks().entrySet()) {
250 Runnable r = e.getValue();
251 if (!(r instanceof EventHandler)) {
252 LOG.warn("Non-EventHandler " + r + " running in " + name);
253 continue;
254 }
255 running.add(new RunningEventStatus(e.getKey(), (EventHandler)r));
256 }
257
258 return new ExecutorStatus(this, queuedEvents, running);
259 }
260 }
261
262
263
264
265
266 static class TrackingThreadPoolExecutor extends ThreadPoolExecutor {
267 private ConcurrentMap<Thread, Runnable> running = Maps.newConcurrentMap();
268
269 public TrackingThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
270 long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
271 super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
272 }
273
274 @Override
275 protected void afterExecute(Runnable r, Throwable t) {
276 super.afterExecute(r, t);
277 running.remove(Thread.currentThread());
278 }
279
280 @Override
281 protected void beforeExecute(Thread t, Runnable r) {
282 Runnable oldPut = running.put(t, r);
283 assert oldPut == null : "inconsistency for thread " + t;
284 super.beforeExecute(t, r);
285 }
286
287
288
289
290
291
292
293 public ConcurrentMap<Thread, Runnable> getRunningTasks() {
294 return running;
295 }
296 }
297
298
299
300
301
302
303
304
305 public static class ExecutorStatus {
306 final Executor executor;
307 final List<EventHandler> queuedEvents;
308 final List<RunningEventStatus> running;
309
310 ExecutorStatus(Executor executor,
311 List<EventHandler> queuedEvents,
312 List<RunningEventStatus> running) {
313 this.executor = executor;
314 this.queuedEvents = queuedEvents;
315 this.running = running;
316 }
317
318 public List<EventHandler> getQueuedEvents() {
319 return queuedEvents;
320 }
321
322 public List<RunningEventStatus> getRunning() {
323 return running;
324 }
325
326
327
328
329
330
331
332
333 public void dumpTo(Writer out, String indent) throws IOException {
334 out.write(indent + "Status for executor: " + executor + "\n");
335 out.write(indent + "=======================================\n");
336 out.write(indent + queuedEvents.size() + " events queued, " +
337 running.size() + " running\n");
338 if (!queuedEvents.isEmpty()) {
339 out.write(indent + "Queued:\n");
340 for (EventHandler e : queuedEvents) {
341 out.write(indent + " " + e + "\n");
342 }
343 out.write("\n");
344 }
345 if (!running.isEmpty()) {
346 out.write(indent + "Running:\n");
347 for (RunningEventStatus stat : running) {
348 out.write(indent + " Running on thread '" +
349 stat.threadInfo.getThreadName() +
350 "': " + stat.event + "\n");
351 out.write(ThreadMonitoring.formatThreadInfo(
352 stat.threadInfo, indent + " "));
353 out.write("\n");
354 }
355 }
356 out.flush();
357 }
358 }
359
360
361
362
363
364 public static class RunningEventStatus {
365 final ThreadInfo threadInfo;
366 final EventHandler event;
367
368 public RunningEventStatus(Thread t, EventHandler event) {
369 this.threadInfo = ThreadMonitoring.getThreadInfo(t);
370 this.event = event;
371 }
372 }
373 }