1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.thrift;
20
21 import java.util.concurrent.ExecutorService;
22 import java.util.concurrent.LinkedBlockingQueue;
23 import java.util.concurrent.RejectedExecutionException;
24 import java.util.concurrent.SynchronousQueue;
25 import java.util.concurrent.TimeUnit;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.hbase.classification.InterfaceAudience;
30 import org.apache.hadoop.conf.Configuration;
31 import org.apache.hadoop.hbase.thrift.CallQueue.Call;
32 import org.apache.hadoop.hbase.util.Threads;
33 import org.apache.thrift.TException;
34 import org.apache.thrift.TProcessor;
35 import org.apache.thrift.protocol.TProtocol;
36 import org.apache.thrift.server.TServer;
37 import org.apache.thrift.server.TThreadPoolServer;
38 import org.apache.thrift.transport.TServerTransport;
39 import org.apache.thrift.transport.TSocket;
40 import org.apache.thrift.transport.TTransport;
41 import org.apache.thrift.transport.TTransportException;
42
43 import com.google.common.util.concurrent.ThreadFactoryBuilder;
44
45
46
47
48 @InterfaceAudience.Private
49 public class TBoundedThreadPoolServer extends TServer {
50
51 private static final String QUEUE_FULL_MSG =
52 "Queue is full, closing connection";
53
54
55
56
57
58 public static final String MIN_WORKER_THREADS_CONF_KEY =
59 "hbase.thrift.minWorkerThreads";
60
61
62
63
64
65
66 public static final int DEFAULT_MIN_WORKER_THREADS = 16;
67
68
69
70
71
72
73 public static final String MAX_WORKER_THREADS_CONF_KEY =
74 "hbase.thrift.maxWorkerThreads";
75
76 public static final int DEFAULT_MAX_WORKER_THREADS = 1000;
77
78
79
80
81
82
83
84 public static final String MAX_QUEUED_REQUESTS_CONF_KEY =
85 "hbase.thrift.maxQueuedRequests";
86
87 public static final int DEFAULT_MAX_QUEUED_REQUESTS = 1000;
88
89
90
91
92
93 public static final String THREAD_KEEP_ALIVE_TIME_SEC_CONF_KEY =
94 "hbase.thrift.threadKeepAliveTimeSec";
95
96 private static final int DEFAULT_THREAD_KEEP_ALIVE_TIME_SEC = 60;
97
98
99
100
101
102 public static final int TIME_TO_WAIT_AFTER_SHUTDOWN_MS = 5000;
103
104 private static final Log LOG = LogFactory.getLog(
105 TBoundedThreadPoolServer.class.getName());
106
107 private final CallQueue callQueue;
108
109 public static class Args extends TThreadPoolServer.Args {
110 int maxQueuedRequests;
111 int threadKeepAliveTimeSec;
112
113 public Args(TServerTransport transport, Configuration conf) {
114 super(transport);
115 minWorkerThreads = conf.getInt(MIN_WORKER_THREADS_CONF_KEY,
116 DEFAULT_MIN_WORKER_THREADS);
117 maxWorkerThreads = conf.getInt(MAX_WORKER_THREADS_CONF_KEY,
118 DEFAULT_MAX_WORKER_THREADS);
119 maxQueuedRequests = conf.getInt(MAX_QUEUED_REQUESTS_CONF_KEY,
120 DEFAULT_MAX_QUEUED_REQUESTS);
121 threadKeepAliveTimeSec = conf.getInt(THREAD_KEEP_ALIVE_TIME_SEC_CONF_KEY,
122 DEFAULT_THREAD_KEEP_ALIVE_TIME_SEC);
123 }
124
125 @Override
126 public String toString() {
127 return "min worker threads=" + minWorkerThreads
128 + ", max worker threads=" + maxWorkerThreads
129 + ", max queued requests=" + maxQueuedRequests;
130 }
131 }
132
133
134 private ExecutorService executorService;
135
136
137 private volatile boolean stopped;
138
139 private Args serverOptions;
140
141 public TBoundedThreadPoolServer(Args options, ThriftMetrics metrics) {
142 super(options);
143
144 if (options.maxQueuedRequests > 0) {
145 this.callQueue = new CallQueue(
146 new LinkedBlockingQueue<Call>(options.maxQueuedRequests), metrics);
147 } else {
148 this.callQueue = new CallQueue(new SynchronousQueue<Call>(), metrics);
149 }
150
151 ThreadFactoryBuilder tfb = new ThreadFactoryBuilder();
152 tfb.setDaemon(true);
153 tfb.setNameFormat("thrift-worker-%d");
154 executorService =
155 new THBaseThreadPoolExecutor(options.minWorkerThreads,
156 options.maxWorkerThreads, options.threadKeepAliveTimeSec,
157 TimeUnit.SECONDS, this.callQueue, tfb.build(), metrics);
158 serverOptions = options;
159 }
160
161 @Override
162 public void serve() {
163 try {
164 serverTransport_.listen();
165 } catch (TTransportException ttx) {
166 LOG.error("Error occurred during listening.", ttx);
167 return;
168 }
169
170 Runtime.getRuntime().addShutdownHook(
171 new Thread(getClass().getSimpleName() + "-shutdown-hook") {
172 @Override
173 public void run() {
174 TBoundedThreadPoolServer.this.stop();
175 }
176 });
177
178 stopped = false;
179 while (!stopped && !Thread.interrupted()) {
180 TTransport client = null;
181 try {
182 client = serverTransport_.accept();
183 } catch (TTransportException ttx) {
184 if (!stopped) {
185 LOG.warn("Transport error when accepting message", ttx);
186 continue;
187 } else {
188
189 break;
190 }
191 }
192
193 ClientConnnection command = new ClientConnnection(client);
194 try {
195 executorService.execute(command);
196 } catch (RejectedExecutionException rex) {
197 if (client.getClass() == TSocket.class) {
198
199 LOG.warn(QUEUE_FULL_MSG + " from " +
200 ((TSocket) client).getSocket().getRemoteSocketAddress());
201 } else {
202 LOG.warn(QUEUE_FULL_MSG, rex);
203 }
204 client.close();
205 }
206 }
207
208 shutdownServer();
209 }
210
211
212
213
214
215
216
217 private void shutdownServer() {
218 executorService.shutdown();
219
220 long msLeftToWait =
221 serverOptions.stopTimeoutUnit.toMillis(serverOptions.stopTimeoutVal);
222 long timeMillis = System.currentTimeMillis();
223
224 LOG.info("Waiting for up to " + msLeftToWait + " ms to finish processing" +
225 " pending requests");
226 boolean interrupted = false;
227 while (msLeftToWait >= 0) {
228 try {
229 executorService.awaitTermination(msLeftToWait, TimeUnit.MILLISECONDS);
230 break;
231 } catch (InterruptedException ix) {
232 long timePassed = System.currentTimeMillis() - timeMillis;
233 msLeftToWait -= timePassed;
234 timeMillis += timePassed;
235 interrupted = true;
236 }
237 }
238
239 LOG.info("Interrupting all worker threads and waiting for "
240 + TIME_TO_WAIT_AFTER_SHUTDOWN_MS + " ms longer");
241
242
243 executorService.shutdownNow();
244 Threads.sleepWithoutInterrupt(TIME_TO_WAIT_AFTER_SHUTDOWN_MS);
245
246
247 if (interrupted) {
248 Thread.currentThread().interrupt();
249 }
250 LOG.info("Thrift server shutdown complete");
251 }
252
253 @Override
254 public void stop() {
255 stopped = true;
256 serverTransport_.interrupt();
257 }
258
259 private class ClientConnnection implements Runnable {
260
261 private TTransport client;
262
263
264
265
266
267
268 private ClientConnnection(TTransport client) {
269 this.client = client;
270 }
271
272
273
274
275 @Override
276 public void run() {
277 TProcessor processor = null;
278 TTransport inputTransport = null;
279 TTransport outputTransport = null;
280 TProtocol inputProtocol = null;
281 TProtocol outputProtocol = null;
282 try {
283 processor = processorFactory_.getProcessor(client);
284 inputTransport = inputTransportFactory_.getTransport(client);
285 outputTransport = outputTransportFactory_.getTransport(client);
286 inputProtocol = inputProtocolFactory_.getProtocol(inputTransport);
287 outputProtocol = outputProtocolFactory_.getProtocol(outputTransport);
288
289
290 while (!stopped && processor.process(inputProtocol, outputProtocol)) {}
291 } catch (TTransportException ttx) {
292
293 } catch (TException tx) {
294 LOG.error("Thrift error occurred during processing of message.", tx);
295 } catch (Exception x) {
296 LOG.error("Error occurred during processing of message.", x);
297 }
298
299 if (inputTransport != null) {
300 inputTransport.close();
301 }
302
303 if (outputTransport != null) {
304 outputTransport.close();
305 }
306 }
307 }
308 }