Sistema de Mensajeria de Colas con ZeroMQ y Python

download Sistema de Mensajeria de Colas con ZeroMQ y Python

If you can't read please download the document

Transcript of Sistema de Mensajeria de Colas con ZeroMQ y Python

  • 1. Sistema de Mensajeria de Colas con ZeroMQ y PythonErnesto CrespoTwitter: @_seraph1Email: [email protected]: http://ernesto-ecrespo.blogspot.com

2. Agenda Qu es ZeroMQ? Implementar una capa de mensajes con ZeroMQ Tipos de transporte Patrones de mensajes Seleccionar infraestructura Patrones de comunicacin: Patrn Solicitud/Respuesta (Request/Reply) Patrn Suscriptor/Publicador(PUB/SUB) Patrn PUSH/PULL Patrn de mensaje PAR Otros ejemplos: REQ/REP y PUB/SUB Multicast Benchmarks Licencia Referencias 3. Qu es ZeroMQ?Es una librera asncrona de alto rendimiento de mensajeradestinado a su uso en aplicaciones distribuidas escalableso concurrentes.Es una librera que soporte distintos patrones de comunicacin de redes.ZeroMQ usa colas internamente para el buffer de mensajes para noBloquear aplicaciones al enviar mensajes.Rpido: 8M msg/seg ,30useg de latencia.Licencia: LGPLMultiplataforma y multilenguajes(ms de 30 lenguajes de programacin) 4. Implementar una capa de mensajes con ZeroMQPara implementar una capa de mensajes ZeroMQ se necesitan 3 pasos:1. Seleccionar transporte (ipc,tcp,epgm,inproc y pgm).2. Seleccionar infraestructura.3. Seleccionar patrn de mensaje. 5. Tipos de transporteZeroMQ soporta varios estilos de capa de transporte: tcp://equipo:puerto, procesos en una red TCP. inproc://equipo, hilos en un proceso. ipc:///tmp/archivo socket Unix que permite una comunicacinentre procesos. pgm://interface:direccin:puerto y epgm://interface:direccin:puertosoporta la librera OpenPGM para comunicacin multicast sobre IP(pgm) y sobre UDP (epgm). 6. Patrones de mensajesLos patrones bsicos de ZeroMQ son: Request/Reply: bidireccional, balanceo de carga y basado en estado. Publish/Subscribe: publica multiples recipientes en uno. Push/Pull (pipeline): distribuye datos a nodos en un pipeline. Pair: comunicacin exclusiva entre pares. 7. Seleccionar infraestructuraLuego de definir el tipo de transporte es necesario pensar como losdiferentes componentes se conectan.Tipos de dispositivos:1. Queue: un forwarder para el patrn req/resp.2. Forwarder: un forwarder para el patrn PUB/SUB.3. Streamer: un forwarder para el patrn pipelined. 8. Patrn REQ/REPComunicacin bidireccional que facilita el balanceo de carga y sebasa en estados.Este patrn es muy comn y es normalmente usado por varios servicioscomo: HTTP, POP o IMAP. 9. Servidor REQ/REP#!/usr/bin/env python#Se importa zeromqimport zmq#Se crea la instancia del contexto de zmq.context = zmq.Context()#Se define el socket con parmetro respuesta REP.socket = context.socket(zmq.REP)#Se asocia la direccin IP y el puerto donde el servidor escucha las peticiones.socket.bind("tcp://127.0.0.1:5000")#Se define un contadosc=1#Se genera un ciclo que slo finaliza si se recibe la letra q.while True: #Se recibe los mensajes. msg = socket.recv() #Se consulta si la longitud del mensaje es 1 y es la letra q se termina el ciclo if len(msg) == 1 and msg == "q": break #Se separa los datos que viene en un string separados por : datos = msg.split(":") #se realiza una suma con los datos recibidos. resultado = int(datos[0]) + int(datos[1]) #Se muestra en pantalla el resultado print "Iteracion: %s ,He recibido: %s, el resultado es: %s " %(c,msg,resultado) #Se enva el resultado al cliente socket.send(str(resultado)) #Se incrementa el contador. c += 1 10. Cliente REQ/REP#!/usr/bin/env python#Se importa zeromq y randomimport zmqimport random#Se crea la instancia del contextocontext = zmq.Context()#Se crea el socket y se para el argumento de peticin REQsocket = context.socket(zmq.REQ)#Se coencta a la IP y puerto donde escucha el servidorsocket.connect("tcp://127.0.0.1:5000")#Se genera un ciclo de 1000 repeticionesfor i in range(1000):#Se crea el string con el mensaje, se pasa 2 aargumentos aleatoriosmsg = "%s:%s" %(random.randint(1, 1000),random.randint(1, 1000))#Se envia el mensaje al servidorsocket.send(msg)#Se recibe el mensaje del servidormsg_in = socket.recv()#Se muestra en patalla los datos y el resultadoprint "Iteracion: %s, Enviado: %s, Recibido: %s" %(i,msg,msg_in)#Si se llea a la iteracin 999 se enva la letra q para finalizarif i == 999: socket.send("q") 11. Patrn PUB/SUBEn este patrn los componentes son pobremente acoplados, son degran ayuda para escalar ya que no existe necesidad de preocuparsede los suscriptores.Se puede pensar en servicios como XMPP o twitter, donde los mensajesslo le llegan a las personas que les toca recibirlos. 12. Servidor Patrn PUB/SUB#!/usr/bin/env python#Se importa ZeroMQimport zmq#Se importa choice a partir de randomfrom random import choice#Se crea la instancia del contextocontext = zmq.Context()#Se crea el socket pasandole argumento de publicacion PUBsocket = context.socket(zmq.PUB)#Se asocia la IP y el puerto que va a escuchar.socket.bind("tcp://127.0.0.1:5000")#Se importa sleepfrom time import sleep#Se crea una lista de paises y de eventospaises = [holanda,brasil,alemania,portugal,argentina,italia,rusia,venezuela]eventos = [tarjeta amarilla,tarjeta roja,gol,corner,falta] 13. Servidor Patrn PUB/SUB#Se crea un contador con valor inicial 1c=1#Se crea un ciclo indefinidowhile True:#Se define un mensaje pasando de forma aleatoria un pais y un eventomensaje = choice( paises) + " " + choice(eventos)#Se muestra en pantalla el valor del contador y el mensaje.print "->",c , mensaje#Se envia el mensajesocket.send(mensaje)#Se genera un retardo de 1 segsleep(1)#Se incrementa el contadorc += 1#Si se llega a 180 se termina el ciclo si no continua.if c == 180: breakelse: continue 14. #!/usr/bin/env python Cliente 1 Patrn PUB/SUB#Se importa zeroMQimport zmq#Se importa sleep a partir de timefrom time import sleep#Se crea la instancia del contexto de zeroMQcontext = zmq.Context()#Se crea el socket del suscriptor SUBsocket = context.socket(zmq.SUB)#Se crea la conexion a la IP y puerto del servidorsocket.connect("tcp://127.0.0.1:5000")#Se define una opcion del socket del suscriptor con argentina y venezuelasocket.setsockopt(zmq.SUBSCRIBE, "argentina")socket.setsockopt(zmq.SUBSCRIBE, "venezuela")#Se define el valor inicial de un contadorc=1#Se crea un ciclo indefinidowhile True: #Se muestra en pantalla el valor del contador y el mensaje recibido print c, "->",socket.recv() #Se genera un retardo de 1 seg en cada ciclo sleep(1) #Se incrementa el contador en 1 c += 1 #Si el contador llega a 90 se termina el ciclo, si no continua if c == 90:break else:continue 15. #!/usr/bin/env pythonCliente 2 Patrn PUB/SUB#Se importa zeroMQimport zmq#Se importa sleep a partir de timefrom time import sleep#Se crea la instancia del contexto de zeroMQcontext = zmq.Context()#Se crea el socket del suscriptor SUBsocket = context.socket(zmq.SUB)#Se crea la conexion a la IP y puerto del servidorsocket.connect("tcp://127.0.0.1:5000")#Se define una opcion del socket del suscriptor con brasil y alemaniasocket.setsockopt(zmq.SUBSCRIBE, "brasil")socket.setsockopt(zmq.SUBSCRIBE, "alemania")#Se define el valor inicial de un contadorc=1#Se crea un ciclo indefinidowhile True: #Se muestra en pantalla el valor del contador y el mensaje recibido print c, "->",socket.recv() #Se genera un retardo de 1 seg en cada ciclo sleep(1) #Se incrementa el contador en 1 c += 1 #Si el contador llega a 90 se termina el ciclo, si no continua if c == 90:break else:continue 16. Emisor, ejemplo Patrn PUSH/PULL#!/usr/bin/python#Se importa zeroMQ y randomimport zmqimport random#Se crea la instancia del contextocontext = zmq.Context()#Se crea el socket con el argumento PUSHenvio =context.socket(zmq.PUSH)#Se asocia el socket a escuchar todas las IPs y el puerto 5557envio.bind("tcp://*:5557")#se muestra que es necesario esperar que arranquen los workersprint "Hay que esperar que los workers se inicien"#Al dar enter se inicia el proceso de transmisionraw_input()print "Se inicia la transmision del trabajo..."#tupla de strings que se van a enviarcadenas = [hola, aloha,hello,buenas noches,buenas tardes,buenos dias,bienvenido]#Se crea un ciclo para recorrer la tuplafor i in range(len(cadenas)): cadena = cadenas[i] envio.send(cadena) print "Enviando: {0}".format(cadena) 17. Workers, ejemplo Patrn PUSH/PULL#!/usr/bin/python#Se importa ZeroMQ y sleep de timeimport zmqfrom time import sleep#Se crea la instancia del contextocontext = zmq.Context()#Se define el Socket con argumento PULLrecepcion = context.socket(zmq.PULL)#Se conecta el socket a localhost puerto 5557#Es el puerto donde origen envia con PUSH los datosrecepcion.connect("tcp://localhost:5557")#Se crea el socket de envio de los datos procesados con argumento PUSHenvio = context.socket(zmq.PUSH)#Se conecta el socket a localhost y puerto 5558envio.connect("tcp://localhost:5558")#Se genera un ciclo#donde se recive lo transmitido por origen#se procesa (se coloca en mayusculas)#se muestra en pantalla y se envia.#los ciclos tienen un retardo de 1 segwhile True: cadena = recepcion.recv() print "Proceso:{0}".format(cadena) envio.send(cadena.upper()) sleep(1) 18. Resultado, ejemplo Patrn PUSH/PULL#!/usr/bin/python#Se importa ZeroMQimport zmq#Se crea la instancia del contextocontext = zmq.Context()#Se crea el socket PULL que recibe los mensajes de los workersrecepcion = context.socket(zmq.PULL)#Se asocia el socket a escuchar todas las IPs en el puerto 5558#el puerto donde los workers envian los mensajesrecepcion.bind("tcp://*:5558")#Se inicia un ciclo donde se recibe los mensajes#de los workers y se muestra en pantallawhile True: mensaje = recepcion.recv() print "Recibo: {0}".format(mensaje) 19. Patrn PairComunicacin exlusiva entre pares.Lo que se logra es que si otro nodo intenta conectarse no lo lograr sino est declarado.Si est declarado, este establece conexin pero saca al nodo anterior. 20. Servidor Patrn Pair#!/usr/bin/env python#Importar zmqimport zmq#Se crea la instancia del contextocontext = zmq.Context()#Se crea el socket del tipo PARsocket = context.socket(zmq.PAIR)#Se asocia a una IP y puerto donde escucha el servidor.socket.bind("tcp://127.0.0.1:5555")#Se crea un ciclo.while True:#Se recibe un mensaje del clientemensaje = socket.recv()#Se muestra en pantallaprint "Recivo", mensaje#Se envia de vuelta el mensajesocket.send(mensaje) 21. Cliente Patrn Pair#!/usr/bin/env python#Se importa zmqimport zmq#Se crea la instancia del contextocontext = zmq.Context()#Se crea el socket con argumento del tipo de mensaje Par.socket = context.socket(zmq.PAIR)#Se conecta al servidor dado la IP y puerto.socket.connect("tcp://127.0.0.1:5555")#Se crea un cilo de 100 repeticiones.for i in range(100): #Se define el mensaje a pasar mensaje = "mensaje %s" % i #Se pasa el mensaje al servidor socket.send(mensaje) #Se presenta en pantalla el mensaje print "Enviando", mensaje #Se recibe el mensaje de vuelta msg_in = socket.recv() #Se presenta en pantalla el mensaje de vuelta print "Recibido:", msg_in 22. Patrones REQ/REP y PUB/SUB Emisor#!/usr/bin/python#Se importa zeroMQimport zmq#Se crea el contextocontext = zmq.Context()#Se crea el socket con el parametro REQsocket = context.socket(zmq.REQ)#Se asocia la IP y el puerto del socket.socket.connect("tcp://127.0.0.1:4000")#Se genera los mensajes estilo tuiter y se envia al socket.for i in [@_seraph1 Esta es una prueba,@otro viendo el juego, @_seraph1 otra prueba,@otro otro]: socket.send(i) msg_in = socket.recv() 23. Patrones REQ/REP y PUB/SUB Tuiter#!/usr/bin/env python#Se importa zeroMQimport zmq#Se importa choice de randomfrom random import choice#Se crea el contextocontext = zmq.Context()#Se define el socket de recepcion con argumento REPsocket_recv = context.socket(zmq.REP)#Se asocia a una IP y puerto el socket de recepcionsocket_recv.bind("tcp://127.0.0.1:4000")#Se define el socket de publicacion con argumento PUBsocket = context.socket(zmq.PUB)#Se asocia la ip y un puerto distinto al anterio socketsocket.bind("tcp://127.0.0.1:5000")#Se crea un ciclowhile True: #Se recibe el mensaje del socket de recepcion msg = socket_recv.recv() #Se envia el mensaje de recepcion socket_recv.send(msg) #Se muestra el mensaje en pantalla print "Reenvio: {0}".format(msg) #Se envia el mensaje al socket de publicacion socket.send(msg) 24. Patrones REQ/REP y PUB/SUB Receptor 1:#!/usr/bin/python#Se importa zeroMQimport zmq#Se crea el contextocontext = zmq.Context()#Se crea el socket de suscripcionsocket = context.socket(zmq.SUB)#Se asocia ese socket a la IP y puerto donde publica tuitersocket.connect("tcp://127.0.0.1:5000")#Se suscribe a escuchar los mensajes de @_seraph1socket.setsockopt(zmq.SUBSCRIBE, "@_seraph1")#se crea un ciclo donde se recibe los mensajeswhile True: print "->",socket.recv() 25. Patrones REQ/REP y PUB/SUB Receptor 2:#!/usr/bin/python#Se importa zeroMQimport zmq#Se crea el contextocontext = zmq.Context()#Se crea el socket de suscripcionsocket = context.socket(zmq.SUB)#Se asocia ese socket a la IP y puerto donde publica tuitersocket.connect("tcp://127.0.0.1:5000")#Se suscribe a escuchar los mensajes de @otrosocket.setsockopt(zmq.SUBSCRIBE, "@otro")#se crea un ciclo donde se recibe los mensajeswhile True: print "->",socket.recv() 26. Multicast Productor:#!/usr/bin/env python## producer#Se importa ZeroMQimport zmq#Se crea la instancia de la clase Context.context = zmq.Context()#Se define el socket con parametro PUBsocket = context.socket(zmq.PUB)#Se define unas opciones en el socket#esta opcion LINGER con valor cero, descarta mensajes no enviadossocket.setsockopt(zmq.LINGER, 0)#Se conecta al socket a la IP y puerto#por medio de multicastsocket.connect(epgm://192.168.10.96:5000)#Se crea un ciclo#si se envia el texto salir, se envia y luego finaliza el ciclo#si no, se envia el textowhile True: mensaje = raw_input("->") print mensaje socket.send(mensaje) if mensaje == "salir": breaksocket.close() 27. Multicast Consumidor:#!/usr/bin/env python#Importar zeroMQimport zmq#Se crea la instancia de la clase contextocontext = zmq.Context()#Se define el socket con parametro SUBsocket = context.socket(zmq.SUB)#Se conecta el socket a la IP y puerto del productor#con el transporte epgmsocket.connect(epgm://192.168.10.96:5000)#Se define los textos que se recibe la informacion.#prueba, hora y salir.socket.setsockopt(zmq.SUBSCRIBE, prueba)socket.setsockopt(zmq.SUBSCRIBE, hora)socket.setsockopt(zmq.SUBSCRIBE, salir)#Se define un ciclo,#se recibe la informacion#Si el texto es salir se muestra en pantalla#finaliza el ciclo, si no se muestra#el texto en pantallawhile True: rcv = socket.recv() print rcv if rcv == "salir": beak 28. Benchmarkhttp://mikehadlow.blogspot.com/2011/04/message-queue-shootout.html 29. LicenciaEste documento est licenciado bajo la GNU Free DocumentationLicense (GFDL). www.gnu.orgSe autoriza la copia y distribucin por cualquier medio, siempreque se realice bajo esta misma licencia, se mencione al autororiginal y se incluya esta nota. 30. Referencias: http://nichol.as/zeromq-an-introduction http://blog.pythonisito.com/2012/08/distributed-systems-with-zeromq.html http://mikehadlow.blogspot.com/2011/04/message-queue-shootout.html http://pop-servis.blogspot.com/2011/04/benchmarking-zeromq-over-network.html http://ernesto-ecrespo.blogspot.com/search/label/ZeroMQ http://www.zeromq.org/results:multicore-tests http://www.zeromq.org/results:10gbe-tests-v031 http://blog.pythonisito.com/2012/08/zeromq-flow-control-and-other-options.html#more