python - how - Tareas de fondo en appengine
ndb put (8)
GAE es una herramienta muy útil para crear aplicaciones web escalables. Pocas de las limitaciones señaladas por muchos son incompatibles con tareas en segundo plano, falta de tareas periódicas y límite estricto de cuánto tarda cada solicitud HTTP, si una solicitud excede ese límite de tiempo, la operación finaliza, lo que hace que las tareas que consumen tiempo sean imposibles. .
¿Cómo ejecutar una tarea en segundo plano?
En GAE, el código se ejecuta solo cuando hay una solicitud HTTP. Hay un límite de tiempo estricto (creo que 10 segundos) sobre cuánto tiempo puede tomar el código. Entonces, si no hay solicitudes, el código no se ejecuta. Uno de los trabajos sugeridos fue usar una caja externa para enviar solicitudes continuamente, creando así una tarea de fondo. Pero para esto necesitamos una caja externa y ahora dependemos de un elemento más. La otra alternativa era enviar 302 una respuesta de redireccionamiento para que el cliente reenviara la solicitud, esto también nos hace dependientes del elemento externo que es el cliente. ¿Qué pasa si esa caja externa es GAE en sí? Todos los que han utilizado un lenguaje funcional que no admite la construcción de bucles en el lenguaje conocen la alternativa, es decir, la recursión es el ciclo de sustitución. Entonces, ¿qué pasa si completamos parte de la computación y hacemos un HTTP GET en la misma url con muy poco tiempo de espera, por ejemplo, 1 segundo? Esto crea un bucle (recursión) en el código php que se ejecuta en apache.
<?php $i = 0; if(isset($_REQUEST["i"])){ $i= $_REQUEST["i"]; sleep(1); } $ch = curl_init("http://localhost".$_SERVER["PHP_SELF"]."?i=".($i+1)); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_TIMEOUT, 1); curl_exec($ch); print "hello world/n"; ?>
De alguna manera, esto no funciona en GAE. Entonces, ¿qué pasa si hacemos HTTP GET en alguna otra url, digamos url2, que hace HTTP GET en la primera url? Esto parece funcionar en GAE. El código para esto se parece a esto.
class FirstUrl(webapp.RequestHandler): def get(self): self.response.out.write("ok") time.sleep(2) urlfetch.fetch("http://"+self.request.headers["HOST"]+''/url2'') class SecondUrl(webapp.RequestHandler): def get(self): self.response.out.write("ok") time.sleep(2) urlfetch.fetch("http://"+self.request.headers["HOST"]+''/url1'') application = webapp.WSGIApplication([(''/url1'', FirstUrl), (''/url2'', SecondUrl)]) def main(): run_wsgi_app(application) if __name__ == "__main__": main()
Desde que descubrimos una forma de ejecutar una tarea en segundo plano, le permitimos crear abstracciones para tareas periódicas (temporizador) y una construcción de bucle que abarca muchas solicitudes HTTP (foreach).
Minutero
Ahora construir el temporizador es sencillo. La idea básica es tener una lista de temporizadores y el intervalo al que se debe llamar cada uno. Una vez que alcanzamos ese intervalo llamamos a la función de devolución de llamada. Usaremos Memcache para mantener la lista de temporizadores. Para saber cuándo llamar a la devolución de llamada, almacenaremos una clave en Memcache con intervalo como tiempo de caducidad. Periódicamente (digamos 5 segundos) verificamos si esa clave está presente, si no está presente, llamamos a la devolución de llamada y nuevamente configuramos esa tecla con el intervalo.
def timer(func, interval): timerlist = memcache.get(''timer'') if(None == timerlist): timerlist = [] timerlist.append({''func'':func, ''interval'':interval}) memcache.set(''timer-''+func, ''1'', interval) memcache.set(''timer'', timerlist) def checktimers(): timerlist = memcache.get(''timer'') if(None == timerlist): return False for current in timerlist: if(None == memcache.get(''timer-''+current[''func''])): #reset interval memcache.set(''timer-''+current[''func''], ''1'', current[''interval'']) #invoke callback function try: eval(current[''func'']+''()'') except: pass return True return False
Para cada
Esto es necesario cuando queremos hacer un cálculo largo, por ejemplo, hacer alguna operación en 1000 filas de bases de datos o buscar 1000 URL, etc. La idea básica es mantener una lista de devoluciones de llamadas y argumentos en Memcache y cada invocación de invocación con el argumento.
def foreach(func, args): looplist = memcache.get(''foreach'') if(None == looplist): looplist = [] looplist.append({''func'':func, ''args'':args}) memcache.set(''foreach'', looplist) def checkloops(): looplist = memcache.get(''foreach'') if(None == looplist): return False if((len(looplist) > 0) and (len(looplist[0][''args'']) > 0)): arg = looplist[0][''args''].pop(0) func = looplist[0][''func''] if(len(looplist[0][''args'']) == 0): looplist.pop(0) if((len(looplist) > 0) and (len(looplist[0][''args'']) > 0)): memcache.set(''foreach'', looplist) else: memcache.delete(''foreach'') try: eval(func+''(''+repr(arg)+'')'') except: pass return True else: return False # instead of # foreach index in range(0, 1000): # someoperaton(index) # we will say # foreach(''someoperaton'', range(0, 1000))
Ahora construir un programa que obtenga la lista de URL cada hora es sencillo. Aquí está el código.
def getone(url): try: result = urlfetch.fetch(url) if(result.status_code == 200): memcache.set(url, ''1'', 60*60) #process result.content except : pass def getallurl(): #list of urls to be fetched urllist = [''http://www.google.com/'', ''http://www.cnn.com/'', ''http://www.yahoo.com'', ''http://news.google.com''] fetchlist = [] for url in urllist: if (memcache.get(url) is None): fetchlist.append(url) #this is equivalent to #for url in fetchlist: getone(url) if(len(fetchlist) > 0): foreach(''getone'', fetchlist) #register the timer callback timer(''getallurl'', 3*60)
el código completo está aquí http://groups.google.com/group/httpmr-discuss/t/1648611a54c01aa He estado ejecutando este código en appengine durante unos días sin muchos problemas.
Advertencia: Hacemos un uso intensivo de urlfetch. El límite en no de urlfetch por día es 160000. Por lo tanto, tenga cuidado de no alcanzar ese límite.
¿Cómo ejecutar tareas en segundo plano en appengine?
La próxima versión de runtime tendrá algún tipo de motor de ejecución periódica a''la cron. Vea este mensaje en el grupo de App Engine.
Entonces, todas las piezas del SDK parecen funcionar, pero mis pruebas indican que esto no se está ejecutando aún en los servidores de producción. Configuré un cron "cada 1 minuto" que se registra cuando se ejecuta y aún no se ha invocado.
Aunque es difícil decir cuándo estará disponible, ...
Puede usar la API de Task Queue Python .
Use la cola de tareas - http://code.google.com/appengine/docs/java/taskqueue/overview.html
Puede encontrar más sobre trabajos cron en Python App Engine aquí .
Hay una instalación de cron integrada en el motor de la aplicación.
Consulte: https://developers.google.com/appengine/docs/python/config/cron?hl=en
El uso de la biblioteca diferida de Python es la forma más sencilla de realizar tareas en segundo plano en Appengine utilizando Python, que se basa en la API de TaskQueue.
from google.appengine.ext import deferred
def do_something_expensive(a, b, c=None):
logging.info("Doing something expensive!")
# Do your work here
# Somewhere else
deferred.defer(do_something_expensive, "Hello, world!", 42, c=True)
Si desea ejecutar tareas periódicas en segundo plano, consulte esta pregunta (cron de App Engine)
Si sus tareas no son periódicas, consulte Task Queue Python API o Task Queue Java API