| Mark Slee | 89e2bb8 | 2007-03-01 00:20:36 +0000 | [diff] [blame] | 1 | # | 
| David Reiss | ea2cba8 | 2009-03-30 21:35:00 +0000 | [diff] [blame] | 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, | 
|  | 13 | # software distributed under the License is distributed on an | 
|  | 14 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | 
|  | 15 | # KIND, either express or implied. See the License for the | 
|  | 16 | # specific language governing permissions and limitations | 
|  | 17 | # under the License. | 
|  | 18 | # | 
| Mark Slee | 89e2bb8 | 2007-03-01 00:20:36 +0000 | [diff] [blame] | 19 |  | 
| David Reiss | b04df76 | 2008-06-10 22:55:38 +0000 | [diff] [blame] | 20 | import logging | 
| Mark Slee | c98d050 | 2006-09-06 02:42:25 +0000 | [diff] [blame] | 21 | import sys | 
| David Reiss | 6653654 | 2008-06-10 22:54:49 +0000 | [diff] [blame] | 22 | import os | 
| Mark Slee | c98d050 | 2006-09-06 02:42:25 +0000 | [diff] [blame] | 23 | import traceback | 
| Mark Slee | 3c4d7fd | 2006-10-02 17:53:20 +0000 | [diff] [blame] | 24 | import threading | 
| Mark Slee | b90aa7c | 2006-10-24 18:49:45 +0000 | [diff] [blame] | 25 | import Queue | 
| Mark Slee | c98d050 | 2006-09-06 02:42:25 +0000 | [diff] [blame] | 26 |  | 
| Mark Slee | c967656 | 2006-09-05 17:34:52 +0000 | [diff] [blame] | 27 | from thrift.Thrift import TProcessor | 
|  | 28 | from thrift.transport import TTransport | 
| Mark Slee | 4ac459f | 2006-10-25 21:39:01 +0000 | [diff] [blame] | 29 | from thrift.protocol import TBinaryProtocol | 
| Mark Slee | c967656 | 2006-09-05 17:34:52 +0000 | [diff] [blame] | 30 |  | 
|  | 31 | class TServer: | 
|  | 32 |  | 
| Mark Slee | 794993d | 2006-09-20 01:56:10 +0000 | [diff] [blame] | 33 | """Base interface for a server, which must have a serve method.""" | 
| Mark Slee | c967656 | 2006-09-05 17:34:52 +0000 | [diff] [blame] | 34 |  | 
| Aditya Agarwal | 5c46819 | 2007-02-06 01:14:33 +0000 | [diff] [blame] | 35 | """ 3 constructors for all servers: | 
|  | 36 | 1) (processor, serverTransport) | 
|  | 37 | 2) (processor, serverTransport, transportFactory, protocolFactory) | 
|  | 38 | 3) (processor, serverTransport, | 
|  | 39 | inputTransportFactory, outputTransportFactory, | 
|  | 40 | inputProtocolFactory, outputProtocolFactory)""" | 
|  | 41 | def __init__(self, *args): | 
| Aditya Agarwal | 5c46819 | 2007-02-06 01:14:33 +0000 | [diff] [blame] | 42 | if (len(args) == 2): | 
|  | 43 | self.__initArgs__(args[0], args[1], | 
|  | 44 | TTransport.TTransportFactoryBase(), | 
|  | 45 | TTransport.TTransportFactoryBase(), | 
|  | 46 | TBinaryProtocol.TBinaryProtocolFactory(), | 
|  | 47 | TBinaryProtocol.TBinaryProtocolFactory()) | 
|  | 48 | elif (len(args) == 4): | 
|  | 49 | self.__initArgs__(args[0], args[1], args[2], args[2], args[3], args[3]) | 
|  | 50 | elif (len(args) == 6): | 
|  | 51 | self.__initArgs__(args[0], args[1], args[2], args[3], args[4], args[5]) | 
|  | 52 |  | 
|  | 53 | def __initArgs__(self, processor, serverTransport, | 
|  | 54 | inputTransportFactory, outputTransportFactory, | 
|  | 55 | inputProtocolFactory, outputProtocolFactory): | 
| Mark Slee | d788b2e | 2006-09-07 01:26:35 +0000 | [diff] [blame] | 56 | self.processor = processor | 
|  | 57 | self.serverTransport = serverTransport | 
| Aditya Agarwal | 5c46819 | 2007-02-06 01:14:33 +0000 | [diff] [blame] | 58 | self.inputTransportFactory = inputTransportFactory | 
|  | 59 | self.outputTransportFactory = outputTransportFactory | 
|  | 60 | self.inputProtocolFactory = inputProtocolFactory | 
|  | 61 | self.outputProtocolFactory = outputProtocolFactory | 
| Mark Slee | c967656 | 2006-09-05 17:34:52 +0000 | [diff] [blame] | 62 |  | 
| Mark Slee | 794993d | 2006-09-20 01:56:10 +0000 | [diff] [blame] | 63 | def serve(self): | 
| Mark Slee | c967656 | 2006-09-05 17:34:52 +0000 | [diff] [blame] | 64 | pass | 
|  | 65 |  | 
|  | 66 | class TSimpleServer(TServer): | 
|  | 67 |  | 
|  | 68 | """Simple single-threaded server that just pumps around one transport.""" | 
|  | 69 |  | 
| Aditya Agarwal | 5c46819 | 2007-02-06 01:14:33 +0000 | [diff] [blame] | 70 | def __init__(self, *args): | 
|  | 71 | TServer.__init__(self, *args) | 
| Mark Slee | c967656 | 2006-09-05 17:34:52 +0000 | [diff] [blame] | 72 |  | 
| Mark Slee | 794993d | 2006-09-20 01:56:10 +0000 | [diff] [blame] | 73 | def serve(self): | 
| Mark Slee | d788b2e | 2006-09-07 01:26:35 +0000 | [diff] [blame] | 74 | self.serverTransport.listen() | 
| Mark Slee | c967656 | 2006-09-05 17:34:52 +0000 | [diff] [blame] | 75 | while True: | 
| Mark Slee | d788b2e | 2006-09-07 01:26:35 +0000 | [diff] [blame] | 76 | client = self.serverTransport.accept() | 
| Aditya Agarwal | 5c46819 | 2007-02-06 01:14:33 +0000 | [diff] [blame] | 77 | itrans = self.inputTransportFactory.getTransport(client) | 
|  | 78 | otrans = self.outputTransportFactory.getTransport(client) | 
|  | 79 | iprot = self.inputProtocolFactory.getProtocol(itrans) | 
| Mark Slee | fb84b2b | 2007-02-20 03:37:28 +0000 | [diff] [blame] | 80 | oprot = self.outputProtocolFactory.getProtocol(otrans) | 
| Mark Slee | c967656 | 2006-09-05 17:34:52 +0000 | [diff] [blame] | 81 | try: | 
|  | 82 | while True: | 
| Mark Slee | 4ac459f | 2006-10-25 21:39:01 +0000 | [diff] [blame] | 83 | self.processor.process(iprot, oprot) | 
| Mark Slee | 4f0fed6 | 2006-10-02 17:50:08 +0000 | [diff] [blame] | 84 | except TTransport.TTransportException, tx: | 
|  | 85 | pass | 
| Mark Slee | c967656 | 2006-09-05 17:34:52 +0000 | [diff] [blame] | 86 | except Exception, x: | 
| David Reiss | b04df76 | 2008-06-10 22:55:38 +0000 | [diff] [blame] | 87 | logging.exception(x) | 
| Mark Slee | d788b2e | 2006-09-07 01:26:35 +0000 | [diff] [blame] | 88 |  | 
| Mark Slee | 4ac459f | 2006-10-25 21:39:01 +0000 | [diff] [blame] | 89 | itrans.close() | 
|  | 90 | otrans.close() | 
| Mark Slee | 4f0fed6 | 2006-10-02 17:50:08 +0000 | [diff] [blame] | 91 |  | 
|  | 92 | class TThreadedServer(TServer): | 
|  | 93 |  | 
|  | 94 | """Threaded server that spawns a new thread per each connection.""" | 
|  | 95 |  | 
| Bryan Duxbury | f2ef59f | 2010-09-02 15:12:06 +0000 | [diff] [blame] | 96 | def __init__(self, *args, **kwargs): | 
| Aditya Agarwal | 5c46819 | 2007-02-06 01:14:33 +0000 | [diff] [blame] | 97 | TServer.__init__(self, *args) | 
| Bryan Duxbury | f2ef59f | 2010-09-02 15:12:06 +0000 | [diff] [blame] | 98 | self.daemon = kwargs.get("daemon", False) | 
| Mark Slee | 4f0fed6 | 2006-10-02 17:50:08 +0000 | [diff] [blame] | 99 |  | 
|  | 100 | def serve(self): | 
|  | 101 | self.serverTransport.listen() | 
|  | 102 | while True: | 
|  | 103 | try: | 
|  | 104 | client = self.serverTransport.accept() | 
|  | 105 | t = threading.Thread(target = self.handle, args=(client,)) | 
| Bryan Duxbury | f2ef59f | 2010-09-02 15:12:06 +0000 | [diff] [blame] | 106 | t.setDaemon(self.daemon) | 
| Mark Slee | 4f0fed6 | 2006-10-02 17:50:08 +0000 | [diff] [blame] | 107 | t.start() | 
| Mark Slee | 5299a95 | 2007-10-05 00:13:24 +0000 | [diff] [blame] | 108 | except KeyboardInterrupt: | 
|  | 109 | raise | 
| Mark Slee | 4f0fed6 | 2006-10-02 17:50:08 +0000 | [diff] [blame] | 110 | except Exception, x: | 
| David Reiss | b04df76 | 2008-06-10 22:55:38 +0000 | [diff] [blame] | 111 | logging.exception(x) | 
| Mark Slee | 4f0fed6 | 2006-10-02 17:50:08 +0000 | [diff] [blame] | 112 |  | 
|  | 113 | def handle(self, client): | 
| Aditya Agarwal | 5c46819 | 2007-02-06 01:14:33 +0000 | [diff] [blame] | 114 | itrans = self.inputTransportFactory.getTransport(client) | 
|  | 115 | otrans = self.outputTransportFactory.getTransport(client) | 
|  | 116 | iprot = self.inputProtocolFactory.getProtocol(itrans) | 
| Mark Slee | fb84b2b | 2007-02-20 03:37:28 +0000 | [diff] [blame] | 117 | oprot = self.outputProtocolFactory.getProtocol(otrans) | 
| Mark Slee | 4f0fed6 | 2006-10-02 17:50:08 +0000 | [diff] [blame] | 118 | try: | 
|  | 119 | while True: | 
| Mark Slee | 4ac459f | 2006-10-25 21:39:01 +0000 | [diff] [blame] | 120 | self.processor.process(iprot, oprot) | 
| Mark Slee | 4f0fed6 | 2006-10-02 17:50:08 +0000 | [diff] [blame] | 121 | except TTransport.TTransportException, tx: | 
|  | 122 | pass | 
|  | 123 | except Exception, x: | 
| David Reiss | b04df76 | 2008-06-10 22:55:38 +0000 | [diff] [blame] | 124 | logging.exception(x) | 
| Mark Slee | b90aa7c | 2006-10-24 18:49:45 +0000 | [diff] [blame] | 125 |  | 
| Mark Slee | 4ac459f | 2006-10-25 21:39:01 +0000 | [diff] [blame] | 126 | itrans.close() | 
|  | 127 | otrans.close() | 
|  | 128 |  | 
| Mark Slee | b90aa7c | 2006-10-24 18:49:45 +0000 | [diff] [blame] | 129 | class TThreadPoolServer(TServer): | 
|  | 130 |  | 
|  | 131 | """Server with a fixed size pool of threads which service requests.""" | 
|  | 132 |  | 
| Bryan Duxbury | f2ef59f | 2010-09-02 15:12:06 +0000 | [diff] [blame] | 133 | def __init__(self, *args, **kwargs): | 
| Aditya Agarwal | 5c46819 | 2007-02-06 01:14:33 +0000 | [diff] [blame] | 134 | TServer.__init__(self, *args) | 
| Mark Slee | b90aa7c | 2006-10-24 18:49:45 +0000 | [diff] [blame] | 135 | self.clients = Queue.Queue() | 
|  | 136 | self.threads = 10 | 
| Bryan Duxbury | f2ef59f | 2010-09-02 15:12:06 +0000 | [diff] [blame] | 137 | self.daemon = kwargs.get("daemon", False) | 
| Mark Slee | b90aa7c | 2006-10-24 18:49:45 +0000 | [diff] [blame] | 138 |  | 
| Mark Slee | 4ce787f | 2006-10-24 18:54:06 +0000 | [diff] [blame] | 139 | def setNumThreads(self, num): | 
| Mark Slee | b90aa7c | 2006-10-24 18:49:45 +0000 | [diff] [blame] | 140 | """Set the number of worker threads that should be created""" | 
|  | 141 | self.threads = num | 
|  | 142 |  | 
|  | 143 | def serveThread(self): | 
|  | 144 | """Loop around getting clients from the shared queue and process them.""" | 
|  | 145 | while True: | 
|  | 146 | try: | 
| Mark Slee | 9a695ba | 2006-10-24 18:55:36 +0000 | [diff] [blame] | 147 | client = self.clients.get() | 
| Mark Slee | b90aa7c | 2006-10-24 18:49:45 +0000 | [diff] [blame] | 148 | self.serveClient(client) | 
|  | 149 | except Exception, x: | 
| David Reiss | b04df76 | 2008-06-10 22:55:38 +0000 | [diff] [blame] | 150 | logging.exception(x) | 
| David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame] | 151 |  | 
| Mark Slee | b90aa7c | 2006-10-24 18:49:45 +0000 | [diff] [blame] | 152 | def serveClient(self, client): | 
|  | 153 | """Process input/output from a client for as long as possible""" | 
| Aditya Agarwal | 5c46819 | 2007-02-06 01:14:33 +0000 | [diff] [blame] | 154 | itrans = self.inputTransportFactory.getTransport(client) | 
|  | 155 | otrans = self.outputTransportFactory.getTransport(client) | 
|  | 156 | iprot = self.inputProtocolFactory.getProtocol(itrans) | 
| Mark Slee | 04342d8 | 2007-02-20 03:41:35 +0000 | [diff] [blame] | 157 | oprot = self.outputProtocolFactory.getProtocol(otrans) | 
| Mark Slee | b90aa7c | 2006-10-24 18:49:45 +0000 | [diff] [blame] | 158 | try: | 
|  | 159 | while True: | 
| Mark Slee | 4ac459f | 2006-10-25 21:39:01 +0000 | [diff] [blame] | 160 | self.processor.process(iprot, oprot) | 
| Mark Slee | b90aa7c | 2006-10-24 18:49:45 +0000 | [diff] [blame] | 161 | except TTransport.TTransportException, tx: | 
|  | 162 | pass | 
|  | 163 | except Exception, x: | 
| David Reiss | b04df76 | 2008-06-10 22:55:38 +0000 | [diff] [blame] | 164 | logging.exception(x) | 
| Mark Slee | b90aa7c | 2006-10-24 18:49:45 +0000 | [diff] [blame] | 165 |  | 
| Mark Slee | 4ac459f | 2006-10-25 21:39:01 +0000 | [diff] [blame] | 166 | itrans.close() | 
|  | 167 | otrans.close() | 
|  | 168 |  | 
| Mark Slee | b90aa7c | 2006-10-24 18:49:45 +0000 | [diff] [blame] | 169 | def serve(self): | 
|  | 170 | """Start a fixed number of worker threads and put client into a queue""" | 
|  | 171 | for i in range(self.threads): | 
|  | 172 | try: | 
|  | 173 | t = threading.Thread(target = self.serveThread) | 
| Bryan Duxbury | f2ef59f | 2010-09-02 15:12:06 +0000 | [diff] [blame] | 174 | t.setDaemon(self.daemon) | 
| Mark Slee | b90aa7c | 2006-10-24 18:49:45 +0000 | [diff] [blame] | 175 | t.start() | 
|  | 176 | except Exception, x: | 
| David Reiss | b04df76 | 2008-06-10 22:55:38 +0000 | [diff] [blame] | 177 | logging.exception(x) | 
| David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame] | 178 |  | 
| Mark Slee | b90aa7c | 2006-10-24 18:49:45 +0000 | [diff] [blame] | 179 | # Pump the socket for clients | 
|  | 180 | self.serverTransport.listen() | 
|  | 181 | while True: | 
|  | 182 | try: | 
|  | 183 | client = self.serverTransport.accept() | 
|  | 184 | self.clients.put(client) | 
|  | 185 | except Exception, x: | 
| David Reiss | b04df76 | 2008-06-10 22:55:38 +0000 | [diff] [blame] | 186 | logging.exception(x) | 
| David Reiss | 6653654 | 2008-06-10 22:54:49 +0000 | [diff] [blame] | 187 |  | 
|  | 188 |  | 
| David Reiss | 6653654 | 2008-06-10 22:54:49 +0000 | [diff] [blame] | 189 | class TForkingServer(TServer): | 
|  | 190 |  | 
|  | 191 | """A Thrift server that forks a new process for each request""" | 
|  | 192 | """ | 
|  | 193 | This is more scalable than the threaded server as it does not cause | 
|  | 194 | GIL contention. | 
|  | 195 |  | 
|  | 196 | Note that this has different semantics from the threading server. | 
|  | 197 | Specifically, updates to shared variables will no longer be shared. | 
|  | 198 | It will also not work on windows. | 
|  | 199 |  | 
|  | 200 | This code is heavily inspired by SocketServer.ForkingMixIn in the | 
|  | 201 | Python stdlib. | 
|  | 202 | """ | 
|  | 203 |  | 
|  | 204 | def __init__(self, *args): | 
|  | 205 | TServer.__init__(self, *args) | 
|  | 206 | self.children = [] | 
|  | 207 |  | 
|  | 208 | def serve(self): | 
| David Reiss | bcaa2ad | 2008-06-10 22:55:26 +0000 | [diff] [blame] | 209 | def try_close(file): | 
|  | 210 | try: | 
|  | 211 | file.close() | 
|  | 212 | except IOError, e: | 
| David Reiss | b04df76 | 2008-06-10 22:55:38 +0000 | [diff] [blame] | 213 | logging.warning(e, exc_info=True) | 
| David Reiss | bcaa2ad | 2008-06-10 22:55:26 +0000 | [diff] [blame] | 214 |  | 
|  | 215 |  | 
| David Reiss | 6653654 | 2008-06-10 22:54:49 +0000 | [diff] [blame] | 216 | self.serverTransport.listen() | 
|  | 217 | while True: | 
|  | 218 | client = self.serverTransport.accept() | 
|  | 219 | try: | 
|  | 220 | pid = os.fork() | 
|  | 221 |  | 
|  | 222 | if pid: # parent | 
|  | 223 | # add before collect, otherwise you race w/ waitpid | 
|  | 224 | self.children.append(pid) | 
|  | 225 | self.collect_children() | 
|  | 226 |  | 
| David Reiss | bcaa2ad | 2008-06-10 22:55:26 +0000 | [diff] [blame] | 227 | # Parent must close socket or the connection may not get | 
|  | 228 | # closed promptly | 
|  | 229 | itrans = self.inputTransportFactory.getTransport(client) | 
|  | 230 | otrans = self.outputTransportFactory.getTransport(client) | 
|  | 231 | try_close(itrans) | 
|  | 232 | try_close(otrans) | 
| David Reiss | 6653654 | 2008-06-10 22:54:49 +0000 | [diff] [blame] | 233 | else: | 
|  | 234 | itrans = self.inputTransportFactory.getTransport(client) | 
|  | 235 | otrans = self.outputTransportFactory.getTransport(client) | 
|  | 236 |  | 
|  | 237 | iprot = self.inputProtocolFactory.getProtocol(itrans) | 
|  | 238 | oprot = self.outputProtocolFactory.getProtocol(otrans) | 
|  | 239 |  | 
| David Reiss | bcaa2ad | 2008-06-10 22:55:26 +0000 | [diff] [blame] | 240 | ecode = 0 | 
| David Reiss | 6653654 | 2008-06-10 22:54:49 +0000 | [diff] [blame] | 241 | try: | 
| Kevin Clark | 1e0744d | 2008-06-24 20:46:32 +0000 | [diff] [blame] | 242 | try: | 
|  | 243 | while True: | 
|  | 244 | self.processor.process(iprot, oprot) | 
|  | 245 | except TTransport.TTransportException, tx: | 
|  | 246 | pass | 
|  | 247 | except Exception, e: | 
|  | 248 | logging.exception(e) | 
|  | 249 | ecode = 1 | 
| David Reiss | bcaa2ad | 2008-06-10 22:55:26 +0000 | [diff] [blame] | 250 | finally: | 
|  | 251 | try_close(itrans) | 
|  | 252 | try_close(otrans) | 
| David Reiss | 6653654 | 2008-06-10 22:54:49 +0000 | [diff] [blame] | 253 |  | 
| David Reiss | bcaa2ad | 2008-06-10 22:55:26 +0000 | [diff] [blame] | 254 | os._exit(ecode) | 
| David Reiss | 6653654 | 2008-06-10 22:54:49 +0000 | [diff] [blame] | 255 |  | 
|  | 256 | except TTransport.TTransportException, tx: | 
|  | 257 | pass | 
|  | 258 | except Exception, x: | 
| David Reiss | b04df76 | 2008-06-10 22:55:38 +0000 | [diff] [blame] | 259 | logging.exception(x) | 
| David Reiss | 6653654 | 2008-06-10 22:54:49 +0000 | [diff] [blame] | 260 |  | 
|  | 261 |  | 
| David Reiss | 6653654 | 2008-06-10 22:54:49 +0000 | [diff] [blame] | 262 | def collect_children(self): | 
|  | 263 | while self.children: | 
|  | 264 | try: | 
|  | 265 | pid, status = os.waitpid(0, os.WNOHANG) | 
|  | 266 | except os.error: | 
|  | 267 | pid = None | 
|  | 268 |  | 
|  | 269 | if pid: | 
|  | 270 | self.children.remove(pid) | 
|  | 271 | else: | 
|  | 272 | break | 
|  | 273 |  | 
|  | 274 |  |