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