Message Oriented Middleware: Java JMS LSUB GSYC 22 de abril de 2015 (cc) 2013 Laboratorio de Sistemas, Algunos derechos reservados. Este trabajo se entrega bajo la licencia Creative Commons Reconocimiento NoComercial - SinObraDerivada (by-nc-nd). Para obtener la licencia completa, v´ ease http://creativecommons.org/licenses/. Tambi´ en puede solicitarse a Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. Las im´ agenes de terceros conservan su licencia original. MOM • MOM: Message Oriented Middleware. JMS (Java Message Service) es el MOM de Java. • Permite la comunicaci´ on d´ebilmente acoplada, fiable, s´ıncrona y as´ıncrona entre clientes mediante el paso de mensajes. • Es una especificaci´ on de API que forma parte de Java EE, hay m´ ultiples implementaciones. • El MOM depende de un servicio central o broker para gestionar los mensajes. • Usaremos GlassFish como servidor de aplicaciones Java EE. OOM/RPC vs MOM OOM/RPC vs MOM • RPC: el objetivo es ejecutar un procedimiento: ”haz esto”. • MOM: el objetivo es notificar un evento: ”ha pasado esto”. JMS ¿Cu´ando se usa una comunicaci´ on d´ebilmente acoplada? • Si el emisor quiere no saber nada del receptor y viceversa. S´olo se debe saber el formato de los mensajes. • No se quiere depender de las interfaces del resto de componentes, como en RCP/RMI. • No se quiere depender de la ejecuci´ on de los otros componentes: los mensajes pueden quedar almacenados en el sistema y ser atendidos m´as tarde. JMS Una aplicaci´on JMS se compone de: • Clientes: componentes que env´ıan y reciben mensajes. Se garantiza que el mensaje se entrega justo una vez. • Mensajes: objetos de comunicaci´ on entre componentes. • Proveedor JMS: el sistema que implementa el API de JMS. La plataforma Java EE proporciona un proveedor de JMS. Hay distintas implementaciones. • Los objetos administrados (administered objects) necesarios para interactuar con el proveedor. Se administran mediante una herramienta (p. ej. asadmin). JMS Hay tipos de comunicaci´ on en JMS: • Point-to-point. • Publish/subscribe. Point-to-Point • Un consumidor por mensaje. • Colas de mensajes persistentes: emisor y receptor no tienen dependencia temporal. • Los mensajes se entregan ordenados. • El receptor confirma el procesamiento de un mensaje. Imagen: (c) Oracle Publish/Subscribe • M´ ultiples consumidores por mensaje. • Un consumidor se subscribe a un tema (topic). • El receptor debe estar activo para recibir: emisor y receptor tienen dependencia temporal. Imagen: (c) Oracle JMS Tipos de mensajes: • Text: Strings. • Object: Un objeto Serializable. • Bytes: Un array de bytes. • Map: Un diccionario. • Stream: Un stream de valores primitivos. Arquitectura Imagen: (c) Oracle Administered Objects La herramienta de configuraci´ on de Java EE nos permite administrar dos tipos de objetos: • ConnectionFactory: crea conexiones con el proveedor de JMS. • Queue connection factory: para Point-To-Point. • Topic connection factory: para Publish/Subscribe. • Connection factory: para ambas. • Destination: representa emisores y destinatarios. • Queue: para Point-To-Point. • Topic: para Publish/Subscribe. Administraci´on: GlassFish • Crear un dominio (una o m´as instancias del servidor de apliaciones) que tiene asociados estos puertos TCP entre otros: • • • • Un Un Un Un puerto puerto puerto puerto para el servidor de aplicaciones (8080 por omisi´ on). de administraci´ on (4848 por omisi´ on). de JMS (7070 por omisi´ on). de IIOP para RMI/CORBA (3700 por omisi´ on). asadmin create-domain --adminport 5000 asadmin list-domains mydomain • Arrancar el dominio y la base de datos con la herramienta asadmin: asadmin start-domain mydomain asadmin start-database • Arrancar un navegador: http://localhost:4848 Administraci´on: GlassFish • Configurar JMS Default Host con la direcci´on de la m´aquina que ejecuta el broker de JMS (en este caso la m´ aquina en la que ejecuta GlassFish). Configurations → JavaMessageService → default JMS host • Crear los objetos administrados de JMS (JMS Resources): Resources → JMSResources → ConnectionFactories Resources → JMSResources → DestinationResources • Nombre (JNDI). • Tipo de recurso. Administraci´on: GlassFish Administraci´on: GlassFish Administraci´on: GlassFish JNDI • Los recursos se registran en un espacio de nombres de JNDI (Java Naming and Directory Interface). • JNDI es una interfaz homogenea para distintos sistemas de nombrado (LDAP, CORBA, NDS, etc.). • En JMS se usa para localizar los objetos administrados. • Por omisi´ on se conecta a localhost:3700. Si el servidor JNDI est´a en otra parte, hay que definirlo en las propiedades. • Para resolver los nombres: I n i t i a l C o n t e x t j n d i = new I n i t i a l C o n t e x t ( ) ; QueueConnectionFactory qFactory = ( QueueConnectionFactory ) j n d i . lookup ( ” Factoria1 ” ) ; // Lookup q ue u e Queue que ue = ( Queue ) j n d i . l o o k u p ( ” C o l a 1 ” ) ; Modelo • Conexi´ on: objeto que representa la conexi´ on con el proveedor de JMS. • Sesi´ on: contexto para enviar y recibir mensajes desde un u ´nico thread. La sesi´on crea mensajes, productores y consumidores para su thread (no se deben usar desde otros threads). Modelo Imagen: (c) Oracle Interfaces Conexi´on Point-to-Point • El m´ etodo createQueueConnection de la factor´ıa crea una conexi´on con el proveedor de JMS. • Dentro de una conexi´ on podemos crear una o m´as sesiones para enviar, recibir, etc. • El m´ etodo close() cierra la conexi´ on y todo lo que depende de ella (sesiones, etc.). QueueConnection c o n n e c t i o n = f a c t o r y . createQueueConnection ( ) ; QueueSession s e s s i o n = c o n n e c t i o n . c r e a t e Q u e u e S e s s i o n ( f a l s e , Q u e u e S e s s i o n . AUTO ACKNOWLEDGE ) ; Sesi´on Point-to-Point • El primer argumento de createQueueSession inica si la sesi´on es transaccional. • Si no es transaccional, hay que indicar un modo de asentimiento del mensaje (si es transaccional se ignora el segundo argumento): • AUTO ACKNOWLEDGE: se hace autom´ aticamente. • CLIENT ACKNOWLEDGE: el receptor debe invocar el m´ etodo acknowledge() manualmente. Si una sesi´ on termina sin confirmar la recepci´ on de un mensaje, el mensaje vuelve a estar disponible en la cola. La confirmaci´ on de un mensaje confirma todos los mensajes anteriores recibidos en la sesi´on. Sesi´on La sesi´on nos permite crear objetos de tipo: • QueueReceiver para recibir mensajes. Aunque es posible tener dos sesiones distintas con QueueReceivers para la misma cola, el est´andar de JMS no define como se reparten los mensajes. S´olo un consumidor recibe el mensaje. • QueueSender para enviar mensajes. • QueueBrowser para inspeccionar mensajes en la cola sin sacarlos. • TemporaryQueue para crear una cola temporal que solo sobrevive a la conexi´ on en la que se crea. Sesi´on Transaccional • La transacci´ on es entre un extremo y el proveedor de JMS, no entre los extremos. • Sem´ antica: los mensajes se ponen/quitan de la colas todos o ninguno. • Forman parte de la transacci´ on todos los mensajes enviados y recibidos en la sesi´ on entre dos invocaciones de commit()/rollback(), potencialmente de distintas colas. • Transaction Commit: todos los mensajes producidos est´ an enviados y todos los mensajes consumidos ent´an asentidos. • Transaction Rollback: todos los mensajes producidos se han destruido y todos los mensajes consumidos se han devuelto. Sesi´on Transaccional Env´ıo: • Despu´ es de enviar, los mensajes no est´an disponibles en la cola hasta que no se invoque el m´etodo commit() de la sesi´on. • Si se invoca rollback() en su lugar, los mensajes de la transacci´on no llegan nunca a estar disponibles en la cola. Sesi´on Transaccional Recepci´on: • Los mensajes recibidos en la transacci´ on no se eliminan de la cola mientras se van recibiendo; se eliminan de la cola cuando el receptor invoca commit(), despu´ es de recibirlos. • Si se invoca rollback() en su lugar, todos los mensajes de la transacci´on vuelven a estar disponibles en la cola. Cierre • Cuando ya no se van a utilizar, hay que cerrar los receptores/emisores, sesiones y conexiones, llamando a close(). • El cierre de una sesi´ on cierra los receptores/emisores que la pertenecen. • El cierre de una conexi´ on cierra las sesiones correspondientes. Ejemplo Point-To-Point: Sender // e x c e p t i o n s a r e i g n o r e d i n t h i s e x a m p l e I n i t i a l C o n t e x t j n d i = new I n i t i a l C o n t e x t ( ) ; QueueConnectionFactory f a c t o r y = ( QueueConnectionFactory ) j n d i . lookup ( ” Factoria1 ” ) ; Queue q ue ue = ( Queue ) j n d i . l o o k u p ( ” C o l a 1 ” ) ; QueueConnection c o n n e c t i o n = f a c t o r y . createQueueConnection ( ) ; QueueSession s e s s i o n = c o n n e c t i o n . c r e a t e Q u e u e S e s s i o n ( f a l s e , Q u e u e S e s s i o n . AUTO ACKNOWLEDGE ) ; Qu eue Sender s e n d e r = s e s s i o n . c r e a t e S e n d e r ( queue ) ; f o r ( i n t i = 0 ; i < 1 0 ; i ++){ T e x t M e s s a g e msg = s e s s i o n . c r e a t e T e x t M e s s a g e ( ) ; msg . s e t T e x t ( ” Message ” + i + ” t o C o l a 1 ! ” ) ; s e n d e r . s e n d ( msg ) ; System . e r r . p r i n t ( ” . ” ) ; Thread . s l e e p ( 1 0 0 0 ) ; } connection . close ( ) ; // c l o s e s t h e c o n n e c t i o n , t h e s e s s i o n and t h e r e c e i v e r Ejemplo Point-To-Point: Synchronous Receiver // e x c e p t i o n s a r e i g n o r e d i n t h i s e x a m p l e I n i t i a l C o n t e x t j n d i = new I n i t i a l C o n t e x t ( ) ; QueueConnectionFactory f a c t o r y = ( QueueConnectionFactory ) j n d i . lookup ( ” Factoria1 ” ) ; Queue q ue ue = ( Queue ) j n d i . l o o k u p ( ” C o l a 1 ” ) ; QueueConnection c o n n e c t i o n = f a c t o r y . createQueueConnection ( ) ; QueueSession s e s s i o n = c o n n e c t i o n . c r e a t e Q u e u e S e s s i o n ( f a l s e , Q u e u e S e s s i o n . AUTO ACKNOWLEDGE ) ; Q u e u e R e c e i v e r r e c e i v e r = s e s s i o n . c r e a t e R e c e i v e r ( queue ) ; connection . s t ar t ( ) ; System . e r r . p r i n t l n ( ” L i s t e n i n g . . . ” ) ; for ( ; ; ) { Message msg = r e c e i v e r . r e c e i v e ( ) ; i f ( msg == n u l l ){ System . o u t . p r i n t l n ( ” no more m e s s a g e s ! ” ) ; break ; } i f ( msg i n s t a n c e o f T e x t M e s s a g e ){ T e x t M e s s a g e m = ( T e x t M e s s a g e ) msg ; System . o u t . p r i n t l n ( ” Message r e c e i v e d : ” + m. g e t T e x t ( ) ) ; } } connection . close ( ) ; // c l o s e s t h e c o n n e c t i o n , t h e s e s s i o n and t h e r e c e i v e r Ejemplo Point-To-Point: Asynchronous Receiver // e x c e p t i o n s and c l o s e ( ) c a l l s a r e i g n o r e d i n t h i s e x a m p l e p r i v a t e c l a s s A s y n c R e c e i v e r i m p l e m e n t s Runnable , M e s s a g e L i s t e n e r { p r i v a t e QueueConnection c o n n e c t i o n ; p r i v a t e Queue queue ; p u b l i c A s y n c R e c e i v e r ( Q u e u e C o n n e c t i o n con , Queue queue ){ t h i s . c o n n e c t i o n = con ; t h i s . queue = queue ; } @Override p u b l i c void run (){ try { QueueSession s e s s i o n = c o n n e c t i o n . c r e a t e Q u e u e S e s s i o n ( f a l s e , Q u e u e S e s s i o n . AUTO ACKNOWLEDGE ) ; Q u e u e R e c e i v e r r e c e i v e r = s e s s i o n . c r e a t e R e c e i v e r ( queue ) ; receiver . setMessageListener ( this ); System . o u t . p r i n t l n ( Thread . c u r r e n t T h r e a d ( ) . g e t I d ( ) + ” l i s t e n i n g ! ” ) ; // The t h r e a d w i l l be d o i n g i t s j o b doJob ( ) ; } c a t c h ( E x c e p t i o n e ){ . . . } f i n a l l y { . . . } } @Override p u b l i c v o i d onMessage ( Message msg ) { try { T e x t M e s s a g e m = ( T e x t M e s s a g e ) msg ; System . o u t . p r i n t l n ( ” L i s t e n e r , Thread ” + Thread . c u r r e n t T h r e a d ( ) . g e t I d ( ) + ” m e s s a g e r e c e i v e d : ” + m. g e t T e x t ( ) ) ; } c a t c h ( JMSException e ) { . . . } f i n a l l y { . . . } } } Publish/Subscribe • La conexi´ on y la sesi´ on se crea de forma similar, pero usando las interfaces de Topic en lugar de las de Queue. • El emisor se crea con el m´ etodo createPublisher() de la sesi´on. Recibe como par´ametro el Topic al que se quiere enviar. • Para recepci´ on s´ıncrona, debemos instanciar un objeto MessageConsumer invocando createConsumer(). • Para recepci´ on as´ıncrona, debemos instanciar un objeto TopicSubscriber invocando createSubscriber() e instalar un manejador: un objeto que implemente la interfaz MessageListener. Ejemplo Pub/Sub: Sender try { I n i t i a l C o n t e x t j n d i = new I n i t i a l C o n t e x t ( ) ; TopicConnectionFactory factory = ( TopicConnectionFactory ) j n d i . lookup (” Factoria2 ” ) ; Topic t o p i c = ( Topic ) j n d i . lookup ( ” Topic1 ” ) ; TopicConnection connection = f a c t o r y . createTopicConnection ( ) ; TopicSession session = c o n n e c t i o n . c r e a t e T o p i c S e s s i o n ( f a l s e , T o p i c S e s s i o n . AUTO ACKNOWLEDGE ) ; TopicPublisher publisher = session . createPublisher ( topic ); f o r ( i n t i = 0 ; i < 1 0 ; i ++){ T e x t M e s s a g e msg = s e s s i o n . c r e a t e T e x t M e s s a g e ( ) ; msg . s e t T e x t ( ” Message ” + i + ” t o C o l a 1 ! ” ) ; p u b l i s h e r . p u b l i s h ( msg ) ; System . e r r . p r i n t ( ” . ” ) ; Thread . s l e e p ( 1 0 0 0 ) ; } connection . close ( ) ; } c a t c h ( E x c e p t i o n e ){ e . printStackTrace ( ) ; } Ejemplo Pub/Sub: Synchronous Receiver try { I n i t i a l C o n t e x t j n d i = new I n i t i a l C o n t e x t ( ) ; TopicConnectionFactory factory = ( TopicConnectionFactory ) j n d i . lookup (” Factoria2 ” ) ; Topic t o p i c = ( Topic ) j n d i . lookup ( ” Topic1 ” ) ; TopicSession session = c o n n e c t i o n . c r e a t e T o p i c S e s s i o n ( f a l s e , T o p i c S e s s i o n . AUTO ACKNOWLEDGE ) ; MessageConsumer c o n s u m e r = s e s s i o n . c r e a t e C o n s u m e r ( t o p i c ) ; System . o u t . p r i n t l n ( ” Thread ” + Thread . c u r r e n t T h r e a d ( ) . g e t I d ( ) + ” l i s t e n i n g ! ” ) ; for (;;){ for ( ; ; ) { Message msg = c o n s u m e r . r e c e i v e ( ) ; i f ( msg == n u l l ){ System . o u t . p r i n t l n ( ” no more m e s s a g e s ! ” ) ; break ; } i f ( msg i n s t a n c e o f T e x t M e s s a g e ){ T e x t M e s s a g e m = ( T e x t M e s s a g e ) msg ; System . o u t . p r i n t l n ( ” Consumer , Thread ” + Thread . c u r r e n t T h r e a d ( ) . g e t I d ( ) + ” m e s s a g e r e c e i v e d : ” + m. g e t T e x t ( ) ) ; } } }catch ( Exception e ) { . . . } f i n a l l y { . . . } Ejemplo Pub/Sub: Asynchronous Receiver // e x c e p t i o n s and c l o s e ( ) c a l l s a r e i g n o r e d i n t h i s e x a m p l e p r i v a t e s t a t i c c l a s s A s y n c R e c e i v e r i m p l e m e n t s Runnable , M e s s a g e L i s t e n e r { p ri v at e TopicConnection connection ; p r i v a t e Topic t o p i c ; p u b l i c A s y n c R e c e i v e r ( T o p i c C o n n e c t i o n con , T o p i c t o p i c ){ t h i s . c o n n e c t i o n = con ; this . topic = topic ; } @Override p u b l i c void run (){ try { TopicSession session = c o n n e c t i o n . c r e a t e T o p i c S e s s i o n ( f a l s e , T o p i c S e s s i o n . AUTO ACKNOWLEDGE ) ; TopicSubscriber subscriber = session . createSubscriber ( topic ); subscriber . setMessageListener ( this ); System . o u t . p r i n t l n ( ” Thread ” + Thread . c u r r e n t T h r e a d ( ) . g e t I d ( ) + ” s u b s c r i b e d ! ” ) ; // The t h r e a d w i l l be d o i n g i t s j o b doJob ( ) ; }catch ( Exception e ) { . . . } f i n a l l y { . . . } } @Override p u b l i c v o i d onMessage ( Message msg ) { try { T e x t M e s s a g e m = ( T e x t M e s s a g e ) msg ; System . o u t . p r i n t l n ( ” S u b s c r i b e r , Thread ” + Thread . c u r r e n t T h r e a d ( ) . g e t I d ( ) + ” m e s s a g e r e c e i v e d : ” + m. g e t T e x t ( ) ) ; } c a t c h ( JMSException e ) { . . . } f i n a l l y { . . . } } } Ejecuci´on En la m´aquina en la que ejecuta GlassFish: # $GLASSFISH i s t h e p a t h t o t h e G l a s s F i s h d i r e c t o r y : j a v a −cp j m s e x a m p l e . j a r : $GLASSFISH/ g l a s s f i s h / l i b / g f − c l i e n t . j a r org . l s u b . jmsexample . P2PReceiver Ejecuci´on En otras m´aquinas: • No hace falta ejecutar GlassFish en ellas. • Es necesario tener todas las clases en el classpath. Podemos instalar GlassFish o copiar todos los JAR necesarios a mano. • Hay que indicar la m´ aquina en la que ejecuta el proveedor de JMS. # $GLASSFISH i s t h e p a t h t o t h e G l a s s F i s h d i r e c t o r y : j a v a −Dorg . omg . CORBA . O R B I n i t i a l H o s t=omac . l s u b . o r g −cp j m s e x a m p l e . j a r : $GLASSFISH/ g l a s s f i s h / l i b / g f − c l i e n t . j a r org . l s u b . jmsexample . P2PReceiver asadmin Otros comandos u ´tiles: • flush-jmsdest --desttype queue cola : drena la cola indicada. • flush-jmsdest --desttype topic topic : drena el topic. • stop-domain domain : para la ejecuci´ on del dominio. • delete-domain domain : borra el dominio. • ...
© Copyright 2024