Java Enterprise Edition

Java Enterprise Edition spotkanie nr 16 Java Message Service i Message-Driven Beans Alternatywa dla RMI-IIOP ● ● ● asynchroniczność (asynchrony)...
10 downloads 2 Views 186KB Size
Java Enterprise Edition spotkanie nr 16

Java Message Service i Message-Driven Beans

Alternatywa dla RMI-IIOP ●





asynchroniczność (asynchrony) –

brak blokowania



możliwy jest fire-and-forget

rozprężenie (decoupling) –

klienci nie muszą znać serwera



można wyłączyć jeden serwer i podłączyć drugi

niezawodność (reliability) –



serwer nie musi cały czas działać

wielu odbiorców i nadawców

2

Style komunikacji przy pomocy komunikatów ●

Publish/Subscribe –





jak radio/telewizja

Point-to-Point –

producenci/konsumenci



jak obsługa zamówień

Request-Reply –

asynchroniczne wywoływanie procedur



rzadziej spotykane



implementowane przy pomocy poprzednich

3

Gwarantowane dostarczanie wiadomości ●



guaranteed message delivery – MOM utrwala wiadomość I ponawia dopóki nie otrzyma potwierdzenia; występuje w odmianach –

certified message delivery – producent zostaje poinformowany o skonsumowaniu wiadomości



store and forward – producent też utrwala wiadomość I staje się niezależny od MOM

trwali konsumenci (durable subscription) – otrzymują zaległe wiadomości (z tematu) jak zaczną działać po przerwie

4

Message oriented middleware (MOM) ●

Przykładowe MOM: –

Tibco Rendezvous,



IBM Web-Sphere MQ,



BEA Tuxedo/Q,



Sun Java System Messaging Server,





Niektóre funkcje –

gwarantowanie dostarczenia wiadomości



odporność na błędy



równoważenie obciążenia



wykrywanie nieaktywnych odbiorców



SOAP po JMS

Microsoft MSMQ,



Sonic Software SonicMQ i



FioranoMQ

5

Java Message Service (JMS) ●

podobny pomysł do JDBC i JNDI



API do wysyłania i odbierania komunikatów



Service Provider Interface (SPI)

6

JMS z klienta 7

Przykład producer import javax.jms.*; import javax.naming.InitialContext; public class Producent { public static void main(String[] args) throws Exception { InitialContext ctx = new InitialContext(...); TopicConnectionFactory factory = (TopicConnectionFactory) ctx.lookup("jms/mojaFabrykaT"); TopicConnection connection = factory.createTopicConnection(); //pierwszy parametr oznacza, że nie stosujemy transakcji //drugi dotyczy potwierdzeń i nie ma znaczeniu przy producencie TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); Topic topic = (Topic)ctx.lookup("jms/mojT"); TopicPublisher publisher = session.createPublisher(topic); TextMessage msg = session.createTextMessage(); msg.setText("To jest komunikat."); publisher.send(msg);

} }

publisher.close(); connection.close(); 8

Przykład subscriber ,,, TopicSubscriber subscriber = session.createSubscriber(topic); subscriber.setMessageListener(new Consumer()); connection.start(); BufferedReader commandLine = new BufferedReader(new InputStreamReader(System.in)); while (true) { String s = commandLine.readLine(); if (s.equalsIgnoreCase("exit")) { connection.close(); break; } } ... class Consumer implements MessageListener { public void onMessage(Message message) { try { TextMessage textMessage = (TextMessage) message; String text = textMessage.getText(); System.out.println(text); } catch (JMSException jmse) { jmse.printStackTrace(); } } } 9

Wstrzeliwanie zależności Zamiast pobierać zasoby z JNDI można zastosować DI, ale tylko w obiektach działających na kontenerze @Resource(mappedName = "jms/mojaFabrykaT") private static TopicConnectionFactory factory; @Resource(mappedName = "jms/mojT") private static Topic topic;

10

Rodzaje interfejsów ●





ConnectionFactory



Session



QueueConnectionFactory



QueueSession



TopicConnectionFactory



TopicSession

Connection



MessageProducer



QueueConnection



QueueSender



TopicConnection



TopicPublisher

Destination –

Queue



Topic



MessageConsumer –

QueueReceiver, QueueBrowser



TopicSubscriber

11

Rodzaje wiadomości ●

BytesMessage



ObjectMessage



TextMessage



StreamMessage



MapMessage

Wiadomości mogą mieć nagłówki.

12

Message-Driven Beans

13

MDB ●





Niedostępny dla klientów Zazwyczaj implementuje javax.jms.MessageListener i odbiera wiadomości JMS –

dostaje wszystkie wiadomości (chyba, że używamy selectora)



trzeba się nagimnastykować żeby odpowiedzieć



wyjątki zgłaszane podczas przetwarzania wiadomości nie dotrą do klienta



nie ma stanu



kontener odpowiada za równoległe przetwarzanie (nie można nic zakładać o kolejności obsługi wiadomości)

Od EJB 2.1 konektory Java EE Connector Architecture mogą stanowić źródło komunikatów –

zyskujemy możliwość asynchronicznej komunikacji z innymi systemami 14

Alternatywy ●



Własne obiekty odpalają Session Beany –

Trzeba napisać kod, który zarejestruje nasz obiekt jako konsumenta.



Trzeba obsługiwać wielowątkowość.



Trzeba samemu startować konsumentów.



