[转载]使用 Apache Geronimo 和 JMS 构建事件驱动的框架
使用 Apache Geronimo 和 JMS 構(gòu)建事件驅(qū)動(dòng)的框架
能夠及時(shí)響應(yīng)實(shí)時(shí)信號(hào)和事件成為企業(yè)框架的最重要方面之一。本文講述了 Apache Geronimo 框架提供的技術(shù)和工具,使應(yīng)用程序和服務(wù)能夠有效地對(duì)這些信號(hào)和事件作出響應(yīng),并將其作為消息傳播到駐留在平臺(tái)虛擬層之間的相關(guān)組件中。這些技術(shù)(包括面 向服務(wù)的架構(gòu) (SOA) 和使用 Java? 反射的有效事件驅(qū)動(dòng)交互框架)有助于降低設(shè)計(jì)有效事件驅(qū)動(dòng)的軟件系統(tǒng)的復(fù)雜性,同時(shí)可以增加靈活性。
當(dāng)實(shí)時(shí)更改和事件發(fā)生時(shí),對(duì)其作出響應(yīng)是企業(yè)框架的重要需求。本文介紹了 Apache Geronimo 框架中采用的技術(shù)和機(jī)制,它們使應(yīng)用程序和服務(wù)能夠有效地響應(yīng)實(shí)時(shí)刺激,然后跨架構(gòu)虛擬層發(fā)送和接收事件。
開(kāi)發(fā)人員試圖使用傳統(tǒng)順序處理方法設(shè)計(jì)和構(gòu)建動(dòng)態(tài)工作流和集成系統(tǒng)時(shí)會(huì)遇到麻煩,因此,急需更適合的、事件感知的技術(shù)和工具。SOA 和事件驅(qū)動(dòng)的編程可以解決這一復(fù)雜的難題。
SOA 給出一個(gè)松散耦合的開(kāi)發(fā)模型和運(yùn)行時(shí)環(huán)境。它使服務(wù)提供者和服務(wù)消費(fèi)者能夠使用動(dòng)態(tài)組件交互來(lái)構(gòu)建交互模型,這些交互模型能夠利用該開(kāi)發(fā)模型靈活性和強(qiáng)大 功能。事件驅(qū)動(dòng)的交互模型比傳統(tǒng)同步機(jī)制能更及時(shí)地對(duì)動(dòng)態(tài)事件作出響應(yīng),部分原因是 SOA 中事件驅(qū)動(dòng)的編程利用分布式系統(tǒng)本身需要的許多相同特性,包括專門(mén)化、模塊化和適應(yīng)性。
事件驅(qū)動(dòng)的架構(gòu)
2003 年,Gartner Group 引入事件驅(qū)動(dòng)的架構(gòu) (EDA) 作為一種構(gòu)建系統(tǒng)、服務(wù)和應(yīng)用程序的方法,在這些所構(gòu)建的東西中,事件在松散耦合的事件接收者之間路由。事件驅(qū)動(dòng)的系統(tǒng)由事件生產(chǎn)者 和事件接收者 組成。事件生產(chǎn)者可以將事件發(fā)布到事件通道,后者可以將事件分發(fā)到訂閱事件的接收者。 與生產(chǎn)者發(fā)布事件一樣,事件通道將事件轉(zhuǎn)發(fā)給接收者。如果沒(méi)有可用的接收者,事件通道會(huì)將事件存儲(chǔ)起來(lái),然后將其轉(zhuǎn)發(fā)到稍后可用的接收者。此過(guò)程稱為存儲(chǔ)和轉(zhuǎn)發(fā)。
EDA 使用傳遞消息概念作為兩個(gè)或多個(gè)實(shí)體之間交互的方法。通過(guò)觸發(fā)對(duì)應(yīng)于某些業(yè)務(wù)領(lǐng)域事件的信號(hào)和消息,來(lái)啟動(dòng)交互。當(dāng)每個(gè)給定事件發(fā)生時(shí),會(huì)通知該事件的所有訂閱者。然后訂閱者可以對(duì)事件采取行動(dòng)。
EDA 受益于以下屬性:
- 無(wú)耦合的關(guān)聯(lián):事件發(fā)布者和事件訂閱者預(yù)先無(wú)需知道彼此的存在。
- 多對(duì)多的交互:一個(gè)或多個(gè)事件會(huì)影響一個(gè)或多個(gè)訂閱者。
- 基于事件的控制流程:當(dāng)應(yīng)用程序響應(yīng)發(fā)生的事件時(shí),應(yīng)用程序流程非常自然。
- 異步消息傳遞:業(yè)務(wù)邏輯可以隨事件同時(shí)發(fā)生。
通過(guò)圍繞 EDA 構(gòu)建應(yīng)用程序和系統(tǒng),您可以用使其更具響應(yīng)性的方式構(gòu)建它們,因?yàn)橥ㄟ^(guò)設(shè)計(jì),事件驅(qū)動(dòng)系統(tǒng)更適用于不可預(yù)知且不斷更改的環(huán)境。
事件驅(qū)動(dòng)設(shè)計(jì)和開(kāi)發(fā)的優(yōu)點(diǎn)
事件驅(qū)動(dòng)的編程有許多優(yōu)點(diǎn)。例如,此類編程可以:
- 減少開(kāi)發(fā)和維護(hù)分布式系統(tǒng)的復(fù)雜性。
- 使得應(yīng)用程序和服務(wù)的裝配和配置更加容易且成本更低。
- 促進(jìn)源代碼和組件重用,從而減少 bug 并促進(jìn)敏捷的開(kāi)發(fā)和部署。
短期內(nèi),事件驅(qū)動(dòng)的設(shè)計(jì)和開(kāi)發(fā)允許更加快速、容易的定制。長(zhǎng)期內(nèi),系統(tǒng)狀態(tài)更加精確。
|
EDA 和 SOA 的結(jié)合
與 順序式或過(guò)程式系統(tǒng)中客戶機(jī)必須輪詢更改請(qǐng)求不同,EDA 允許系統(tǒng)和組件在事件發(fā)生時(shí)實(shí)時(shí)動(dòng)態(tài)地作出響應(yīng)。EDA 通過(guò)引入長(zhǎng)時(shí)間運(yùn)行的處理功能來(lái)彌補(bǔ) SOA 的不足。因?yàn)槭录M(fèi)者在事件發(fā)生時(shí)接收事件,并且會(huì)調(diào)用松散耦合的服務(wù)來(lái)向客戶提供更及時(shí)更精確的數(shù)據(jù),所以對(duì)業(yè)務(wù)有益。
在 EDA 內(nèi)部,您可以跨 SOA 的各個(gè)分段(包括物理層和架構(gòu)的虛擬層)傳輸事件,這樣系統(tǒng)可以有效地作出響應(yīng)。圖 1 說(shuō)明了跨架構(gòu)堆棧各層傳播的事件。
圖 1. 跨虛擬層的傳播
正如您看到的,事件可以因應(yīng)用程序、業(yè)務(wù)、組件、平臺(tái)或系統(tǒng)層的任何更改而發(fā)生,從技術(shù)觀點(diǎn)上講,業(yè)務(wù)事件的級(jí)別自然要比系統(tǒng)事件或組件事件高。
事件的原因(事件因果關(guān)系)是理解該事件的重要因素。事件因果關(guān)系可分為水平因果關(guān)系 和垂直因果關(guān)系。當(dāng)事件發(fā)布者與事件接收者位于架構(gòu)虛擬層中的同一層時(shí),發(fā)生水平因果關(guān)系。當(dāng)事件發(fā)布者與事件接收者位于不同層時(shí),發(fā)生垂直因果關(guān)系。
|
EDA 和事件隊(duì)列
事件驅(qū)動(dòng)的編程是圍繞事件生產(chǎn)者和事件消費(fèi)者之間的無(wú)耦合關(guān)系的概念構(gòu)造的。也就是說(shuō),事件消費(fèi)者不關(guān)心事件發(fā)生的地點(diǎn)或原因;而是關(guān)注事件發(fā)生后它(消費(fèi)者)將被調(diào)用。將事件生產(chǎn)者與事件消費(fèi)者隔離開(kāi)來(lái)的系統(tǒng)和應(yīng)用程序通常依賴于事件分配器或通道。此通道包含事件隊(duì)列,用作事件生產(chǎn)者和事件處理程序之間的中間層。
圖 2 說(shuō)明了生產(chǎn)者、消費(fèi)者、事件通道和主題(或隊(duì)列)之間的關(guān)系。
圖 2. 事件隊(duì)列
事件隊(duì)列的角色是存儲(chǔ)從生產(chǎn)者接收的事件,并在每個(gè)消費(fèi)者可用時(shí)將這些事件傳輸給消費(fèi)者 —— 通常是為了事件被接收。
事件隊(duì)列和主題
多數(shù)事件驅(qū)動(dòng)的系統(tǒng)依賴于預(yù)先構(gòu)建的事件隊(duì)列技術(shù),如面向消息的中間件(Message-Oriented Middleware,MOM)框架。MOM 是一種基于消息隊(duì)列的異步消息模型框架。
MOM 框架的主要優(yōu)點(diǎn)是它能夠無(wú)限期地存儲(chǔ)消息,并在消費(fèi)者準(zhǔn)備接收消息時(shí),將其路由到消費(fèi)者。MOM 按照以下消息模型工作:
- 點(diǎn)對(duì)點(diǎn):此模型基于稱為隊(duì)列 的消息庫(kù),在該模型中,可以將消息從一個(gè)或多個(gè)生產(chǎn)者發(fā)送給單個(gè)消費(fèi)者。
- 發(fā)布/訂閱:此模型基于稱為主題 的消息庫(kù),在該模型中,可以將消息從一個(gè)或多個(gè)生產(chǎn)者發(fā)布給一個(gè)或多個(gè)已訂閱的消費(fèi)者。
圖 3 說(shuō)明了一個(gè)發(fā)布者、事件通道、主題和多個(gè)訂閱了給定消息類型的消費(fèi)者之間的交互。
圖 3. 一個(gè)發(fā)布者、多個(gè)訂閱者、事件通道和主題之間的交互
Java 消息服務(wù) (JMS) 框架是 Java 應(yīng)用程序編程接口 (API) 在 MOM 模型上的抽象。
在 EDA 中使用 JMS
Java 技術(shù)為 Java 程序提供 JMS 作為一種普通方法,用于創(chuàng)建、發(fā)送、接收和讀取消息。JMS 是大多數(shù)消息傳遞系統(tǒng)中的常見(jiàn)概念和語(yǔ)義的接口和類抽象的框架。
通過(guò) JMS 接口,消息生產(chǎn)者和消費(fèi)者能夠以點(diǎn)對(duì)點(diǎn)或發(fā)布/訂閱模型發(fā)送和接收消息。下面的列表顯示了 JMS 中的主要組件:
- ConnectionFactory:該對(duì)象用于創(chuàng)建 JMS 連接
- Connection:這是到 JMS 系統(tǒng)的連接
- Destination:消息主題或消息隊(duì)列的抽象
- Session:發(fā)送或接收消息所在的上下文
- MessageProducer:會(huì)話創(chuàng)建的組件,用于將消息發(fā)送到目的地
- MessageConsumer:會(huì)話創(chuàng)建的組件,用于從目的地接收消息
|
使用 Geronimo 和 JMS 的簡(jiǎn)單事件框架
Apache Geronimo 與 Active MQ 開(kāi)放源碼消息提供程序綁定在一起。Active MQ 支持 JMS,因此為圍繞 Geronimo 框架構(gòu)建的應(yīng)用程序提供一種方法來(lái)以充分利用 JMS 的消息傳遞功能。
以 下各節(jié)定義了使用 Geronimo、Active MQ 和 JMS 的概念和語(yǔ)義構(gòu)建的簡(jiǎn)單事件框架。在這些小節(jié)中定義的事件框架包括事件通道、事件發(fā)布者和事件接收者。事件通道負(fù)責(zé)注冊(cè)和取消注冊(cè)事件接收者,并負(fù)責(zé)將事 件消息從事件發(fā)布者以匿名方式路由到事件接收者。此框架給出的惟一概念是事件通道功能,即根據(jù)事件對(duì)象實(shí)現(xiàn)的 Java 類或接口的類型,來(lái)過(guò)濾消息,并將消息路由到適當(dāng)?shù)慕邮照摺?/p>
使用類/接口層次結(jié)構(gòu)過(guò)濾并路由事件
典型的消息傳遞系統(tǒng)允許消息訂閱者根據(jù)點(diǎn)號(hào)分隔的字符串(如 travel.flights.seats 或 travel.lodging.rates )來(lái)定義將接收的事件類型。本文中給出的事件框架還允許訂閱者訂閱特定類型的事件;不過(guò),該事件類型由 Java 類和接口的層次結(jié)構(gòu)定義。
事件框架可以表示為點(diǎn)號(hào)分隔的消息類型層次結(jié)構(gòu),如 圖 4 所示的類層次結(jié)構(gòu)。
圖 4. 事件應(yīng)用程序類關(guān)系
根據(jù)此圖表,訂閱 Event 接口所代表的事件的事件接收者將接收所有事件,而訂閱 FlightEvent 接口所代表的事件的事件接收者將只接收基于該接口或 FlightDelayed 類或 SeatAvailable 類的事件。此設(shè)計(jì)允許事件接收者一次訂閱多個(gè)事件類型。例如,事件接收者通過(guò)使用參數(shù) Event.class 調(diào)用事件通道的 subscribe() 方法,可以訂閱所有事件。如果添加新的事件類型,則事件接收者會(huì)在它們發(fā)布時(shí)自動(dòng)接收它們。
事件通道通過(guò)確定事件接收者訂閱的事件接口的最具體的子類型,來(lái)處理事件層次結(jié)構(gòu)。例如,清單 1 所示的 FlightDelayed 類實(shí)現(xiàn)了 FlightEvent 接口;因此,事件通道將首先查找 FlightDelayed 類的訂閱者,然后查找 FlightEvent 接口等,一直沿著類/接口層次結(jié)構(gòu)向上。
清單 1. FlightDelayed 類
| class TravelEvent extends Event {} class FlightEvent extends TravelEvent {} class LodgingEvent extends TravelEvent {} public class FlightDelayed implements FlightEvent { private String message = ""; public FlightDelayed() { } public FlightDelayed(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } |
事件通道
事件通道 是事件發(fā)布者用于發(fā)布事件,事件接收者用于訂閱和接收事件的組件。簡(jiǎn)單事件通道的接口如 清單 2 所示。
清單 2. 簡(jiǎn)單事件通道的接口
| public interface Channel { void start(); void stop(); void publish(final Event event); void subscribe(final Receiver receiver, final Class eventClass); void unsubscribe(final Receiver receiver, final Class eventClass); } |
|
注意,subscribe() 和 unsubscribe() 方法需要一個(gè) Class 類型的參數(shù),事件通道使用該參數(shù)來(lái)確定接收者將訂閱或取消訂閱哪些類型的事件。
為避免事件接收者輪詢事件何時(shí)發(fā)生,可以通過(guò) Receiver 接口的 receive() 方法調(diào)用事件接收者。每當(dāng)將事件發(fā)布到事件通道時(shí),都可以使用 Java 反射確定哪些訂閱者將接收該事件。然后在這些對(duì)象上調(diào)用 receive() 方法。 清單 3 顯示了一個(gè)簡(jiǎn)單事件接收者。
清單 3. 一個(gè)簡(jiǎn)單事件接收者的實(shí)現(xiàn)
| public class EventReceiver implements Receiver { private static final transient Log log = LogFactory.getLog(EventReceiver.class); private String id = ""; public EventReceiver() { } public EventReceiver(String id) { this.id = id; } public void setId(String id) { this.id = id; } public String getId() { return id; } public void receive(final Event event) { log.info("EventReceiver [" + id + "] received event [" + event.getMessage() + "]"); } } |
清單 4 顯示了事件通道的一個(gè)摘錄。
清單 4. 事件通道的實(shí)現(xiàn)
| public class EventChannel implements Channel { private static final String TOPIC_NAME = "java:comp/env/EventTopic"; private static final String MQ_URL = "tcp://localhost:61616"; private HashMap subscribers = new HashMap(); private TopicConnectionFactory factory = null; private Topic eventTopic = null; private TopicConnection topicConn = null; private TopicSession topicSess = null; private TopicSubscriber topicSubscriber = null; private TopicPublisher topicPublisher = null; private EventConsumer eventConsumer = null; private void handleEvent(Event event) { final Set received = new HashSet(); for (Class eventClass = event.getClass(); Event.class.isAssignableFrom(eventClass); eventClass = eventClass.getSuperclass()) { ArrayList receiverList = new ArrayList(); getReceiversForEvent(getEventLeafInterface(eventClass), receiverList); Receiver[] receivers = new Receiver[receiverList.size()]; receiverList.toArray(receivers); for (int i = 0; i < receivers.length; i++) { invokeOnce(received, receivers[i], event); } } } private void invokeOnce(Set received, Receiver receiver, Event event) { received.add(receiver); receiver.receive(event); } private Class getEventLeafInterface(Class cls) { Class retVal = null; if (Event.class.isAssignableFrom(cls)) { retVal = cls; if (cls.isInterface()) { return retVal; } } Class[] interfaces = cls.getInterfaces(); if (interfaces != null) { for (int i = 0; i < interfaces.length; i++) { if (Event.class.isAssignableFrom(interfaces[i])) { retVal = interfaces[i]; break; } retVal = getEventLeafInterface(interfaces[i]); } } return retVal; } public void start() { try { factory = new ActiveMQConnectionFactory(MQ_URL); topicConn = factory.createTopicConnection(); topicSess = topicConn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE); eventTopic = topicSess.createTopic(TOPIC_NAME); topicSubscriber = topicSess.createSubscriber(eventTopic); topicPublisher = topicSess.createPublisher(eventTopic); eventConsumer = new EventConsumer(this); Thread consumerThread = new Thread(eventConsumer); consumerThread.setDaemon(false); consumerThread.start(); } catch (Exception e) { e.printStackTrace(); } } public void stop() { // close topic connections, sessions, consumers, etc. } public void publish(final Event event) { try { ObjectMessage eventMessage = topicSess.createObjectMessage(); eventMessage.setObject(event); topicPublisher.publish(eventMessage); } catch (Exception e) { e.printStackTrace(); } } public void subscribe(final Receiver receiver, final Class eventClass) { ArrayList receiverList = null; Class leafCls = getEventLeafInterface(eventClass); if (subscribers.get(leafCls) == null) { receiverList = new ArrayList(); subscribers.put(leafCls, receiverList); } else { receiverList = (ArrayList) subscribers.get(leafCls); } if (receiverList.indexOf(receiver) < 0) { receiverList.add(receiver); } } public void unsubscribe(final Receiver receiver, final Class eventClass) { Class leafCls = getEventLeafInterface(eventClass); if (subscribers.get(leafCls) != null) { ArrayList receiverList = (ArrayList) subscribers.get(leafCls); receiverList.remove(receiverList); } } } |
注意: EventChannel 類的完整源代碼可從本文末尾的 下載 部分通過(guò)下載獲得。
清單 5 顯示事件消費(fèi)者的實(shí)現(xiàn)摘錄。
清單 5. 事件消費(fèi)者的實(shí)現(xiàn)
| class EventConsumer implements Runnable, ExceptionListener { private boolean running = false; private boolean stopped = true; private EventChannel eventChannel = null; private EventConsumer(EventChannel eventChannel) { this.eventChannel = eventChannel; } public void run() { log.info("Event Consumer started"); // Create a Topic Connection, Session, and a MessageConsumer for the Topic // loop until stopped and distribute events to the event channel // using the handleEvent method eventChannel.handleEvent(event); stopped = true; log.info("Event Consumer stopped"); } public void shutdown() { running = false; while (stopped == false) { Thread.yield(); } } } |
注意: EventConsumer 類的完整源代碼可以從本文末尾的 下載 部分通過(guò)下載獲得。
在 Geronimo 中部署和運(yùn)行事件框架
事件框架使用部署在 Geronimo 中的 Web 應(yīng)用程序來(lái)測(cè)試每個(gè)事件類型。除了事件框架外,Web 應(yīng)用程序還包括一個(gè)用于輸入事件消息的 HTML 表單和一個(gè)用于接收 HTTP 請(qǐng)求并將內(nèi)容分派到事件通道的 servlet。
HTML 表單(如 圖 5 所示)只允許將三種類型的事件消息發(fā)送到分派 servlet。
圖 5. Web 應(yīng)用程序的開(kāi)始屏幕
事件分派 servlet 實(shí)例化事件通道對(duì)象和三個(gè)示例事件接收者。事件接收者然后訂閱給定的事件,servlet 將事件發(fā)布給事件通道對(duì)象。清單 6 顯示了該 servlet。
清單 6. 分派 servlet 的實(shí)現(xiàn)
| public class SenderServlet extends HttpServlet { private EventChannel eventChannel = null; private EventReceiver allTravelEventReceiver = null; private EventReceiver flightEventReceiver = null; private EventReceiver lodgingEventReceiver = null; public void init() throws ServletException { super.init(); eventChannel = new EventChannel(); eventChannel.start(); // create event receivers allTravelEventReceiver = new EventReceiver("allTravelEventReceiver"); flightEventReceiver = new EventReceiver("flightEventReceiver"); lodgingEventReceiver = new EventReceiver("lodgingEventReceiver"); // subscribe to all Travel events eventChannel.subscribe(allTravelEventReceiver, TravelEvent.class); // subscribe to Flight events eventChannel.subscribe(flightEventReceiver, FlightEvent.class); // subscribe to Lodging events eventChannel.subscribe(lodgingEventReceiver, LodgingEvent.class); } public void destroy() { super.destroy(); // unsubscribe all event receivers and stop the event channel } public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { // respond with input form } public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException, ServletException { String txtMsg = req.getParameter("txtMsg"); if (txtMsg != null && txtMsg.length() > 0) { String flightDelayed = req.getParameter("FlightDelayed"); String rateIncreased = req.getParameter("RateIncreased"); String seatAvailable = req.getParameter("SeatAvailable"); if (flightDelayed != null) { // send a Flight event eventChannel.publish(new FlightDelayed(txtMsg)); } else if (rateIncreased != null) { // send a Lodging event eventChannel.publish(new RateIncreased(txtMsg)); } else if (seatAvailable != null) { // send a Flight event eventChannel.publish(new SeatAvailable(txtMsg)); } } doGet(req, res); } } |
注意: SenderServlet 類的完整源代碼可以從本文末尾的 下載 部分通過(guò)下載獲得。
事件分派 servlet 調(diào)用的事件框架的應(yīng)用程序流程如 圖 6 所示。
圖 6. 事件 Web 應(yīng)用程序的順序
部署應(yīng)用程序
事件框架的類和 Web 應(yīng)用程序打包在 .war 文件中,并放置在 GERONIMO_HOME/deploy 目錄下。對(duì)于創(chuàng)建并復(fù)制到 deploy 目錄下的 .war 文件,Geronimo 在啟動(dòng)時(shí)會(huì)自動(dòng)部署它。放置在 deploy 目錄下的應(yīng)用程序是熱加載的,當(dāng)發(fā)生更改時(shí),Geronimo 能夠在運(yùn)行時(shí)重新加載應(yīng)用程序。這使調(diào)試應(yīng)用程序變得非常便利。
運(yùn)行應(yīng)用程序
您可以使用位于 GERONIMO_HOME/bin 目錄下的啟動(dòng)腳本(startup.bat 或 startup.sh)啟動(dòng) Geronimo 應(yīng)用服務(wù)器。當(dāng)調(diào)用 Geronimo 啟動(dòng)腳本時(shí),會(huì)出現(xiàn) Geronimo 控制臺(tái)窗口。對(duì)于部署的事件框架的 Web 應(yīng)用程序,啟動(dòng)時(shí)出現(xiàn)的 Geronimo 控制臺(tái)窗口將包含類似于 清單 7 所示的行,確認(rèn) Web 應(yīng)用程序已成功啟動(dòng)。
清單 7. Web 應(yīng)用程序的成功啟動(dòng)
| 00:12:33,921 INFO [EventChannel] Starting EventChannel... 00:12:33,937 INFO [EventChannel] Creating topic connection... 00:12:35,062 INFO [EventChannel] EventChannel started 00:12:35,062 INFO [EventChannel] Event Consumer started 00:12:35,093 INFO [SenderServlet] AllTravelEventReceiver [com.jeffhanson.eda.EventReceiver@f84033] 00:12:35,093 INFO [SenderServlet] FlightEventReceiver [com.jeffhanson.eda.EventReceiver@3ee73b] 00:12:35,093 INFO [SenderServlet] LodgingEventReceiver [com.jeffhanson.eda.EventReceiver@16127f4] |
將事件發(fā)送給 servlet 后,servlet 會(huì)將它發(fā)布給事件通道。如果將包含文本 Flight 2365 to Detroit will be delayed 15 minutes 的 Flight-Delayed 消息發(fā)送給 servlet,則 Geronimo 控制臺(tái)窗口會(huì)顯示 類似于 清單 8 的信息。
清單 8. 成功的事件發(fā)布
| 00:12:53,718 INFO [SenderServlet] >>>>> 00:12:53,718 INFO [SenderServlet] >>>>> 00:12:53,734 INFO [EventChannel] Publishing event [com.jeffhanson.eda.events.business.FlightDelayed@863854] 00:12:53,859 INFO [EventReceiver] EventReceiver [flightEventReceiver] received event [Flight 2365 to Detroit will be delayed 15 minutes] 00:12:53,859 INFO [EventReceiver] EventReceiver [allTravelEventReceiver] received event [Flight 2365 to Detroit will be delayed 15 minutes] |
|
結(jié)束語(yǔ)
設(shè) 計(jì)能夠?qū)?shí)時(shí)更改和事件作出及時(shí)響應(yīng)的有效事件驅(qū)動(dòng)軟件系統(tǒng)是一項(xiàng)復(fù)雜工作。結(jié)合使用 SOA 與使用 Java 反射的有效事件驅(qū)動(dòng)的交互框架可以減少?gòu)?fù)雜性,并增加靈活性。Geronimo 平臺(tái)提供了 API 和工具(包括 JMS 提供程序),可以用來(lái)構(gòu)建功能強(qiáng)大的事件驅(qū)動(dòng)的交互框架。
從線性企業(yè)編程轉(zhuǎn)移到面向服務(wù)的設(shè)計(jì)只能帶來(lái)適用于 SOA 模型的優(yōu)勢(shì)。重構(gòu)系統(tǒng)以獲得業(yè)務(wù)服務(wù)會(huì)導(dǎo)致服務(wù)和組件的模塊化框架。如果服務(wù)基礎(chǔ)設(shè)施的交互模型敏捷而可擴(kuò)展,那么您可以將這些組件重用于多種不同的應(yīng)用 程序。SOA、EDA 和 Apache Geronimo 為功能強(qiáng)大的有效軟件基礎(chǔ)設(shè)施提供了基礎(chǔ)。
來(lái)自 “ ITPUB博客 ” ,鏈接:http://blog.itpub.net/374079/viewspace-130290/,如需轉(zhuǎn)載,請(qǐng)注明出處,否則將追究法律責(zé)任。
轉(zhuǎn)載于:http://blog.itpub.net/374079/viewspace-130290/
總結(jié)
以上是生活随笔為你收集整理的[转载]使用 Apache Geronimo 和 JMS 构建事件驱动的框架的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 软文写作是什么?如何写软文?软文标题怎样
- 下一篇: clickhouse SSB 性能测试