desarrollando aplicaciones de red con twisted

45
Desarrollando aplicaciones de red con Twisted

Upload: jjconti

Post on 14-Aug-2015

192 views

Category:

Documents


2 download

TRANSCRIPT

Desarrollando aplicaciones de red conTwisted

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

Aplicaciones de red

• LAN/WAN

• Internet (TCP/IP)

• Cliente/Servidor

Capas

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

Qué es bloqueante?

1. Python stdlib networking

2. Extensiones C

3. <adivina que va aca>

1) Python stdlib networking

• urllib[2] & httplib

• smtplib

• python-memcached

Alternativas en Twisted

• twisted.web.client

• twisted.mail.smtp

• twisted.protocols.memcache

2) Extensiones en C

• APIs de BD: psycopg2, sqlite

• Cálculo intensivo: PyCripto

• socket.gethostbyname

Alternativas en Twiseted

• twisted.enterprise.adbapi

• deferToThread

• deferToThread/twisted.names

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()

Servidores y clientes

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