Está en la página 1de 4

DESARROLLO Python: ZeroMQ

Redes de alto rendimiento

ZeroMQ
Sebastian Duda, 123RF.com

Gracias a ZeroMQ es posible crear complejos sistemas de comunicaciones en Python de forma sencilla. POR JOS MARA RUZ

a comunicacin de informacin a travs de una red es, a da de hoy, una de las principales tareas de prcticamente cualquier programa. Nos resulta casi raro pensar en un programa que no disponga de la capacidad de compartir informacin mediante algn tipo de protocolo. Ya sea mediante TCP/ IP, XMLRPC o AJAX, el abanico de posibilidades es inmenso. Existen literalmente cientos de protocolos de comunicaciones que podemos usar, y a pesar de ello aparece ZeroMQ cmo se le puede ocurrir a alguien crear un nuevo protocolo de red?

ZeroMQ
Los creadores de ZeroMQ pertenecen a la empresa iMatix Corporation. Esta empresa dise el protocolo AMQP, cuyo objetivo era unificar y simplificar el desarrollo de software que trabajase con sistemas de cola de mensajes. Un sistema de cola permite a una aplicacin enviar mensajes a otra aplicacin. Dicho as parece una cosa muy sencilla, pero esconde muchas dificultades. No slo queremos que la

