1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.ipc;
20
21
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.concurrent.BlockingQueue;
25 import java.util.concurrent.ThreadLocalRandom;
26 import java.util.concurrent.atomic.AtomicInteger;
27
28 import org.apache.commons.logging.Log;
29 import org.apache.commons.logging.LogFactory;
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.hbase.Abortable;
32 import org.apache.hadoop.hbase.HConstants;
33 import org.apache.hadoop.hbase.classification.InterfaceAudience;
34 import org.apache.hadoop.hbase.monitoring.MonitoredRPCHandler;
35 import org.apache.hadoop.util.StringUtils;
36
37 import com.google.common.base.Preconditions;
38 import com.google.common.base.Strings;
39
40
41
42
43
44 @InterfaceAudience.Private
45 public abstract class RpcExecutor {
46 private static final Log LOG = LogFactory.getLog(RpcExecutor.class);
47
48 protected static final int DEFAULT_CALL_QUEUE_SIZE_HARD_LIMIT = 250;
49
50 protected volatile int currentQueueLimit;
51
52 private final AtomicInteger activeHandlerCount = new AtomicInteger(0);
53 private final List<Handler> handlers;
54 private final int handlerCount;
55 private final String name;
56 private final AtomicInteger failedHandlerCount = new AtomicInteger(0);
57
58 private boolean running;
59
60 private Configuration conf = null;
61 private Abortable abortable = null;
62
63 public RpcExecutor(final String name, final int handlerCount) {
64 this.handlers = new ArrayList<Handler>(handlerCount);
65 this.handlerCount = handlerCount;
66 this.name = Strings.nullToEmpty(name);
67 }
68
69 public RpcExecutor(final String name, final int handlerCount, final Configuration conf,
70 final Abortable abortable) {
71 this(name, handlerCount);
72 this.conf = conf;
73 this.abortable = abortable;
74 }
75
76 public void start(final int port) {
77 running = true;
78 startHandlers(port);
79 }
80
81 public void stop() {
82 running = false;
83 for (Thread handler : handlers) {
84 handler.interrupt();
85 }
86 }
87
88 public int getActiveHandlerCount() {
89 return activeHandlerCount.get();
90 }
91
92
93 public abstract int getQueueLength();
94
95
96 public abstract boolean dispatch(final CallRunner callTask) throws InterruptedException;
97
98
99 protected abstract List<BlockingQueue<CallRunner>> getQueues();
100
101 protected void startHandlers(final int port) {
102 List<BlockingQueue<CallRunner>> callQueues = getQueues();
103 startHandlers(null, handlerCount, callQueues, 0, callQueues.size(), port);
104 }
105
106
107
108
109 protected Handler getHandler(final String name, final double handlerFailureThreshhold,
110 final BlockingQueue<CallRunner> q) {
111 return new Handler(name, handlerFailureThreshhold, q);
112 }
113
114
115
116
117 protected void startHandlers(final String nameSuffix, final int numHandlers,
118 final List<BlockingQueue<CallRunner>> callQueues,
119 final int qindex, final int qsize, final int port) {
120 final String threadPrefix = name + Strings.nullToEmpty(nameSuffix);
121 double handlerFailureThreshhold =
122 conf == null ? 1.0 : conf.getDouble(HConstants.REGION_SERVER_HANDLER_ABORT_ON_ERROR_PERCENT,
123 HConstants.DEFAULT_REGION_SERVER_HANDLER_ABORT_ON_ERROR_PERCENT);
124 for (int i = 0; i < numHandlers; i++) {
125 final int index = qindex + (i % qsize);
126 String name = "RpcServer." + threadPrefix + ".handler=" + handlers.size() + ",queue=" +
127 index + ",port=" + port;
128 Handler handler = getHandler(name, handlerFailureThreshhold, callQueues.get(index));
129 handler.start();
130 LOG.debug("Started " + name);
131 handlers.add(handler);
132 }
133 }
134
135
136
137
138 protected class Handler extends Thread {
139
140
141
142 final BlockingQueue<CallRunner> q;
143
144 final double handlerFailureThreshhold;
145
146 Handler(final String name, final double handlerFailureThreshhold,
147 final BlockingQueue<CallRunner> q) {
148 super(name);
149 setDaemon(true);
150 this.q = q;
151 this.handlerFailureThreshhold = handlerFailureThreshhold;
152 }
153
154
155
156
157
158 protected CallRunner getCallRunner() throws InterruptedException {
159 return this.q.take();
160 }
161
162 @Override
163 public void run() {
164 boolean interrupted = false;
165 try {
166 while (running) {
167 try {
168 run(getCallRunner());
169 } catch (InterruptedException e) {
170 interrupted = true;
171 }
172 }
173 } catch (Exception e) {
174 LOG.warn(e);
175 throw e;
176 } finally {
177 if (interrupted) {
178 Thread.currentThread().interrupt();
179 }
180 }
181 }
182
183 private void run(CallRunner cr) {
184 MonitoredRPCHandler status = RpcServer.getStatus();
185 cr.setStatus(status);
186 try {
187 activeHandlerCount.incrementAndGet();
188 cr.run();
189 } catch (Throwable e) {
190 if (e instanceof Error) {
191 int failedCount = failedHandlerCount.incrementAndGet();
192 if (this.handlerFailureThreshhold >= 0 &&
193 failedCount > handlerCount * this.handlerFailureThreshhold) {
194 String message = "Number of failed RpcServer handler runs exceeded threshhold " +
195 this.handlerFailureThreshhold + "; reason: " + StringUtils.stringifyException(e);
196 if (abortable != null) {
197 abortable.abort(message, e);
198 } else {
199 LOG.error("Error but can't abort because abortable is null: " +
200 StringUtils.stringifyException(e));
201 throw e;
202 }
203 } else {
204 LOG.warn("Handler errors " + StringUtils.stringifyException(e));
205 }
206 } else {
207 LOG.warn("Handler exception " + StringUtils.stringifyException(e));
208 }
209 } finally {
210 activeHandlerCount.decrementAndGet();
211 }
212 }
213 }
214
215 public static abstract class QueueBalancer {
216
217
218
219 public abstract int getNextQueue();
220 }
221
222 public static QueueBalancer getBalancer(int queueSize) {
223 Preconditions.checkArgument(queueSize > 0, "Queue size is <= 0, must be at least 1");
224 if (queueSize == 1) {
225 return ONE_QUEUE;
226 } else {
227 return new RandomQueueBalancer(queueSize);
228 }
229 }
230
231
232
233
234 private static QueueBalancer ONE_QUEUE = new QueueBalancer() {
235 @Override
236 public int getNextQueue() {
237 return 0;
238 }
239 };
240
241
242
243
244 private static class RandomQueueBalancer extends QueueBalancer {
245 private final int queueSize;
246
247 public RandomQueueBalancer(int queueSize) {
248 this.queueSize = queueSize;
249 }
250
251 public int getNextQueue() {
252 return ThreadLocalRandom.current().nextInt(queueSize);
253 }
254 }
255
256
257
258
259
260 public void resizeQueues(Configuration conf) {
261 String configKey = RpcScheduler.IPC_SERVER_MAX_CALLQUEUE_LENGTH;
262 if (name != null && name.toLowerCase().contains("priority")) {
263 configKey = RpcScheduler.IPC_SERVER_PRIORITY_MAX_CALLQUEUE_LENGTH;
264 }
265 currentQueueLimit = conf.getInt(configKey, currentQueueLimit);
266 }
267 }