Nie możemy korzystać z usług kontenera.

Session Bean jako konsument –

Bean jest jednowątkowy i jeżeli obsługuje właśnie inne (zwykłe) żądanie, nie będzie mógł obsłużyć wiadomości.



Co jak nie ma beanów w chwili nadejścia wiadomości?

15

Cykl życia ●



wymagany jest bezargumentowy konstruktor dla JMS trzeba implementować: javax.jms.MessageListener –



z metodą onMessage(Message)

opcjonalnie można implementować javax.ejb.MessageDrivenBean –

setMessageDrivenContext(M essageDrivenContext) – kontener inicjalizuje beana kontekstem (alternatywnie można uzyskać z DI)



ejbRemove() - alternatywnie można adnotować jakąś metodę @PreDestroy

16

Przykład @MessageDriven(activationConfig = { @ActivationConfigProperty ( ropertyName = "destinationType", propertyValue = "javax.jms.Topic") } ) public class MyFirstMDB implements MessageListener { public MyFirstMDB() { System.out.println("MyFirstMDB()"); } public void onMessage(Message msg) { if (msg instanceof TextMessage) { TextMessage tm = (TextMessage) msg; try { String text = tm.getText(); System.out.println("Odebrany komunikat : " + text); } catch (JMSException e) { e.printStackTrace(); } } } @PreDestroy public void remove() { System.out.println("MyFirstMDB.remove()"); } }

17

Alternatywa w deskryptorze LogBeanDD examples.messaging.dd.LogBean javax.jms.MessageListener Bean javax.jms.Topic destinationType javax.jms.Topic ●

Mapowanie na konkretny temat/kolejkę wskazujemy w deskryptorze kontenera (ze względu na przenośność) 18

Opcjonalna zawartość @MessageDriven oraz ●



@ActivationConfigProperty( propertyName = "destinationType", propertyValue = "javax.jms.Topic" )

@ActivationConfigProperty( propertyName="messageSelector", propertyValue="JMSType = 'ala' AND ma = 'kota'" ) –

m.setStringProperty("ma","kota")



składnia wzorowana na SQL





● ●



@ActivationConfigProperty( propertyName="subscriptionDurability ", propertyValue="NonDurable" ) o tranzakcjach jeszcze będzie

destinationType javax.jms.Topic messageSelector JMSType='ala' AND ma='kota' subscriptionDurability NonDurable 19

Dalsze zagadnienia ●

Transakcje –



Bezpieczeństwo –







konsument i producent nie należą do tej samej transakcji (wiadomość pojawia się dopiero po zacommitowaniu) nie ma standardowego sposobu przekazywania security identity

Równoważenie obciążenia –

pośrednik i model pull



to kontener jest odbiorcą wiadomości, a nie poszczególne egzemplarze



wszystkie kontenery w klastrze są odbiorcami (i dla tematu i dla kolejki)

Kolejność przetwarzania komunikatów nie jest gwarantowana przez kontener Metody @PreDestroy są woływana rzadko (kontenery często stosują pule beanów) –

trzeba sprzątać zanim zgłosi się system exception (np. EJBException) 20

Poison message ●

Poison message – to wiadomość nieustannie transmitowana przez JMS destination, bo konsument nieustannie jej nie potwierdza –

tylko przy CMT



np. konsument nie umie przetworzyć wiadomości i zgłasza system exception lub woła MessageDrivenContext.setRollbackOnly()



przy wycofywaniu transakcji potwierdzenie nigdy nie zostaje wysłane do JMS destination



niektóre implementacje po pewnym czasie przenoszą taką wiadomość do specjalnej kolejki



w BMT konsumpcja i potwierdzenie nie jest częścią transakcji, czyli można potwierdzić mimo że transakcja się nie powiedzie ●



kiedy odbywa się potwierdzenie decydujemy ustawiając acknowledgeMode na jedną z wartości Auto-ackonwledge (po pomyślnym zakończeniu onMessage()) lub Dups-ok-acknowledge (może spowodować kilkukrotną retransmisję)

do sprawdzenia jako ćwiczenie co robi Message.acknowledge()

21

Odpowiadanie ●

Kolejka do odpowiedzi



Kolejka tymczasowa (związana z obiektem Connection) –

JMSReplyTo



JMSCorrelationID



tymczasowa kolejka stworzona przez bean może przepaść!

22

Kiedy stosować/nie stosować komunikatów Stosować jeżeli ●















wykonujemy kosztowne czynności, których efekty nie muszą być natychmiastowe nie oczekujemy odpowiedzi, więc nie musimy się blokować (wartość zwrotna void) potrzebujemy efektywniejszczego równoważenie obciążenia na zasadzie pull, a nie push

Nie stosować ●









potrzebujemy łatwej priorytyzacja potrzebujemy łatwej integracji z systemami zastanymi chcemy utrzymać luźne sprzężenie





jak oczekujemy wyniku jeżeli nie ma pewności, że operacja się powiedzie kiedy operacja ma być częścią większej transakcji kiedy nie ufamy klientom (można samemu udoskonalić) w małych aplikacjach, gdzie pośrednik może być zbyt zasobożerny jak potrzeba obiektowości i silnych typów system ma być prosty i łatwy w testowaniu i debugowaniu

konieczna jest wysoka niezawodność przy słabym łączu sieciowym potrzebna jest komunikacja wiele do wiele

23