aplicacin A mande mensajes a B, siempre ocurre), rpidamente buscan sino que A no tenga que saber que B mejorarlo. La gente de iMatix comquiere sus mensajes. El sistema de prob que los sistemas de cola no cola recibir los mensajes de A y eran lo bastante flexibles. Dependas cualquier aplicacin podr suscride los servidores de cola de mensajes, birse a esa cola y recibir los mensasoftware que no controlabas, y no era jes. De esta forma es posible disear posible disear sistemas realmente sistemas en los que podemos reemcomplejos a partir de ellos. Aunque el plazar los diferentes componentes en protocolo AMQP permita interactuar cualquier momento sin que todo con todos ellos de forma estndar, caiga como un castillo de naipes (ver cada servidor era incompatible con el Figura 1). AMQP fue un xito, pero resto. era un mero protocolo de comunicaPara suplir estas necesidades, iMacin y dependa de los distintos sistetix decidi crear algo nuevo: un protomas de cola (comerciales y libres) colo de comunicaciones que simplifique existen. Ejemplos libres de estos case el famoso protocolo TCP/ IP pero sistemas son por ejemplo RabListado 1: Servidor Hola Mundo bitMQ o Acti01 import zmq veMQ [2] y [3]. 02 Los usuarios 03 context = zmq.Context() ms importantes 04 socket = context.socket(zmq.REP) de los sistemas 05 socket.bind(tcp://127.0.0.1:5000) de colas de even06 tos son las 07 while True: empresas finan08 msg = socket.recv() cieras, y cuando 09 print He recibido: , msg algo les funciona 10 socket.send(msg) (cosa que no

42

Nmero 72

WWW.LINUX- MAGAZINE.ES

Python: ZeroMQ DESARROLLO

que incorporase las caractersticas ms importantes de los sistemas de cola y permitiese prescindir de los servidores de cola de mensajes si fuese preciso. De este proyecto naci ZeroMQ, cuya implementacin iMatix liber como software libre.

REQ/REP
Explicar qu es ZeroMQ es ms complicado que demostrarlo, por lo que arrancaremos con un ejemplo sencillo de su potencia. Comencemos con nuestro habitual Hola Mundo. Vamos a necesitar dos programas, uno actuar como servidor y el otro como cliente. El servidor aparece en el Listado 1, mientras que el cliente aparece en el Listado 2. Debemos arrancar el servidor como cualquier programa Python en un terminal:
> python servidor.py

El servidor se quedar bloqueado en un bucle infinito esperando recibir mensajes, por lo que pasaremos a arrancar el programa cliente en otro terminal:
> python cliente.py Enviando: hola Enviando: mundo Enviando: cruel

En la consola donde arrancamos el servidor aparecer:


He recibido: hola He recibido: mundo He recibido: cruel

Qu ha ocurrido? Uno de los objetivos de ZeroMQ es el de simplificar el desarrollo de cdigo que haga uso de la red. En el Listado 1 podemos ver

que slo son necesarias 5 instrucciones para tener un servidor que recibe mensajes y responde a los mismos con un alto rendimiento. Las primeras tres lineas preparan el servidor: Generamos un contexto que prepara y gestionar el trabajo. Creamos un socket, con el parmetro zmq.REP (respuesta). Vinculamos el soc- Figura 1: Esquema de funcionamiento de las colas de mensajes . ket a un protocolo, direccin IP y puerto. innovaciones de ZeroMQ en los siguientes apartados. A continuacin entramos en un bucle infinito en el que recibimos un menPUB/SUB saje, hacemos algo con l y comunicamos al cliente que lo hemos recibido. ZeroMQ no habra recibido ninguna Los mensajes en ZeroMQ son unas atencin si slo simplificase TCP/ IP. cadenas de caracteres. Mientras en Han sido caractersticas como las otros protocolos es preciso trabajar a opciones zmq.PUB y zmq.SUB las que bajo nivel con datos binarios, en han atrado la atencin de numerosos ZeroMQ podemos trabajar con mensadesarrolladores y empresas. jes, lo que simplifica bastante nuestro El multicasting (la habilidad de trabajo (el mensaje puede ser XML o hacer que varios clientes reciban el JSON). mismo mensaje a la vez) ha sido Los primeros 3 pasos del cdigo del siempre difcil con el protocolo TCP/ cliente son muy similares, pero en IP. Existen protocolos alternativos que lugar de crear el socket con el parmelo emplean para tareas especficas, tro zmq.REP , empleamos zmq.REQ como por ejemplo el bombeo (strea(request, peticin) y conectamos con ming) de vdeo por Internet, pero sueel servidor en lugar de vincular el so len ser bastante complicados de usar. cket. Posteriormente enviamos los Si al multicasting aadimos la opcin mensajes y esperamos a que el servide suscribirse a determinados evendor nos comunique que los ha recitos, indicando que slo deseamos bido. recibir determinados mensajes, entonNo hemos hecho nada que no sea ces nos encontramos en un terreno posible con TCP/ IP, aunque la simplique hasta la llegada de ZeroMQ cidad de ZeroMQ ya es de por s todo estaba dominado por las colas de un logro. Pasemos a comprobar las eventos.

Listado 2: Cliente Hola Mundo


01 import zmq 02 context = zmq.Context() 03 socket = context.socket(zmq.REQ) 04 socket.connect(tcp://127.0.0.1:5000) 05 06 for i in [hola, mundo, cruel]: 07 08 socket.send(i) 09 print Enviando: , i 10 msg_in = socket.recv()

Listado 3: Fichero emisor.py


01 import zmq 02 context = zmq.Context() 03 socket = context.socket(zmq.REQ) 04 socket.connect(tcp://127.0.0.1:4000) 05 06 for i in [@josemaria hola mundo, 07 @linuxmagazine linux mola, 08 @otro mensaje aburrido]: 09 socket.send(i) 10 msg_in = socket.recv()

WWW.LINUX- MAGAZINE.ES

Nmero 72

43

DESARROLLO Python: ZeroMQ

Figura 2: Nuestro tuiter .

Vamos a crear un ejemplo que simular cmo funciona ms o menos Twitter (una empresa que en realidad es una cola de mensajes gigante). Tendremos un servidor al que un cliente emisor bombear mensajes y otros clientes estarn subscritos a determinados emisores el esquema de la Figura 2 aclara un poco la situacin. En una cola de eventos tradicional dispondramos de un servidor (como por ejemplo RabbitMQ) que recibira los mensajes, los almacenara y pasara a distribuirlos a los clientes subscritos a ellos. ZeroMQ se ha diseado como una herramienta para implementar el nivel de seguridad que deseemos. ZeroMQ no almacena los mensajes en el disco duro o una base de datos, los almacena en memoria y los bombea a gran velocidad. Est en nuestras manos decidir si queremos almacenar los mensajes temporalmente para ofrecer ms seguridad (por ejemplo guardndolos en SQlite3). Comencemos con el servidor que podemos ver en el Listado 3. El cdigo no dista mucho del que usamos para el servidor del anterior apartado. En lugar de emplear un solo socket usaremos dos. A uno nos vincularemos como un servidor tradicional, mientras que al otro lo haremos empleando el modelo PUB/SUB . Recibiremos los mensajes a travs de socket_recv y los reenviaremos a travs de socket .

En el Listado 4 est el cdigo del emisor de mensajes, que es prcticamente idntico al que empleamos en el Listado 2. Es en el Listado 5 donde encontramos diferencias. En lugar de conectarnos al socket del servidor con zmq.REQ , lo hacemos con zmq.SUB . Este cambio nos permite suscribirnos a determinados mensajes de una forma bastante sencilla. Empleando el mtodo setsockiopt() configuramos la conexin para recibir solamente los mensajes que comiencen con las cadenas @linuxmagazine y @josemaria . ZeroMQ descartar cualquier mensaje cuyo inicio no coincida con estos patrones. Podemos suscribirnos a tantas colas como deseemos, y ZeroMQ se encargar de filtrar los mensajes que seleccionemos por nosotros de forma eficiente. ZeroMQ da por supuesto que una vez se haya mandado el mensaje, ste llegar a su destino. Esto significa que no controla si la otra parte ha recibido el mensaje. Esta forma de trabajar se dise as a propsito, buscando aumentar el rendimiento. Si queremos de nuevo un mayor nivel de seguridad, podemos hacer que tanto servidor como cliente comuniquen que los datos han llegado correctamente a la otra parte, empleando por ejemplo un mensaje a una conexin REQ/ REP. Hemos podido replicar algo tan complicado como un sistema de cola de mensajes con subscriptores en mucho menos de 100 lineas de cdigo fuente, lo que no est nada mal.

Listado 4: Fichero tuiter.py


01 import zmq 02 from random import choice 03 context = zmq.Context() 04 05 socket_recv = context.socket(zmq.REP) 06 socket_recv.bind (tcp://127.0.0.1:4000) 07 08 09 socket = context.socket(zmq.PUB) 10 socket.bind (tcp://127.0.0.1:5000) 11 12 while True: 13 msg = socket_recv.recv() 14 socket_recv.send(msg) 15 print Reenvo: {0}.format(msg) 16 socket.send(msg)

Usando la Figura 3 como referencia, pensemos en un sistema en el que hay un proceso que recibe o emite una serie de datos. Los datos deben ser enviados a otro proceso para ser manipulados. En nuestro ejemplo sern cadenas de caracteres que se

Listado 5: Fichero origen.py


01 import zmq 02 import random 03 04 context = zmq.Context() 05 envio = context.socket(zmq.PUSH) 06 envio.bind(tcp://*:5557) 07 08 print Tenemos que esperar a que los workers se conecten 09 raw_input() 10 print Comenzamos a bombear trabajo... 11 cadenas = [hola, aloha, hello] 12 random.seed() 13 14 for i in range(0,5): 15 cadena = random.choice (cadenas) 16 envio.send(cadena) 17 print Enviando: {0}. format(cadena)

PUSH/PULL
Lo que voy a explicar ahora es un poco ms complicado. Incluso la documentacin oficial de ZeroMQ se vuelve confusa en este punto, pero las ideas bsicas son muy sencillas. Cuando estamos creando un sistema complejo compuesto por diferentes componentes (sean hebras, procesos o programas en distintas mquinas), lo ideal sera tener la posibilidad de aadir nuevas mquinas al sistema de forma total transparente, y que estas nuevas mquinas contribuyan hacindose cargo de parte del trabajo. Es lo que a da de hoy se conoce como escalar y la obsesin de casi todas las empresas de la Web.

44

Nmero 72

WWW.LINUX- MAGAZINE.ES

Python: ZeroMQ DESARROLLO

Listado 6: Fichero worker.py


01 import zmq 02 import time 03 context = zmq.Context() 04 05 recepcion = context.socket(zmq.PULL) 06 recepcion.connect (tcp://localhost:5557) 07 08 envio = context.socket(zmq.PUSH) 09 envio.connect (tcp://localhost:5558) 10 11 while True: 12 cadena = recepcion.recv() 13 print Proceso: {0}.format(cadena) 14 envio.send(cadena.upper()) 15 time.sleep(1)

pasarn a maysculas. El resultado de la operacin se recibir en un ltimo proceso que har algo con los datos (como por ejemplo imprimirlos en pantalla). El sistema escalar si es posible aadir nuevos procesos que realicen la operacin sin tener que modificar el cdigo fuente de ninguno de los programas. ZeroMQ dispone de un sistema para poder implementar este tipo de diseo: los sockets PUSH/ PULL. Si en los sockets PUB/ SUB disponemos de un emisor y un nmero indeterminado de receptores que reciben un mensaje, en los sockets PUSH/ PULL slo uno de los receptores recibir el mensaje. La eleccin del receptor ser

Listado 7: Fichero resultado.py


01 import zmq 02 03 context = zmq.Context() 04 05 recepcion = context.socket(zmq.PULL) 06 recepcion.bind(tcp://*:5558) 07 08 while True: 09 mensaje = recepcion.recv() 10 print Recibo: {0}.format(mensaje)

aleatoria, por lo que la carga de trabajo se ir repartiendo entre los distintos receptores. De forma mgica, ZeroMQ nos da la posibilidad de escalar sin apenas esfuerzo. Para poder ver PUSH/ PULL en accin vamos a tener que arrancar no 3 sino 4 programas como mnimo en distintos terminales: origen.py , dos o ms instancias de worker.py (que procesarn los mensajes) y resultado.py . El programa origen.py (Listado 6) ser el nico que funcione en modo PUSH. En este tipo de conexin, todas las conexiones PULL esperan a que se emitan datos desde una conexin PUSH. Como se ve en la Figura 3, worker.py (Listado 7) espera los datos de origen.py, y resultado.py (Listado 8) espera los datos que Figura 3: Arquitectura PUSH/PULL. provengan de worker.py . Podemos ver todo el sistema como un origen.py , podremos ver cmo los flujo de datos que arranca en mensajes son procesados por distintas origen.py y acaba en resultado.py . copias de worker.py y distribuyndose Para que las instancias de worker.py el trabajo entre ellos. se conecten correctamente con oriConclusin gen.py , debemos arrancar primero oriSlo hemos visto de forma superficial gen.py y esperar a que los distintos las caractersticas principales de worker.py se conecten a nosotros ZeroMQ, y an as queda patente que antes de comenzar a emitir datos. es un proyecto muy interesante. Para ello, origen.py debe detenerse ZeroMQ nos proporciona los ingrenada ms arrancar, cosa que hacemos dientes bsicos para crear una arquiesperando una entrada de teclado. tectura de red de la complejidad que Esto nos da tiempo para arrancar las queramos sin apenas esfuerzo. El rendistintas instancias de worker.py . Si dimiento de ZeroMQ lo hace ideal arrancramos worker.py antes que oripara cualquier problema que requiera gen.py , comprobaramos que todos tiempos de respuesta pequeos. Adelos mensajes enviados desde ms, la disponibilidad de libreras origen.py seran procesados por una ZeroMQ en ms de 10 lenguajes de nica instancia worker.py en lugar de programacin lo hacen idneo para distribuirse el trabajo. Una vez hayacomunicar programas de todo tipo. mos arrancado las dos, o ms, copias ZeroMQ es, sin lugar a dudas, uno de de worker.py , podemos arrancar tamlos proyectos ms sorprendentes de bin resultado.py . los ltimos aos. I Para posibilitar que el trabajo se pueda distribuir entre los distintos worker.py , he introducido un retraso RECURSOS de 1 segundo en el procesado del men[1] Protocolo ZeroMQ: http://www. saje con time.sleep() . De esta forma, zeromq.org/ origen.py mandar los mensajes a la [2] RabbitMQ, Erlang en accin: http:// instancia de worker.py que est libre. www.rabbitmq.com/ Cuando, por fin, todo est arran[3] ActiveMQ, proyecto Apache: http:// cado y pulsemos cualquier tecla en el activemq.apache.org/ terminal donde hayamos arrancado

WWW.LINUX- MAGAZINE.ES

Nmero 72

45