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