desarrollando aplicaciones de red con twisted
TRANSCRIPT
Objetivos
• Mío: aprender, ordenar ideas.
• Para algunos: mostrarles algo que no conocían.
• Para otros: darles una razón para estudiar Python.
• Esto no es un curso de Twisted, es apenas una introducción.
• Si ya sabés Twisted:
• No te voy a contar nada nuevo.
• Tu ayuda en la charla es bienvenida!
Agenda
• Aplicaciones de red
• Twisted: conceptos claves y ejemplos
• Reactor
• Deferreds
• Servidores y clientes
ClientesEn la arquitectura C/S el remitente de una solicitud es conocido como cliente.
• Es quien inicia solicitudes o peticiones, tienen por tanto un papel activo en lacomunicación (dispositivo maestro o amo).
• Espera y recibe las respuestas del servidor.
• Por lo general, puede conectarse a varios servidores a la vez.
• Normalmente interactúa directamente con los usuarios finales mediante una interfazgráfica de usuario.
ServidoresAl receptor de la solicitud enviada por cliente se conoce como servidor.
• Al iniciarse esperan a que lleguen las solicitudes de los clientes, desempeñanentonces un papel pasivo en la comunicación (dispositivo esclavo).
• Tras la recepción de una solicitud, la procesan y luego envían la respuesta alcliente.
• Por lo general, aceptan conexiones desde un gran número de clientes (en ciertoscasos el número máximo de peticiones puede estar limitado).
• No es frecuente que interactúen directamente con los usuarios finales.
Python
• Cuantos de aca entienden esta porción de Python?
def funcion(l, b=42): for x in l: if x == b: print x
numeros = [1, 4, 42, 3, 3, 42, 99, 1]
funcion(numeros)
Twisted
Twisted framework para hacer aplicaciones de red, basado en enventos, escrito enPython y con licencia MIT.
• Twisted está formado por varios sus proyectos.
Key concepts
• Event driven
• Sin hilos
• reactor
• No bloqueante
• Deferred
• Protocolos, transports, factories y sevicios
2) Extensiones en C
• APIs de BD: psycopg2, sqlite
• Cálculo intensivo: PyCripto
• socket.gethostbyname
3) Tu código
• El viejo y querido for
def procesar(datos): for x in datos: hacer_algo(x)
• len(datos)?
• 10
• 1000
• 1000000
• time de hacer_algo?
Reactor: multi tarea cooperativa
• Es el event loop que administra las aplicaciones que están corriendo.
• Provee interfaces básicas a disntios serivicos, incluyendo comunicaciones de red,hilos, y disparo de eventos.
• Hay muchas implementaciones de reactor, cada una modificada para dar mejorsoporte a algunas características puntuales.
while True:
stuff = wait_for_stuff(timeout)
if stuff: check_read_and_writes()
run_timed_events()
Reactor: distintas implementaciones
• Un reactor implementa un conjunto de interfaces. Dependiendo del elegido y laplataforma, puede ser que algunas no estén implementadas.
• IReactorCore: funcionalidades principales (requerido)
• IReactorFDSet: usa objetos FileDescriptor
• IReactorProcess: manejo de procesos.
• IReactorSSL: soporte para SSL networking.
• IReactorTCP: soporte para TCP networking.
• IReactorThreads: administración y uso de hilos.
• IReactorTime: calendarización.
• IReactorUDP: soporte para UDP networking.
• IReactorUNIX: soporte para sockets UNIX.
Deferreds
• Una promesa de un resultado
• Pieza clave de construcción de todo el framework
• No convierten mágicamente código bloqueante en código asincrónico.
• from twisted.internet.defer import Deferred
Deferreds (addCallback)
• "Llamame cuando tengas el resulatdo."
• El que llama a una función que devuelve un Deferred puede registrar callbacks yerrbacks.
page = urlopen('http://www.python.org.ar') # bloqueanteprint page.read()
d = client.getPage('http://www.python.org.ar')
def show(page): print page
d.addCallback(show)
Deferreds (addErrback)
• "Si pasa algo avisame"
• El que llama a una función que devuelve un Deferred puede registrar callbacks yerrbacks.
try: guardar_bloqueante(unDato) # bloqueanteexcept Excetion, e: print "Algo no anduvo."
d = guardar_no_bloqueante(unDato) # retorna enseguida
def handle(error): print "Algo no anduvo."
d.addErrback(handle)
Deferreds (errbacks y callbacks)
• El tema con los errbacks y callbacks es que vienen de a pares.
• Cuando usamos addCallback, se agrega un errback que no hace nada.
• Cuando usamos addErrback, se agrega un callback que no hace nada.
Deferreds (addCallbacks)
• Tenemos algunos métodos cómodos
d.addCallbacks(show, handle)
• Pero ojo! no es equivalente a
d.addCallback(show)d.addErrback(handle)
Deferreds (addCallbacks)
• Si teníamos
try: resultado = get_data_bloqueante() resultado = procesa_datos(resultado)except Exception, e: resultado = maneja_excepcion(e)
• Es equivalente a
d = get_data()d.addCallback(procesa_datos)d.addErrback(maneja_excepcion)
Deferreds (addCallbacks)
• Pero si teníamos
try: resultado = get_data_bloqueante()except Exception, e: resultado = maneja_excepcion(e)else: resultado = procesa_datos(resultado)
• Es equivalente a
d = get_data()d.addCallbacks(procesa_datos, maneja_excepcion)
Deferreds
• "Tu resultado está listo"
• El que crea el deferred es responsable de llamar al callback.
• No se pueden disparar más de una vez.
• Si un deferred ya se disparó, un addCallback ejecutará directamente el callback.
class X:
def get_data(self): self.d = Deferred() return self.d
def data_ready(self, data) self.d.callback(data) # solo puede ser llamado una vez!
deferToThread
• Útil, sobre todo cuando estamos empezando
• Puede no ser la solución más óptima a un problema
try: Evento(tipo='W', texto=mensaje).save()except: logError()
se convierte en
d = deferToThread(Evento(tipo='W', texto=mensaje).save)d.addErrback(logError)
Calendarizar eventos
• Ejecutar algo X segundos en el futuro
def futuro(s): print "Se ejecutara luego de 4.1 segundos: %s" % s
reactor.callLater(4.1, futuro, "Hola, hola.")
• Si necesitamos el resultado de la ejecución:
def mostrar(resultado): print resultado
d = task.deferLater(reactor, 4.1, futuro, "Hola, hola.")d.addCallback(mostrar)
Calendarizar eventos
• Correr cada X segundos
from twisted.internet import task
def cadaSegundo(): print "paso 1 segundo"
l = task.LoopingCall(cadaSegundo)l.start(1.0)
• También se pueden cancelar
call_id = reactor.callLater(5, f)call_id.cancel()
Escribiendo servidores
• Protocolos
• Donde implementamos el manejo y el parsing.
• twisted.internet.protocol.Protocol
• Fábricas
• Donde se guardan configuraciones persistentes.
• twisted.internet.protocol.Factory
Protocolos
• Manejan datos en forma asincrónica.
• Responde a los eventos, tan pronto como llegan por la red.
• dataReceived
from twisted.internet.protocol import Protocol
class Echo(Protocol):
def dataReceived(self, data): self.transport.write(data)
Protocolos
• Un ejemplo respondiendo a otro evento: connectionMade
from twisted.internet.protocol import Protocol
class QOTD(Protocol):
def connectionMade(self): self.transport.write("Al que madruga Dios lo ayuda.\r\n") self.transport.loseConnection()
Protocolos
• Un ejemplo respondiendo a otro evento: connectionLost
class Echo(Protocol):
def connectionMade(self): self.factory.numProtocols += 1 if self.factory.numProtocols > 100: self.transport.write("Ya somos muchos.\r\n") self.transport.loseConnection()
def connectionLost(self, reason): self.factory.numProtocols -= 1
def dataReceived(self, data): self.transport.write(data)
Protocolos basados en línea
• Basados en línea (terminan en \r\n o CR-LF)
• Permite un modo mixto (LineReceiver)
• o no (LineOnlyReceiver)
from twisted.protocols.basic import LineReceiver
class Answer(LineReceiver):
answers = {'Como va?': 'Bien', None : "No te entiendo"}
def lineReceived(self, line): if self.answers.has_key(line): self.sendLine(self.answers[line]) else: self.sendLine(self.answers[None])
Fábricas
• Se puede extender Factory aunque por lo general no es necesario
• Asociamos un tipo de protocolo a una fábrica y conectamos la fábrica a la red
from twisted.internet.protocol import Factoryfrom twisted.internet import reactor
myFactory = Factory()myFactory.protocol = Echo
reactor.listenTCP(8007, myFactory)reactor.run()
Clientes
• Protocolos
from twisted.internet.protocol import Protocol
class Bienvenida(Protocol): def connectionMade(self): self.transport.write("Hola servidor, yo soy tu cliente!\r\n") self.transport.loseConnection()
Fábrica de clientes
from twisted.internet.protocol import Protocol, ClientFactoryfrom sys import stdout
class EchoClientFactory(ClientFactory): def startedConnecting(self, connector): print 'Se inicio la conexion.'
def buildProtocol(self, addr): return Echo()
def clientConnectionLost(self, connector, reason): print 'Se perdio la conexion por:', reason
def clientConnectionFailed(self, connector, reason): print 'Fallo la conexion por:', reason
• Reconecciones (ReconnectingClientFactory)
Qué pasa con UDP?
• Lo anterior era para TCP, SSL y sockets UNIX.
• No orientado a la conexión.
• Un socket UDP puede recibir y enviar datagramas a cualquier nodo de la red.
• Los datagramas pueden no llegar en orden, duplicarse o incluso perderse!
• No hay multiples conexiones => no necesitamos fábricas, solo un protocolo.
• Usamos el reactor para conectar el protocolo con un transport UDP
• Extendemos twisted.internet.protocol.DatagramProtocol o uno de susconvenientes hijos.
Ejemplo UDP
from twisted.internet.protocol import DatagramProtocolfrom twisted.internet import reactor
class Echo(DatagramProtocol):
def datagramReceived(self, data, (host, port)): print "recibi %r desde %s:%d" % (data, host, port) self.transport.write(data, (host, port))
reactor.listenUDP(9999, Echo())reactor.run()
• ConnectedUDP
• multicast UDP
Qué no vimos?
• Conectarse a procesos locales
• http://twistedmatrix.com/documents/current/core/howto/process.html
• Distintos tipos de reactors y cómo elejirlos
• http://twistedmatrix.com/documents/current/core/howto/choosing-reactor.html
• Logging
• http://twistedmatrix.com/documents/current/core/howto/logging.html
• Sub frameworks
• Web
• Conch
• Cred
Más recursos
• http://twistedmatrix.com/documents/current/core/howto/index.html
• http://twistedmatrix.com/trac/wiki/TwistedCommunity
Créditos
• http://es.wikipedia.org/wiki/Cliente-servidor
• Cooperative Multitasking with Twisted: Getting Things Done Concurrently.
• David A Reid, 2010
• Twisted para seres humanos
• Lucio Torre, 2009
• Imágenes
• http://www.flickr.com/photos/nationalarchives/3208858799/
• http://es.wikipedia.org/wiki/Archivo:Pila-osi-es.svg
• http://es.wikipedia.org/wiki/Archivo:Winken_ueber_die_Berliner_Mauer.jpg
• http://es.wikipedia.org/wiki/Archivo:Medal_lg.jpeg
• http://www.flickr.com/photos/osucommons/3724233708/in/photostream/
Muchas gracias!
• Contacto
• mail: [email protected]
• twitter: @jjconti
• blog: http://www.juanjoconti.com.ar
Propagandahttp://python.org.ar/pyar/CharlasAbiertas2010#Twisted
Sábado 13 de Noviembre - Charlas Abiertas de Python en La Tribu (Lambaré 873,Capital Federal)
Twisted
Vamos a ver porque el modelo de twisted es necesario, como se programa en modoasyncronico usando deferreds y conocer el api de red de twisted para hacer servicios.
• Disertante: Lucio Torre
• 13 a 15 horas