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