2012-08-06 15 views
7

Antecedentes: soy un principiante cuando se trata de servidores, pero conozco bien la programación en Python.Variable global en el servidor de Python

Estoy intentando configurar un servidor simple utilizando los módulos básicos de Python 2.7 (SimpleHTTServer, CGIHTTPServer, etc.). Este servidor necesita cargar una variable global de solo lectura con varios GB de datos de un archivo cuando se inicia; luego, cuando cada usuario accede a la página, el servidor utiliza los datos grandes para generar algún resultado que luego se le entrega al usuario.

Por el bien de ejemplo, supongamos que tengo un archivo de 4 GB names.txt que contiene todos los posibles nombres propios de Inglés:

Jack 
John 
Allison 
Richard 
... 

Supongamos que mi objetivo es leer toda la lista de nombres en la memoria , y luego elija 1 nombre al azar de esta gran lista de nombres propios. Actualmente puedo usar el módulo nativo CGIHTTPServer de Python para lograr esto. Para empezar, acabo de ejecutar el módulo CGIHTTPServer directamente, mediante la ejecución de un terminal:

python -m CGIHTTPServer 

Entonces, alguien accede www.example-server.net:8000/foo.py y ellos se dan uno de estos nombres al azar. Tengo el siguiente código en foo.py:

#!/usr/bin/env python 

import random 

name_list = list() 
FILE = open('names.txt','r') 
for line in FILE: 
    name = line[:-1] 
    name_list.append(name) 

FILE.close() 
name_to_return = random.choice(name_list) 

print "Content-type: text/html" 
print 
print "<title>Here is your name</title>" 
print "<p>" + name_to_return + "</p>" 

Esto hace lo que yo quiero; sin embargo, es extremadamente ineficiente, porque cada acceso obliga al servidor a volver a leer un archivo de 4 GB.

¿Cómo puedo convertir esto en un proceso eficiente, donde la variable name_list se crea como global inmediatamente cuando se inicia el servidor, y cada acceso solo se lee desde esa variable?

Respuesta

5

Para futuras referencias, si alguna vez alguien tiene el mismo problema: Terminé subclasificando el controlador de solicitudes CGIHTTPServer y la implementación de una nueva función do_POST().Si usted tenía un script CGI trabajar sin variables globales, algo como esto debería empezar:

import CGIHTTPServer 
import random 
import sys 
import cgi 

class MyRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler): 
    global super_important_list 
    super_important_list = range(10) 
    random.shuffle(super_important_list) 

    def do_POST(s):  
     """Respond to a POST request.""" 
     form = cgi.FieldStorage(fp=s.rfile,headers=s.headers,environ={'REQUEST_METHOD':'POST','CONTENT_TYPE':s.headers['Content-Type'],}) 
     s.wfile.write("<html><head><title>Title goes here.</title></head>") 
     s.wfile.write("<body><p>This is a test.</p>") 
     s.wfile.write("<p>You accessed path: %s</p>" % s.path) 
     s.wfile.write("<p>Also, super_important_list is:</p>") 
     s.wfile.write(str(super_important_list)) 
     s.wfile.write("<p>Furthermore, you POSTed the following info: ") 
     for item in form.keys(): 
      s.wfile.write("<p>Item: " + item) 
      s.wfile.write("<p>Value: " + form[item].value) 
     s.wfile.write("</body></html>") 

if __name__ == '__main__': 
    server_address = ('', 8000) 
    httpd = CGIHTTPServer.BaseHTTPServer.HTTPServer(server_address, MyRequestHandler) 
    try: 
     httpd.serve_forever() 
    except KeyboardInterrupt: 
     sys.exit() 

Cada vez que alguien rellena el formulario y realiza un POST, la variable form habrá un objeto de diccionario-como con número- pares de valores que pueden diferir para cada usuario de su sitio, pero la variable global super_important_list será la misma para cada usuario.

