CGI speedup strategies
Phillip Lenhardt
philen at earn.aa.ans.net
Thu Sep 2 16:11:22 EDT 1999
Fredrik Lundh wrote:
>Ian Clarke wrote:
>> Has anyone tried actually implementing a Python webserver which just
>> starts a new thread and runs a piece of Python code in it every time
>> someone makes a HTTP request? This server could then cache any objects
>> (such as files, or SQL connections) in memory to be used by any Python
>> scripts (this would obviously require all code to be thread-safe).
>
>http://www.nightmare.com/medusa (non-threaded, ultra-fast)
>http://www.zope.org (threaded, based on medusa)
I looked at both of these a while back, and decided I didn't like them
because they didn't have the same sort of interface that SocketServer
does. So I wrote one that (sort of) has the same interface, but it is
unfortunately not complete:
"""Asynchronus socket server classes.
by Phillip Lenhardt <philen at monkey.org>.
This module implements a simple (and overidable) asynchronus tcp
socket server. It is based partially on the interface to the
SocketServer module.
Currenltly, BaseAsyncTCPSocketServer and BaseAsyncUnixStreamSocketServer
should work. The udp and unix datagram servers need work.
This implementation of asyncronus socket servers (currently) requires
your version of python to have both the socket and select modules. I
think there may be a way to code around select for platforms where
it is unavailable.
To process data read in from a BaseSocketHandler-derived class, you
must define the processRead() method. It is probably best to
override serverServe()
See the end of this file for an example of how to use this module.
"""
__version__ = '0.1'
import socket
import errno
import select
import sys
class BaseAsyncSocketServer:
"""Base asynchronus socket server class.
Defaults to TCP.
"""
addressFamily = socket.AF_INET
socketType = socket.SOCK_STREAM
requestQueueSize = 5
def __init__(self,address,port,handler):
"""Extend, do not override"""
self.address = address
self.port = port
self.handler = handler
self.handlers = {}
self.socket = socket.socket(self.addressFamily,self.socketType)
self.serverSetBlocking()
self.serverSetSockOpt()
self.serverBind()
self.serverListen()
def serverSetBlocking(self):
"""May be overridden"""
self.socket.setblocking(0)
def serverSetSockOpt(self):
"""May be overridden"""
try: self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
except: print 'Failed to set socket option SO_REUSEADDR'
def serverBind(self):
"""May be overridden"""
self.socket.bind((self.address,self.port))
def serverListen(self):
"""May be overridden"""
self.socket.listen(self.requestQueueSize)
def serverServe(self):
"""May be overridden"""
while 1:
self.serverAccept()
self.serverPoll()
def serverAccept(self):
"""May be overridden"""
try:
self.handlers[self.handler(self,self.socket.accept())] = 1
except socket.error, why:
if why[0] == errno.EWOULDBLOCK: pass
else: raise socket.error, why
def serverPoll(self):
"""May be overridden"""
reads = self.handlers.keys()
writes = self.handlers.keys()
excepts = []
(reads,writes,excepts) = select.select(reads,writes,excepts,0)
for handler in reads:
try: handler.handleRead()
except: handler.handleError()
for handler in writes:
try: handler.handleWrite()
except: handler.handleError()
def serverRemoveHandle(self,handler):
try: del self.handlers[handler]
except: pass
class BaseAsyncTCPSocketServer(BaseAsyncSocketServer):
"""Base asyncronus TCP socket server class."""
class BaseAsyncUDPSocketServer(BaseAsyncSocketServer):
"""Base asyncronus UDP socket server class."""
socketType = socket.SOCK_DGRAM
#NEED TO BE IN BaseAsyncUDPSocketHandler class: maxPacketSize = 8192
if hasattr(socket,'AF_UNIX'):
class BaseAsyncUnixStreamSocketServer(BaseAsyncTCPSocketServer):
"""Base asyncronus unix stream server."""
addressFamily = socket.AF_UNIX
class BaseAsyncUnixDatagramSocketServer(BaseAsyncUDPSocketServer):
"""Base asyncronus unix datagram server."""
addressFamily = socket.AF_UNIX
class BaseAsyncSocketHandler:
"""Base asynchronus tcp socket handler class.
"""
bufferSize = 1024
def __init__(self,server,(socket,(address,port))):
#print server,socket,address,port
self.server = server
self.socket = socket
self.address = address
self.port = port
self.socket.setblocking(0)
self.outBuffer = ''
self.handleInit()
def fileno(self):
return self.socket.fileno()
def handleInit(self):
"""Override to do initilizations"""
pass
def handleRead(self):
try:
self.data = self.socket.recv(self.bufferSize)
if not self.data: self.handleClose()
else: self.processRead()
except socket.error, why:
if why[0] in [errno.ECONNRESET,errno.ENOTCONN]:
self.handleClose()
else: raise socket.error, why
def handleWrite(self):
if len(self.outBuffer):
try:
numSent = self.socket.send(self.outBuffer[:self.bufferSize])
self.outBuffer = self.outBuffer[numSent:]
except socket.error, why:
if why[0] == EWOULDBLOCK:
pass
else:
raise socket.error, why
def handleError(self):
t, v, tbinfo = sys.exc_info()
try: self_repr = repr(self)
except:
self_repr = '<__repr__ (self) failed for object at %0x>'%id(self)
print 'uncaptured python exception, removing handler %s (%s:%s %s)' \
%(self_repr,t,v,tbinfo)
self.handleClose()
def handleClose(self):
self.server.serverRemoveHandle(self)
self.socket.close()
class BaseAsyncTCPSocketHandler(BaseAsyncSocketHandler):
"""Base asyncronus tcp socket handler."""
# Test Harness1
# echos input
if __name__ == '__main__':
class TestAsyncSocketHandler(BaseAsyncSocketHandler):
def processRead(self):
sys.stdout.write('got: ' + self.data)
self.outBuffer = self.outBuffer + self.data
self.data = ''
server = BaseAsyncSocketServer('',8000,TestAsyncSocketHandler)
server.serverServe()
More information about the Python-list
mailing list