在企业应用程序中,大多数处理是以同步方式完成的。客户端调用业务服务并等待业务服务从处理返回。但是,某些用例中的业务处理需要相当多的时间和资源。业务处理甚至可能跨越应用程序,可能与企业内外的应用程序集成。对于这些长期存在的进程,应用程序客户端等待业务处理完成是不可行的,在这种情况下,我们希望异步调用服务。
一些长期运行的用例需要很长时间。我们可以异步运行,而不是阻塞用户。
JMS是 Service Activator Pattern的 一个很好的例子 - JMS(Java Message Service)是一种API,它提供了创建,发送和读取消息的工具。 它提供松散耦合的可靠和异步通信。 为简单起见,这种模式分为若干部分,如问题,效力,解决方案等。
问题
(问题部分描述了开发人员面临的设计问题)
您想要异步调用服务。
动因
(动因列表突出了人们可能选择使用模式并提供使用模式的理由的原因)
解决方案
(此处解决方案部分简要介绍了解决方案方法,并详细介绍了解决方案元素) 使用Service Activator接收异步请求并调用一个或多个业务服务。Service Activator实现为可以侦听和接收JMS消息的 JMS侦听器 和委托服务。
这种模式如何运作?
任何需要异步调用业务服务的客户端(例如EJB或POJO服务)都会创建消息并将其发送到Service Activator。Service Activator接收消息并解析它以解释客户端请求。 一旦客户端的请求被解组,Service Activator就会识别并找到正确的业务服务组件并调用它来异步处理客户端的请求。处理完成后,客户端可能希望接收结果。为了通知客户端处理结果,Service Activator可以向客户端发送响应。此响应可以向客户端指示处理是否成功并提供结果或结果句柄。如果处理失败,响应可以包括故障的细节。详细信息可以指示客户端如何恢复,无论是通过重新提交请求还是通过纠正导致失败的问题。Service Activator可能使用服务定位器来定位业务组件。
结构体
我使用UML类图来显示解决方案的基本结构,本节中的UML序列图介绍了解决方案的动态机制。下面是表示服务定位器模式(Service Locator pattern)的关系的图表 。
类图
序列图
参与者和责任
客户端 - 客户端是应用程序中需要以异步方式调用业务服务的任何客户端。客户端可以是可以创建和发送JMS消息的任何类型的应用程序组件,例如POJO或EJB组件。
请求 - 请求是客户端创建的消息对象,并使用面向消息的中间件(MOM)发送到ServiceActivator。根据JMS规范,Request对象必须实现javax .jms.Message接口。JMS API提供了几种消息类型,例如TextMessage,ObjectMessage等,它们可以用作请求对象,具体取决于您要发送的消息类型。
ServiceActivator - ServiceActivator是模式的主要类。它实现了javax .jms.MessageListener接口,该接口由JMS规范定义。ServiceActivator实现了一个onMessage()方法,该方法在新消息到达时调用。ServiceActivator解析(解组)消息(请求)以确定需要执行的操作。ServiceActivator可能使用服务定位器来查找或创建BusinessService组件。
BusinessService - BusinessService是客户端要求进行异步处理的目标对象。
响应 - 响应是由ServiceActivator或BusinessService创建和发送的消息对象。响应可以是让客户知道已收到请求的确认。响应也可以是异步处理的结果。
示例代码
考虑一个订单处理应用程序,客户在线购物,订单履行过程在后台发生。在某些情况下,订单履行可能会外包给第三方仓库。在这种情况下,在线商店需要异步调用这些履行服务。这是一个演示使用点对点(PTP)消息传递来完成异步处理的示例。但是,使用发布/订阅消息传递与此类似,只是使用了主题而不是队列。选择使用哪种方法,PTP或发布/订阅,取决于业务和应用程序要求,超出了此模式的范围。
第1步: 创建 Order Service Activator。
<b>public</b> <b>class</b> OrderServiceActivator implements javax.jms.MessageListener{ <font><i>// Queue session and receiver: see JMS API for</i></font><font> </font><font><i>// details</i></font><font> <b>private</b> QueueSession orderQueueSession; <b>private</b> QueueReceiver orderQueueReceiver; </font><font><i>// Note: values should come from property files or </i></font><font> </font><font><i>// environment instead of hard coding.</i></font><font> <b>private</b> String connFactoryName = </font><font>"PendingOrdersQueueFactory"</font><font>; <b>private</b> String queueName = </font><font>"PendingOrders"</font><font>; </font><font><i>// use a service locator to locate administered</i></font><font> </font><font><i>// JMS components such as a Queue or a Queue </i></font><font> </font><font><i>// Connection factory</i></font><font> <b>private</b> JMSServiceLocator serviceLocator; <b>public</b> OrderServiceActivator(String connFactoryName, String queueName) { <b>super</b>(); <b>this</b>.connFactoryName = connFactoryName; <b>this</b>.queueName = queueName; startListener(); } <b>private</b> <b>void</b> startListener() { <b>try</b> { serviceLocator = <b>new</b> JMSServiceLocator (connFactoryName); qConnFactory = serviceLocator.getQueueConnectionFactory(); qConn = qConnFactory.createQueueConnection(); </font><font><i>// See JMS API for method usage and arguments</i></font><font> orderQueueSession = qConn.createQueueSession (...); Queue ordersQueue = serviceLocator.getQueue(queueName); orderQueueReceiver = orderQueueSession.createReceiver(ordersQueue); orderQueueReceiver.setMessageListener(<b>this</b>); } <b>catch</b> (JMSException excp) { </font><font><i>// handle error</i></font><font> } } </font><font><i>// The JMS API specifies the onMessage method in the</i></font><font> </font><font><i>// javax.jms.MessageListener interface. </i></font><font> </font><font><i>// This method is asynchronously invoked </i></font><font> </font><font><i>// when a message arrives on the Queue being</i></font><font> </font><font><i>// listened to by the ServiceActivator. </i></font><font> </font><font><i>// See JMS Specification and API for more details.</i></font><font> <b>public</b> <b>void</b> onMessage(Message msg) { <b>try</b> { </font><font><i>// parse Message msg. See JMS API for Message.</i></font><font> ... </font><font><i>// Invoke business method on an enterprise</i></font><font> </font><font><i>// bean using the bean's business delegate.</i></font><font> </font><font><i>// OrderProcessorDelegate is the business </i></font><font> </font><font><i>// delegate for OrderProcessor Session bean.</i></font><font> </font><font><i>// See Business Delegate pattern for details. </i></font><font> OrderProcessorDelegate orderProcDeleg = <b>new</b> OrderProcessorDelegate(); </font><font><i>// Use data values from the parsed message to</i></font><font> </font><font><i>// invoke business method on bean via delegate</i></font><font> orderProcDeleg.fulfillOrder(...); </font><font><i>// send any acknowledgement here...</i></font><font> } <b>catch</b> (JMSException jmsexcp) { </font><font><i>// Handle JMSExceptions, if any</i></font><font> } <b>catch</b> (Exception excp) { </font><font><i>// Handle any other exceptions</i></font><font> } } <b>public</b> <b>void</b> close() { <b>try</b> { </font><font><i>// cleanup before closing</i></font><font> orderQueueReceiver.setMessageListener (<b>null</b>); orderQueueSession.close(); } <b>catch</b>(Exception excp) { </font><font><i>// Handle exception - Failure to close</i></font><font> } } } </font>
第2步: 会话Facade作为Service Activator的客户端。
示例会话Facade代码负责将订单分派给此异步服务。
<b>public</b> <b>class</b> OrderDispatcherFacade implements javax.ejb.SessionBean { ... <font><i>// business method to create new Order</i></font><font> <b>public</b> <b>int</b> createOrder(...) throws OrderException { </font><font><i>// create new business order entity bean</i></font><font> ... </font><font><i>// successfully created Order. send Order to</i></font><font> </font><font><i>// asynchronous backend processing </i></font><font> OrderSender orderSender = <b>new</b> OrderSender(); orderSender.sendOrder(order); </font><font><i>// close the sender, if done...</i></font><font> orderSender.close(); </font><font><i>// other processing</i></font><font> ... } } </font>
第3步: OrderSender:用于将订单分派到队列。
<font><i>// imports...</i></font><font> <b>public</b> <b>class</b> OrderSender { </font><font><i>// Queue session and sender: see JMS API for details</i></font><font> <b>private</b> QueueSession orderQueueSession; <b>private</b> QueueSender orderQueueSender; </font><font><i>// These values could come from some property files</i></font><font> <b>private</b> String connFactoryName = </font><font>"PendingOrdersQueueFactory"</font><font>; <b>private</b> String queueName = </font><font>"PendingOrders"</font><font>; </font><font><i>// use a service locator to locate administered</i></font><font> </font><font><i>// JMS components such as a Queue or a Queue.</i></font><font> </font><font><i>// Connection factory</i></font><font> <b>private</b> JMSServiceLocator serviceLocator; ... </font><font><i>// method to initialize and create queue sender</i></font><font> <b>private</b> <b>void</b> createSender() { <b>try</b> { </font><font><i>// using ServiceLocator and getting Queue</i></font><font> </font><font><i>// Connection Factory is similar to the</i></font><font> </font><font><i>// Service Activator code.</i></font><font> serviceLocator = <b>new</b> JMSServiceLocator (connFactoryName); qConnFactory = serviceLocator.getQueueConnectionFactory(); qConn = qConnFactory.createQueueConnection(); </font><font><i>// See JMS API for method usage and arguments</i></font><font> orderQueueSession = qConn.createQueueSession (...); Queue ordersQueue = serviceLocator.getQueue(queueName); orderQueueSender = orderQueueSession.createSender(ordersQueue); <b>catch</b>(Exception excp) { </font><font><i>// Handle exception - Failure to create sender</i></font><font> } } </font><font><i>// method to dispatch order to fulfillment service</i></font><font> </font><font><i>// for asynchronous processing</i></font><font> <b>public</b> <b>void</b> sendOrder(Order newOrder) { </font><font><i>// create a new Message to send Order object</i></font><font> ObjectMessage objMessage = queueSession.createObjectMessage(order); </font><font><i>// set object message properties and delivery </i></font><font> </font><font><i>// mode as required.</i></font><font> </font><font><i>// See JMS API for ObjectMessage</i></font><font> </font><font><i>// Set the Order into the object message</i></font><font> objMessage.setObject(order); </font><font><i>// send the message to the Queue</i></font><font> orderQueueSender.send(objMessage); ... } <b>catch</b> (Exception e) { </font><font><i>// Handle exceptions</i></font><font> } ... } ... <b>public</b> <b>void</b> close() { <b>try</b> { </font><font><i>// cleanup before closing</i></font><font> orderQueueReceiver.setMessageListener (<b>null</b>); orderQueueSession.close(); } <b>catch</b>(Exception excp) { </font><font><i>// Handle exception - Failure to close</i></font><font> } } } </font>
典型用例
结果