- The good choice on the scenario
Q: What messaging technology is most appropriate for guranteeing the delivery of a message to a single recipient and to multiple recipients?
A: For the single recipient, use the point to point messaging model with persistent delivery mode. For multiple recipients, use the publish/subscribe model with a persistence delivery mode and a durable subscriber.
Q : What messaging technology is most appropriate for sending and receiving messages in a transactional way?
A: Use the point-to-point model. Create transacted sessions and process messages with commit and rollback methods.
- The Benifits of Synchronous and Asynchronous Message Communication
Synchronous ,client與服務端必須同時都是active,如果有一方不是active那麼整個訊息傳遞的交易便會失敗,而且每一個Message處理完畢時,必須發出acknowledgement的通知,否則服務端將會認為這個Message尚未被處理,則無法往下一個待處理的Message進行運作。
Asynchronous,當交易量開始大量激增以後(這裡應該指的是說訊息傳遞需求日益趨增),非同步的傳遞訊息機制就變得更顯重要了,可以選擇使用背景管理處理這些訊息需求,並且控制最大訊息服務數量,這樣也就不受到系統軟硬體、網路損毀的狀況,如果當瞬間服務量達到最大上限時,Message information不會消失,會被延遲直到有可進行服務的時候再次發布message。
何時是選擇使用Synchronous Messaging 的最佳時機?
You are using a credit card authorization/login authentication system to send a message in which the response to the message is required before the transaction can be completed.
- JMS Exception Handling在Exception的處理中,ap開發人員必須實作一個ExceptionListener,並使用onException( JMSException exception )來處理錯誤問題內容。
- JMS Components
[caption id="attachment_457" align="alignnone" width="700" caption="JMS Component"]
- JMS Session Detail
- JMS Body
[caption id="attachment_461" align="alignnone" width="504" caption="JMS_Body"]
- JMS組成的三大結構
JMS是由Header / Properties / Body 三者組成,其中又以Header為必要構成條件,其餘兩者則為optional,在header當中包含了多組欄位資訊,以供標示出是什麼樣的訊息以及要傳遞到何處,這些欄位都必須有accessor/mutator,通常data filed value都是從client端發送時就已經填值了。
Properties的部分,他是一組可以加諸在Headers區塊裡面的資訊,JMS API已經有定義好一部分的property提供使用,這些都是以JMS*開頭的,資料型別僅支援java原生資料型態 but no char。這些fields可以利用MessageSelector來做過濾挑選出哪些Message使用。
Body的部分,有高達5種不同的body format or type,提供JMS client來傳遞或接收不同的訊息格式。
Message Body Content
[caption id="attachment_462" align="alignnone" width="700" caption="Message Body"]
- JMS interface
[caption id="attachment_463" align="alignnone" width="700" caption="JMS interface"]
- Required Component of JMS application
在JMS的組成當中,最重要的是Administered Object,可以透過JNDI去取得,而JMS有兩種administered Object type,分別是Desitination以及ConnectionFactory。Desitination Type包含了有JMS provider 的設定檔資訊,Client端藉由這些object用來得知向何處送發訊息,或者得知準備從何處接收訊息。
Destination interfae 也分有兩大類:Queue & Topic,Queue : 用來作為point to point model,Topic : 用來作為 發布者(publisher)與訂閱者(subscriber)模式。
ConnectionFactory則是透過JNDI 可以取得,而且也包含了有連線設定資訊,管理ip address功能,並允許JMS client建立與JMS server 之間連線的功能。
只有這些嗎? 還有Conneciton and Session object。Connection object 負責實際的連線建立,而session object則是負責管理連線後的訊息傳遞、交易管理、以及message ack等等功用。
[caption id="attachment_465" align="alignnone" width="505" caption="JMS object"]
- Create and Receive Messages
JMS提供了4個類別用來處理傳遞與接收訊息:MessageProducer / MessageConsumer / MessageSelector / MessageListener。
MessageProducer是被session給建立的,用來向desitination傳遞訊息使用,在ptp model ,dest 是Queue,在pub/sub model , dest 則是Topic,同時也可以透過setDeliveryMode來決定是否為PERSISTENT or NON_PERSISTENT,通常這只是選擇是否要記log,而log方式很多一般常見的是寫入db(當然也可以設定優先權 setPriority)。
MessageConsumer也是被session給建立的,他是des用來接收所收到的訊息使用,接收訊息的模式還區分了有兩大類,一個是synchronously,另一個是asynchronously,選擇同步接收方式對應同步非同步則有兩個method可用(receive or receivenowat ),而當consumer建立後,client 會註冊一個MessageListener,之後consumer則會開始進行運作。
MessageListener是用在asynchronous MessageConsumer的運作中的,而使用上必須實作MessageListener - onMessage()。
MessageSelector是用在client端的物件,他的組成是基於SQL92的子集合,藉由指定過濾條件,向MessageHeader中尋求所想要的Message。
[caption id="attachment_466" align="alignnone" width="700" caption="Message Selector example"]
PTP 的運作模式
[caption id="attachment_467" align="alignnone" width="521" caption="PTP processing model"]
- 透過JNDI取得QueueConnectionFactory。
- 從qcfactory中建立QueueConnection,如果有安全性需求,則需要輸入相關的帳號密碼。
- 從qc中取得QueueSession。
- 從JNDI取得queue
- 透過Queuesession建立QueueSender or QueueReceiver
- Send or Receive the message
- Close the QueueConnection,這同時也會自動的關閉QueueSender / QueueReceiver / QueueSession
Sample code 參考 ch08 - page 474
- Publish / Subscribe Model
[caption id="attachment_468" align="alignnone" width="489" caption="pub-sub model"]
以下是 Pub / Sub communication Model
- 透過JNDI取得TopicConnectionFactory
- 透過tcf取得 TopicConnection
- 透過tc 取得TopicSession
- 透過JNDI 取得topic
- 透過topSession 去為這個topic Create TopicPublisher or TopicSubscriber
- public or receive messages
- close pub / sub / session / connection
- MDB - Without writing any thread-safe mechanism
撰寫MDB有個最大的好處就是,完全不需要去寫關於Thread-safe的程式碼,再也不用擔心 static , synchronized 在設計上的思維瑕疵,寫程式時只需要以撰寫Single Thread的角度去做就好,Container會自己幫你cover掉Concurrent user的 multi-thread問題。但以這角度來看意思就是,請不要在MDB中撰寫Multi-thread,否則程式將會發展到無法預期的情境。
- Integrating JMS with EJB
JMS-EJB是一個相當引人注目的( compelling )想法,藉由傳遞message讓client 可以不受到lock機制綁定(需要等待response),進而達成非同步化的作業。
如果要來處理MDB,與其要用一種全新的型態的EJB來處理,Java社群最後還是決定了要使用一個簡單的Java Object就可以用來接收Messages,同時也可以呼叫適當配合的SessionBean , EntityBeans,這樣雖好但也有一些問題存在。
- 必須自己實做一些程式碼用來作為接收JMS messages的Listener。
- 為了提高處理JMS 的產能,要天殺地自己寫Multi-threading,雖然學好多執行緒開發是好的工程師該做的事情,但能不要搬石頭砸自己的腳則是更聰明的選擇方式。
- 接收各項<Message request之前,必須確保這個程式是可以自己活起來的,當然有可能我們是放在某個Container裡面,而這程式又包含了各種ejb,那麼整個程式就會變得跟Container vendor 綁得很死,若要porting時會是一場惡夢。
- 既然是想要以一個單純的POJO來闖蕩MDB海,那麼關於生命週期控管、交易管理、clustering、 pooling也必須自己上,那我乾脆去container vendor上班就好啦。
- 最後一個缺點是要把JMS desitination name給寫死,這樣以後若要重複使用就沒啥機會了,換一個改一次。
好吧那看過這樣自己上的慘劇以後,或許可以考慮用既有的EJB來作變形,不過也有一些問題要解決:
- Threading - 如果一個bean正在處理送來的message request,要如何處理接續而來的下一個需求,尤其是在ejb - bean class 當中不支援多執行緒的開發狀況。
- 如果JMS Message剛好傳來,可是container裡面就正好沒有bean可以提供服務,那麼container該如何知道趕快去生一個bean給人用?
- What is a Message-Driven Bean ?
Decoupled from ny clients that send messages to it. Client端無法直接透過Bean interface 存取到MDB ,頂多只能使用JMS API來傳遞訊息,MDB沒有local/remote business interface。
MDB可以接受不同的類型的client message,前提是只要送來的訊息只要都有實做JCA 提供的message inflow,那麼就不管是任何平台的message都可以收了(message同時支援同步與非同步兩種模式),Listener基本上需要實作javax.jms.MessageListener,其中也只有一個method - void onMessage()。onMessage可接受的物件是JMS Message,他有相關實作品像是 BytesMessage / ObjectMessage / TextMessage / StreamMessage / MapMessage,在runtime時可透過 instanceof 來做判斷並進行相應符合的作業流程。
MDB不一定會把Exception發生時的問題送還給前端的產生者,因為在考量到非同步的訊息傳遞時,基本上前端訊息產生者根本不會等待執行結果,而在MDB身上,本身就僅支援系統產生的exception - javax.rmi.RemoteException,但事實上server本身會自動處理,所以也不需要主動宣告拋出此錯誤。
MDB是Statless,跟Stateless SessionBean很像,container可以處理多個messages by multi mdb instances。而每一個mdb instance都是Single-threaded,Container必須負責將訊息序列化傳入給mdb來進行服務處理,但一次僅只一個,所以不需要設計synchronized block來做系統資源防護的動作,container會以pooling概念來負責達成這樣的議題需求。
JMS MDB並不是自己就可以活得好好的來到處拉客人接生意(receive message),事實上是每個提供Messaging Service的Server Destination都會自己收訊息,藉由server接收訊息然後將訊息傳遞給mdb來服務,這裡還包括了兩種模式- durable & non-durable,durable是表示說萬一沒收到訊息時,會將訊息persist起來等desitination再次服務時,再將message轉為object再傳遞給mdb處理,而 non-durable則是沒接到訊息也無所謂。
以下是相關範例程式參考
[caption id="attachment_369" align="alignnone" width="425" caption="MessageListener interface"]
[caption id="attachment_370" align="alignnone" width="588" caption="MessageDrivenBean interface"]
在MessageDrivenBean中的 setMessageDrivenContex()算是一個call back API,container會在bean instance建立完成後,自動呼叫這個method。而ejbRemove雖然在 ejb 2.1之前都是必要的 call back API,可是整體來說在3.0這裡以後所有的call back就不再是必要存在的了,他可以透過注入@Postconstruct (等同於setMessageDrivenContex) and @PreDestroy ( 等同於ejbRemove)來達成。
MDB Life-cycle
[caption id="attachment_371" align="alignnone" width="575" caption="mdb life cycle"]
Sample MDB
[caption id="attachment_372" align="alignnone" width="684" caption="sample mdb"]
A message-driven bean can register itself with the EJB Timer Service for
time-based notifications by implementing the javax.ejb.TimedObject
interface apart from the message listener interface, or by declaring a timeout
callback method with the @Timeout annotation. The container will invoke the
bean instance’s ejbTimeout() method or the timeout callback upon timer
expiration.
DD參考
[caption id="attachment_374" align="alignnone" width="700" caption="DD"]
- Advanced Concepts
MDB不會跟Message Producer使用同一條的transaction。
JMS MDB 不會收到Message Producer的任何安全性資訊,像是是誰發出來的訊息,所以也就不能把其他ejb security control模式放在mdb身上使用。相較於Session Bean的Push Model,MDB則是一個Pull Model機制。
在Load-Balancing的架構中,container server 其實都會以 pooling的概念來一次存放多個mdb instance ( with same type),先考慮單一Server情形下,假設一個訊息傳入了,基本上只會有一個mdb instance serve it,不會有concurrent processing問題,而若是到了Cluster環境下,每個Container都會自己有一個mdb instance來同時處理message,若是這樣當想要真的只有一個mdb可以處理訊息的話,就改用queue而不要用topic了。
- MDB Exception Handling
MDB的exception基本上是有可能不會拋出app level的錯誤的,唯一有可能的是丟出system level exception,如果是這樣的話,container本身會處理這些exception,並且將mdb instance remove,並進行rollback。
- MDB Message acknowledgement
MDB 的ack處理永遠都是透過container來完成的,ack有兩個method - acknowledge() , rollback()。
acknowledgement還區分了兩種模式 - AUTO_ACKNOWLEDGE(允許傳遞一次)、DUPS_ACKNOWLEDGE(允許在傳遞訊息失敗後再次進行),特別要注意到的是,假設MDB的 transaction attribute設定為Required,那麼container 就會將整個onMessage() 包入交易當中。
- ..
沒有留言:
張貼留言