2009-02-16 6 views
8

Así que estoy jugando con python sin pilas, escribiendo muy simple servidor web para enseñarme a mí mismo la programación con microthreads/tasklets. Pero ahora a mi problema, cuando ejecuto algo como ab -n 100000 -c 50 http://192.168.0.192/ (100k peticiones, 50 concurrency) en apache bench obtengo algo así como 6k req/s, la segunda vez que lo ejecuto obtengo 5.5k, third time 5k, fourth time, 4.5 k, etc. hasta 100req/so algo así.¿El rendimiento de la red python está disminuyendo a lo largo del tiempo?

El problema desaparece cuando reinicio el script de Python.

Ahora mi pregunta es ¿por qué? ¿Me olvido de eliminar los tasklets? He comprobado el stackless.getruncount() (y parece que siempre devuelve 1, por alguna razón) por lo que no parece que haya tareas agotadas. Intenté llamar a .kill() en todas las tareas realizadas, no ayudaba. Simplemente no puedo entender esto.

import socket 
import select 
import stackless 
import time 

class socket_wrapper(object): 
    def __init__(self, sock, sockets): 
     super(socket_wrapper, self).__init__() 
     self.sock = sock 
     self.fileno = sock.fileno 
     self.sockets_list = sockets 
     self.channel = stackless.channel() 
     self.writable = False 
     self.error = False 

    def remove(self): 
     self.sock.close() 
     self.sockets_list.remove(self) 

    def send(self, data): 
     self.sock.send(data) 

    def push(self, bytes): 
     self.channel.send(self.sock.recv(bytes)) 

def stackless_accept(accept, handler, recv_size=1024, timeout=0): 
    sockets = [accept] 

    while True: 
     read, write, error = select.select(sockets, sockets, sockets, timeout) 

     for sock in read: 
      if sock is accept: 
       # Accept socket and create wrapper 
       sock = socket_wrapper(sock.accept()[0], sockets) 

       # Create tasklett for this connection 
       tasklet = stackless.tasklet(handler) 
       tasklet.setup(sock) 

       # Store socket 
       sockets.append(sock) 

      else: 
       # Send data to handler 
       sock.push(recv_size) 

     # Tag all writable sockets 
     for sock in write: 
      if sock is not accept: 
       sock.writable = True 

     # Tag all faulty sockets 
     for sock in error: 
      if sock is not accept: 
       sock.error = True 
      else: 
       pass # should do something here if the main socket is faulty 

     timeout = 0 if socket else 1 
     stackless.schedule() 

def simple_handler(tsock): 
    data = "" 

    while data[-4:] != "\r\n\r\n": 
     data += tsock.channel.receive() 

    while not tsock.writable and not tsock.error: 
     stackless.schedule() 

    if not tsock.error: 
     tsock.send("HTTP/1.1 200 OK\r\nContent-length: 8\r\n\r\nHi there") 
     tsock.remove() 

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
sock.bind(("192.168.0.192", 8000)) 
sock.listen(5) 

stackless.tasklet(stackless_accept)(sock, simple_handler) 
stackless.run() 

Respuesta

14

Dos cosas.

Primero, haga que el nombre de clase comience con una letra mayúscula. Es más convencional y más fácil de leer.

que es más importante, en la función stackless_accept que acumular un list de Sock objetos, llamados sockets. Esta lista parece crecer infinitamente. Sí, tiene un remove, pero no es siempre invocado. Si el socket obtiene un error, parece que se dejará en la colección para siempre.

Cuestiones relacionadas