PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/java/org/apache/cassandra/thrift/CustomTHsHaServer.java

http://github.com/apache/cassandra
Java | 385 lines | 293 code | 31 blank | 61 comment | 25 complexity | 66d3110fba5a31fc458a82677d00487a MD5 | raw file
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. package org.apache.cassandra.thrift;
  19. import java.io.IOException;
  20. import java.net.InetSocketAddress;
  21. import java.nio.channels.SelectionKey;
  22. import java.nio.channels.Selector;
  23. import java.nio.channels.spi.SelectorProvider;
  24. import java.util.HashSet;
  25. import java.util.Iterator;
  26. import java.util.Set;
  27. import java.util.concurrent.ExecutorService;
  28. import java.util.concurrent.RejectedExecutionException;
  29. import java.util.concurrent.SynchronousQueue;
  30. import java.util.concurrent.TimeUnit;
  31. import org.slf4j.Logger;
  32. import org.slf4j.LoggerFactory;
  33. import org.apache.cassandra.concurrent.JMXEnabledThreadPoolExecutor;
  34. import org.apache.cassandra.concurrent.NamedThreadFactory;
  35. import org.apache.cassandra.config.DatabaseDescriptor;
  36. import org.apache.cassandra.config.EncryptionOptions;
  37. import org.apache.thrift.server.TNonblockingServer;
  38. import org.apache.thrift.server.TServer;
  39. import org.apache.thrift.transport.TNonblockingServerTransport;
  40. import org.apache.thrift.transport.TNonblockingSocket;
  41. import org.apache.thrift.transport.TNonblockingTransport;
  42. import org.apache.thrift.transport.TTransportException;
  43. /**
  44. * This is a interim solution till THRIFT-1167 gets committed...
  45. *
  46. * The idea here is to avoid sticking to one CPU for IO's. For better throughput
  47. * it is spread across multiple threads. Number of selector thread can be the
  48. * number of CPU available.
  49. */
  50. public class CustomTHsHaServer extends TNonblockingServer
  51. {
  52. private static final Logger LOGGER = LoggerFactory.getLogger(CustomTHsHaServer.class.getName());
  53. private final Set<SelectorThread> ioThreads = new HashSet<SelectorThread>();
  54. private volatile boolean stopped = true;
  55. private final ExecutorService invoker;
  56. /**
  57. * All the arguments to Non Blocking Server will apply here. In addition,
  58. * executor pool will be responsible for creating the internal threads which
  59. * will process the data. threads for selection usually are equal to the
  60. * number of cpu's
  61. */
  62. public CustomTHsHaServer(Args args, ExecutorService invoker, int threadCount)
  63. {
  64. super(args);
  65. this.invoker = invoker;
  66. // Create all the Network IO Threads.
  67. for (int i = 0; i < threadCount; ++i)
  68. ioThreads.add(new SelectorThread("Selector-Thread-" + i));
  69. }
  70. /** @inheritDoc */
  71. @Override
  72. public void serve()
  73. {
  74. if (!startListening())
  75. return;
  76. if (!startThreads())
  77. return;
  78. setServing(true);
  79. joinSelector();
  80. invoker.shutdown();
  81. setServing(false);
  82. stopListening();
  83. }
  84. /**
  85. * Save the remote socket as a thead local for future use of client state.
  86. */
  87. protected class Invocation implements Runnable
  88. {
  89. private final FrameBuffer frameBuffer;
  90. private final SelectorThread thread;
  91. public Invocation(final FrameBuffer frameBuffer, SelectorThread thread)
  92. {
  93. this.frameBuffer = frameBuffer;
  94. this.thread = thread;
  95. }
  96. public void run()
  97. {
  98. TNonblockingSocket socket = (TNonblockingSocket) frameBuffer.trans_;
  99. ThriftSessionManager.instance.setCurrentSocket(socket.getSocketChannel().socket().getRemoteSocketAddress());
  100. frameBuffer.invoke();
  101. // this is how we let the same selector thread change the selection type.
  102. thread.requestSelectInterestChange(frameBuffer);
  103. }
  104. }
  105. protected boolean startThreads()
  106. {
  107. stopped = false;
  108. // start all the threads.
  109. for (SelectorThread thread : ioThreads)
  110. thread.start();
  111. return true;
  112. }
  113. @Override
  114. protected void joinSelector()
  115. {
  116. try
  117. {
  118. // wait till all done with stuff's
  119. for (SelectorThread thread : ioThreads)
  120. thread.join();
  121. }
  122. catch (InterruptedException e)
  123. {
  124. LOGGER.error("Interrupted while joining threads!", e);
  125. }
  126. }
  127. /**
  128. * Stop serving and shut everything down.
  129. */
  130. @Override
  131. public void stop()
  132. {
  133. stopListening();
  134. stopped = true;
  135. for (SelectorThread thread : ioThreads)
  136. thread.wakeupSelector();
  137. joinSelector();
  138. }
  139. /**
  140. * IO Threads will perform expensive IO operations...
  141. */
  142. protected class SelectorThread extends Thread
  143. {
  144. private final Selector selector;
  145. private final TNonblockingServerTransport serverTransport;
  146. private final Set<FrameBuffer> selectInterestChanges = new HashSet<FrameBuffer>();
  147. public SelectorThread(String name)
  148. {
  149. super(name);
  150. try
  151. {
  152. this.selector = SelectorProvider.provider().openSelector();
  153. this.serverTransport = (TNonblockingServerTransport) serverTransport_;
  154. this.serverTransport.registerSelector(selector);
  155. }
  156. catch (IOException ex)
  157. {
  158. throw new RuntimeException("Couldnt open the NIO selector", ex);
  159. }
  160. }
  161. public void run()
  162. {
  163. try
  164. {
  165. while (!stopped)
  166. {
  167. select();
  168. }
  169. }
  170. catch (Throwable t)
  171. {
  172. LOGGER.error("Uncaught Exception: ", t);
  173. }
  174. finally
  175. {
  176. try
  177. {
  178. selector.close(); // CASSANDRA-3867
  179. }
  180. catch (IOException e)
  181. {
  182. // ignore this exception.
  183. }
  184. }
  185. }
  186. private void select() throws InterruptedException, IOException
  187. {
  188. // wait for new keys
  189. selector.select();
  190. Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
  191. while (keyIterator.hasNext())
  192. {
  193. SelectionKey key = keyIterator.next();
  194. keyIterator.remove();
  195. try
  196. {
  197. if (!key.isValid())
  198. {
  199. // if invalid cleanup.
  200. cleanupSelectionkey(key);
  201. continue;
  202. }
  203. if (key.isAcceptable())
  204. handleAccept();
  205. if (key.isReadable())
  206. handleRead(key);
  207. else if (key.isWritable())
  208. handleWrite(key);
  209. else
  210. LOGGER.debug("Unexpected state " + key.interestOps());
  211. }
  212. catch (Exception io)
  213. {
  214. // just ignore (?)
  215. cleanupSelectionkey(key);
  216. }
  217. }
  218. // process the changes which are inserted after completion.
  219. processInterestChanges();
  220. }
  221. private void handleAccept()
  222. {
  223. SelectionKey clientKey = null;
  224. TNonblockingTransport client = null;
  225. try
  226. {
  227. // accept the connection
  228. client = (TNonblockingTransport) serverTransport.accept();
  229. clientKey = client.registerSelector(selector, SelectionKey.OP_READ);
  230. // add this key to the map
  231. FrameBuffer frameBuffer = new FrameBuffer(client, clientKey);
  232. clientKey.attach(frameBuffer);
  233. } catch (TTransportException ex)
  234. {
  235. // ignore this might have been handled by the other threads.
  236. // serverTransport.accept() as it returns null as nothing to accept.
  237. return;
  238. }
  239. catch (IOException tte)
  240. {
  241. // something went wrong accepting.
  242. LOGGER.warn("Exception trying to accept!", tte);
  243. tte.printStackTrace();
  244. if (clientKey != null)
  245. cleanupSelectionkey(clientKey);
  246. if (client != null)
  247. client.close();
  248. }
  249. }
  250. private void handleRead(SelectionKey key)
  251. {
  252. FrameBuffer buffer = (FrameBuffer) key.attachment();
  253. if (!buffer.read())
  254. {
  255. cleanupSelectionkey(key);
  256. return;
  257. }
  258. if (buffer.isFrameFullyRead())
  259. {
  260. if (!requestInvoke(buffer, this))
  261. cleanupSelectionkey(key);
  262. }
  263. }
  264. private void handleWrite(SelectionKey key)
  265. {
  266. FrameBuffer buffer = (FrameBuffer) key.attachment();
  267. if (!buffer.write())
  268. cleanupSelectionkey(key);
  269. }
  270. public void requestSelectInterestChange(FrameBuffer frameBuffer)
  271. {
  272. synchronized (selectInterestChanges)
  273. {
  274. selectInterestChanges.add(frameBuffer);
  275. }
  276. // Wake-up the selector, if it's currently blocked.
  277. selector.wakeup();
  278. }
  279. private void processInterestChanges()
  280. {
  281. synchronized (selectInterestChanges)
  282. {
  283. for (FrameBuffer fb : selectInterestChanges)
  284. fb.changeSelectInterests();
  285. selectInterestChanges.clear();
  286. }
  287. }
  288. private void cleanupSelectionkey(SelectionKey key)
  289. {
  290. FrameBuffer buffer = (FrameBuffer) key.attachment();
  291. if (buffer != null)
  292. buffer.close();
  293. // cancel the selection key
  294. key.cancel();
  295. }
  296. public void wakeupSelector()
  297. {
  298. selector.wakeup();
  299. }
  300. }
  301. protected boolean requestInvoke(FrameBuffer frameBuffer, SelectorThread thread)
  302. {
  303. try
  304. {
  305. Runnable invocation = new Invocation(frameBuffer, thread);
  306. invoker.execute(invocation);
  307. return true;
  308. }
  309. catch (RejectedExecutionException rx)
  310. {
  311. LOGGER.warn("ExecutorService rejected execution!", rx);
  312. return false;
  313. }
  314. }
  315. @Override
  316. protected void requestSelectInterestChange(FrameBuffer fb)
  317. {
  318. // Dont change the interest here, this has to be done by the selector
  319. // thread because the method is not synchronized with the rest of the
  320. // selectors threads.
  321. }
  322. public static class Factory implements TServerFactory
  323. {
  324. public TServer buildTServer(Args args)
  325. {
  326. if(!DatabaseDescriptor.getClientEncryptionOptions().internode_encryption.equals(EncryptionOptions.InternodeEncryption.none))
  327. throw new RuntimeException("Client SSL is not supported for non-blocking sockets (hsha). Please remove client ssl from the configuration.");
  328. final InetSocketAddress addr = args.addr;
  329. TNonblockingServerTransport serverTransport;
  330. try
  331. {
  332. serverTransport = new TCustomNonblockingServerSocket(addr, args.keepAlive, args.sendBufferSize, args.recvBufferSize);
  333. }
  334. catch (TTransportException e)
  335. {
  336. throw new RuntimeException(String.format("Unable to create thrift socket to %s:%s", addr.getAddress(), addr.getPort()), e);
  337. }
  338. // This is NIO selector service but the invocation will be Multi-Threaded with the Executor service.
  339. ExecutorService executorService = new JMXEnabledThreadPoolExecutor(DatabaseDescriptor.getRpcMinThreads(),
  340. DatabaseDescriptor.getRpcMaxThreads(),
  341. 60L,
  342. TimeUnit.SECONDS,
  343. new SynchronousQueue<Runnable>(),
  344. new NamedThreadFactory("RPC-Thread"), "RPC-THREAD-POOL");
  345. TNonblockingServer.Args serverArgs = new TNonblockingServer.Args(serverTransport).inputTransportFactory(args.inTransportFactory)
  346. .outputTransportFactory(args.outTransportFactory)
  347. .inputProtocolFactory(args.tProtocolFactory)
  348. .outputProtocolFactory(args.tProtocolFactory)
  349. .processor(args.processor);
  350. // Check for available processors in the system which will be equal to the IO Threads.
  351. return new CustomTHsHaServer(serverArgs, executorService, Runtime.getRuntime().availableProcessors());
  352. }
  353. }
  354. }