Gracias a todos los que respondieron mi pregunta, especialmente a Mike Steder, quien me indicó la dirección correcta.

2

Es posible que desee almacenar los valores de los nombres en un archivo db y almacenar los nombres de acuerdo con la letra con la que comienzan. Luego puede hacer una búsqueda aleatoria de una letra entre a y z y desde allí aleatorizar nuevamente para obtener un nombre aleatorio de su letra de inicio aleatoria.

+1

Gracias por la respuesta. Las bases de datos están en mi lista de cosas para aprender, pero parece una exageración completa solo por esta necesidad. – HerrKaputt

+1

De acuerdo, entonces posiblemente intentaría generar un número aleatorio y solo leer esa línea del archivo. De esta forma, no es necesario pasar por todas las líneas. – edhedges

+0

Eso funcionaría para este simple ejemplo. Sin embargo, no funcionaría para la aplicación que tengo en mente, que realmente requiere leer todo el archivo en la memoria. Obviamente, ese malentendido no es tu culpa. Editaré la pregunta original para reflejar eso. – HerrKaputt

4

CGI funciona al generar un proceso para manejar cada solicitud. Debe ejecutar un proceso de servidor que permanezca en la memoria maneja las solicitudes HTTP.

Puede usar un BaseHTTPServer modificado, solo defina su propia clase de controlador. Cargarías el conjunto de datos una vez en tu código y luego el método do_GET de tu controlador elegiría uno aleatoriamente.

Personalmente, me gustaría ver algo así como CherryPy como una solución simple que es IMO mucho mejor que BaseHTTPServer. Hay un montón de opciones distintas de CherryPy como botella, matraz, trenzado, django, etc. Por supuesto, si necesitas que este servidor esté detrás de otro servidor web, deberás buscar configurar un proxy inverso o ejecutar la aplicación CherryPy as a WSGI.

+0

De hecho, me he reducido a la subclasificación de BaseHTTPServer. ¿Estoy en lo correcto al asumir que tengo que volver a definir TODOS los métodos de BaseHTTPServer (es decir, do_GET, do_POST, etc.)? Es por eso que asumí que algo mejor ya existía. En cuanto a CherryPy, ¿me puede indicar un tutorial "para tontos"? He revisado su página, pero incluso su documentación, que otros describen como "excelente", es demasiado difícil de entender para mí. – HerrKaputt

+0

@HerrKaputt: cada uno de esos métodos corresponde a un método HTTP que * puede * querer * respaldar. Para su caso de uso, creo que solo necesita admitir 'do_GET'. – stderr

+0

¡Muchas gracias, Mike! Si bien tu respuesta no es exactamente lo que estoy buscando, en realidad me impulsó a profundizar más. He omitido de mi pregunta que necesito pasar parámetros a través de métodos POST. Lo que necesito, entonces, es crear una subclase de SimpleHTTPServer y crear mi propia función do_POST(). Me inspiraré en el de CGIHTTPServer. ¡Espero no necesitar hacer más preguntas! – HerrKaputt

2

Cree una prefix tree (a.k.a. trie) una vez y genere una caminata aleatoria cada vez que reciba una consulta.

Eso debería ser bastante eficiente.

+0

Es eficiente. Pero todavía no responde a mi pregunta: ¿cómo configuro esto como un servidor que construye un mundo global de solo lectura que es compartido por todos los usuarios? – HerrKaputt

+0

@HerrKaputt Luego, obviamente, fui engañado por la complejidad de tu ejemplo. ¿Prefiere un ejemplo de "mundo hello" para configurar un servidor http básico? – moooeeeep

+0

tipo de. Un servidor HTTP básico es algo que puedo hacer en Python; sin embargo, no podrá compartir variables entre diferentes usuarios. Por otro lado, puedo crear variables globales en un script de Python, pero no entre diferentes usuarios, porque (como dijo Mike) CGI crea procesos independientes para diferentes usuarios. No sé lo que tengo que hacer para combinar estas dos cosas. – HerrKaputt