`
alienj
  • 浏览: 79931 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

第22章. 异步和消息

阅读更多

22章. 异步和消息

 

Seam 使异步执行来自网页请求的工作变得非常容易。在多数人在Java EE中考虑异步时,他们想到的是使用JMS。在Seam中,这的确是一种解决问题的方法,并且在你有严格和明确定义的服务质量需求时,这是正确的方法。 Seam利用Seam组件使发送和接收JMS消息变得非常容易。

但是对多数用例而言,用JMS就太夸张了。Seam在你的调度器(dispatchers) 选择层之上分层了一个简单的异步方法和事件机能:

  • java.util.concurrent.ScheduledThreadPoolExecutor (默认)
  • the EJB timer service (对 EJB 3.0 环境)
  • Quartz

 

22.1. 异步

 

异步事件和方法调用具有与基本的分配器机制一样期望的相同服务质量。基于ScheduledThreadPoolExecutor的默认分配器(dispatcher)能有效地完成任务,但不提供对持久化异步任务的支持,因此不能保证一个任务总会被真正地执行。如果你工作在支持EJB3的环境,增加下列行到components.xml:

<async:timer-service-dispatcher/>

 

那么,你的异步任务会被容器的EJB定时器服务处理。如果你不熟悉定时器服务,不用担心,假如你想在Seam中使用异步方法,你不必直接与它交互。知道的重要事情是任何好的EJB3实现都会有使用持久化定时器的选项,它将保证任务最终会被处理。

另外的一个选择是使用开源Quartz库来管理异步方法。你需要捆绑Quartz库JAR (在lib目录会找到) 在你的EAR中,并application.xml声明它作为一个Java 模块。Quartz分配器可以通过增加一个Quartz 性文件到类目录被配置。它必须被命名为seam.quartz.properties。 此外,你需要增加下列行到components.xml,安装Quartz分配器。

 

<async:quartz-dispatcher/>

 

Seam API使用ScheduledThreadPoolExecutor、 EJB3定时器和Quartz调度器(Scheduler)为默认值很大程度上是相同的。他们可以通过增加一行到components.xml中实现“即插即用”

22.1.1. 异步方法

最简单的形式,一个异步调用仅让来自调用者的方法被异步执行(以不同的线程)。在我们想即时响应客户端时,我们常使用一个异步调用,并让一些费时的工作在后台处理在使用AJAX的应用程序中这个模式运行良好,客户端可以自动轮询服务器的工作成果。

EJB组件,我们注释本地接口来指定一个方法被异步执行。

@Local

 

public interface PaymentHandler

 

{

 

    @Asynchronous

 

    public void processPayment(Payment payment);

 

}

(对JavaBean组件 ,如果我们喜欢,我们可以注释组件实现类)

bean类而言,异步的使用是透明的:

@Stateless

 

@Name("paymentHandler")

 

public class PaymentHandlerBean implements PaymentHandler

 

{

 

    public void processPayment(Payment payment)

 

    {

 

        //do some work!

 

    }

 

}

并对客户端也是透明的:

@Stateful

 

@Name("paymentAction")

 

public class CreatePaymentAction

 

{

 

    @In(create=true) PaymentHandler paymentHandler;

 

    @In Bill bill;

 

        public String pay()

 

    {

 

        paymentHandler.processPayment( new Payment(bill) );

 

        return "success";

 

    }

 

}

异步方法在一个全新的事件(event)上下文中被执行,并且没有访问调用者的会话(session)或对话( conversation)上下文的状态,业务流程(business process上下文被传播。

异步方法调用可以使用@Duration、 @Expiration和 @IntervalDuration 注释来预定计划稍后执行

@Local

 

public interface PaymentHandler

 

{

    @Asynchronous

 

    public void processScheduledPayment(Payment payment, @Expiration Date date);

 

    @Asynchronous

 

    public void processRecurringPayment(Payment payment, @Expiration Date date,  @IntervalDuration Long interval)'

 

}

 

@Stateful

 

@Name("paymentAction")

 

public class CreatePaymentAction

 

{

    @In(create=true) PaymentHandler paymentHandler;

 

    @In Bill bill;

 

       public String schedulePayment()

 

    {

        paymentHandler.processScheduledPayment( new Payment(bill), bill.getDueDate() );

 

        return "success";

   }

 

    public String scheduleRecurringPayment()

 

    {

 

        paymentHandler.processRecurringPayment( new Payment(bill), bill.getDueDate(),  ONE_MONTH );

 

        return "success";

 

    }

 

}

客户端和服务都可以访问与调用相关联的定时器对象。显示在下面的定时器对象是EJB3定时器,在你使用EJB3分配器(dispatcher)时。对于默认的ScheduledThreadPoolExecutor,返回的对象是JDK的Future。对于Quartz 分配器(dispatcher), 它返回 QuartzTriggerHandle,我们在下节讨论它。

@Local

 

public interface PaymentHandler

 

{

    @Asynchronous

 

    public Timer processScheduledPayment(Payment payment, @Expiration Date date);

 

}

 

@Stateless

 

@Name("paymentHandler")

 

public class PaymentHandlerBean implements PaymentHandler

 

{

 

    @In Timer timer;

 

        public Timer processScheduledPayment(Payment payment, @Expiration Date date)

 

    {

        //do some work!

 

        

 

        return timer; //注意返回的值完全被忽略

    }

 

}

 

 

@Stateful

 

@Name("paymentAction")

 

public class CreatePaymentAction

 

{

 

    @In(create=true) PaymentHandler paymentHandler;

 

    @In Bill bill;

 

    public String schedulePayment()

 

    {

 

        Timer timer = paymentHandler.processScheduledPayment( new Payment(bill), bill.getDueDate() );

 

        return "success";

 

    }

 

}

异步方法不能返回任何其它值给调用者。

22.1.2. 使用Quartz分配器的异步方法

Quartz分配器(如何安装它见前文)允许使用上文的@Asynchronous, @Duration, @Expiration, 和 @IntervalDuration注释。 然而它有一些超强的附加功能。Quartz分配器支持三个新的注释。

@FinalExpiration注释为经常性的任务指定一个结束日期。注意,你可以注入 QuartzTriggerHandle。

 

 

        @In QuartzTriggerHandle timer;

 

          //  在" processor "组件中定义方法 

 

    @Asynchronous

 

     public QuartzTriggerHandle schedulePayment(@Expiration Date when,  @IntervalDuration Long interval, @FinalExpiration Date endDate, Payment payment) 

 

    { 

        // 做重复或长期运行的任务,直到结束日期(endDate)

 

    }

    ... ...

 

    // 在业务逻辑处理代码中制定计划任务

 

    // 现在开始, 每隔一小时重复,2010510号结束

 

    Calendar cal = Calendar.getInstance ();

 

    cal.set (2010, Calendar.MAY, 10);

 

    processor.schedulePayment(new Date(), 60*60*1000, cal.getTime(), payment);

注意,这个方法返回QuartzTriggerHandle对象,稍后你可以用它来停止、暂停和重新开始日程安排程序(scheduler)。QuartzTriggerHandle对象被系列化,所以,如果你需要保持它一段较长的时间,你可以存储它到数据库。

QuartzTriggerHandle handle =processor.schedulePayment(payment.getPaymentDate(),  payment.getPaymentCron(),payment);

 

        payment.setQuartzTriggerHandle( handle );

 

        // 存 payment到数据库

 

             // 稍后 ...

 

             // 从数据库取回 payment

 

        // 取消剩余的计划任务

 

        payment.getQuartzTriggerHandle().cancel();

 @IntervalCron支持任务行程安排的Unix cron工作语法。例如,下面的异步方法运行在3月的每个星期三的下午2点10分到下午2点44分。

    // 定义方法

 

    @Asynchronous

 

    public QuartzTriggerHandle schedulePayment(@Expiration Date when, @IntervalCron String cron,  Payment payment) 

 

    { 

 

        // 做重复或长期运行的任务

 

    }

    ... ...

    //在业务逻辑处理代码中制定计划任务

 

    QuartzTriggerHandle handle = processor.schedulePayment(new Date(), "0 10,44 14 ? 3 WED", payment);

@IntervalBusinessDay注释支持在“第N营业日”调用的情形。例如,下面的异步方法运行在每月的第2个营业日的14:00点。默认时,它从营业日中排除了直到2010年之前的所有周末和美国联邦假期。

    // 定义

 

    @Asynchronous

 

    public QuartzTriggerHandle schedulePayment(@Expiration Date when, @IntervalBusinessDay NthBusinessDay nth,  Payment payment) 

 

    { 

        // 做重复或长期运行的任务

 

    }

 

     ... ...

 

    // 在业务逻辑处理代码中制定计划任务

 

    QuartzTriggerHandle handle = processor.schedulePayment(new Date(),  new NthBusinessDay(2, "14:00", WEEKLY), payment);

NthBusinessDay对象包含调用触发器的配置。 你可以通过additionalHolidays属性指定更多的假日(例如,公司假日,非美国假日等等)。

 

public class NthBusinessDay implements Serializable

 

{

 

      int n;

 

      String fireAtTime;

 

      List <Date> additionalHolidays;

 

      BusinessDayIntervalType interval;

 

      boolean excludeWeekends;

 

      boolean excludeUsFederalHolidays;

 

 

      public enum BusinessDayIntervalType { WEEKLY, MONTHLY, YEARLY }

 

 

      public NthBusinessDay ()

 

      {

 

        n = 1;

 

        fireAtTime = "12:00";

 

        additionalHolidays = new ArrayList <Date> ();

 

        interval = BusinessDayIntervalType.WEEKLY;

 

        excludeWeekends = true;

 

        excludeUsFederalHolidays = true;

 

      }     

 

      ... ...

 

}

@IntervalDuration, @IntervalCron, 和 @IntervalNthBusinessDay注释是互斥的。 如果使用它们在同一个方法中,会抛出RuntimeException。

22.1.3. 异步事件

组件驱动事件也可以是异步的。为引发一个异步处理事件,简单调用Events 类的raiseAsynchronousEvent()方法就行了。 要安排一个定时事件,调用 raiseTimedEvent()方法, 传递一个schedule对象给它(对默认分配器或定时器服务分配器,使用TimerSchedule)。 组件可以用通常的方法观察异步事件,但是要记着只有业务流程(business process)上下文被传播到异步线程。

22.1.4. 根据异步调用处理异常

当一个异常通过异步分配器传播时,每种异步分配器的行为表现不同。例如,java.util.concurrent 分配器会暂停一个重复调用的进一步执行,而EJB3定时服务会取消(swallow)异常。因此,Seam 捕获异步调用产生的所有异常,在它达到分配器之前。

默认时,异步执行产生的任何异常将被捕获,并记录在错误级别。你可以自定义这种全局行为,通过覆盖org.jboss.seam.async.asynchronousExceptionHandler组件:

 

@Scope(ScopeType.STATELESS)

 

@Name("org.jboss.seam.async.asynchronousExceptionHandler")

 

public class MyAsynchronousExceptionHandler extends AsynchronousExceptionHandler { 

 

 

   @Logger Log log;

 

   @In Future timer;

 

   @Override

 

   public void handleException(Exception exception) {

 

      log.debug(exception);

 

      timer.cancel(false);

 

   }

 

 }

这里,例如, 使用java.util.concurrent 分配器,我们注入它的控制(control)对象,并且在遭遇一个异常时,取消以后所有调用。

对个别组件你也可以改变这种行为,通过在这个组件中实现方法public void handleAsynchronousException(Exception exception); 例如 :

   public void handleAsynchronousException(Exception exception) {

 

      log.fatal(exception);

 

   }

22.2. 在Seam中的消息

Seam可轻松在Seam组件间发送和接收JMS消息。

22.2.1. 配置Configuration

为配置发送JMS消息的Seam的基础设施,你需要告诉Seam你想发送消息去的所有topics(主题)和queues(队列) ,并也要告诉Seam在什么地方找到QueueConnectionFactory(队列连接工厂)和 TopicConnectionFactory(主题连接工厂)。

Seam默认使用UIL2ConnectionFactory, 一个使用JBossMQ(一个实现了JMS 1.1 规范的JMS服务器)的通用连接工厂。如果你使用了一些其他JMS供应者,你需要在seam.properties,web.xml或components.xml中设置一个或两个queueConnection.queueConnectionFactoryJndiName 和topicConnection.topicConnectionFactoryJndiName。

你也需要在components.xml列出topics和queues,安装Seam管理的 TopicPublishers和 QueueSenders:

 

<jms:managed-topic-publisher name="stockTickerPublisher"   auto-create="true" topic-jndi-name="topic/stockTickerTopic"/>

 

 

 

<jms:managed-queue-sender name="paymentQueueSender"  auto-create="true"  queue-jndi-name="queue/paymentQueue"/>

 

22.2.2. 发送消息

现在,你可以注入一个JMS TopicPublisher 和TopicSession到任何组件:

@In 

 

private TopicPublisher stockTickerPublisher;   

 

@In 

 

private TopicSession topicSession;

 

 

public void publish(StockPrice price) {

 

      try

 

      {

 

         stockTickerPublisher.publish( topicSession.createObjectMessage(price) );

 

      } 

 

      catch (Exception ex)

 

      {

 

         throw new RuntimeException(ex);

 

      } 

 

}

或者, 使用队列:

@In

 

private QueueSender paymentQueueSender;   

 

@In

 

private QueueSession queueSession;

 

public void publish(Payment payment) {

 

      try

 

      {

 

         paymentQueueSender.send( queueSession.createObjectMessage(payment) );

 

      } 

 

      catch (Exception ex)

 

      {

 

         throw new RuntimeException(ex);

 

      } 

 

}

22.2.3. 使用消息驱动bean接收消息

你也可能使用任何EJB3消息驱动bean处理消息。消息驱动bean甚至可以是Seam组件,在这种情况下,注入其它事件和应用程序作用域的Seam组件成为可能。

22.2.4. 在客户端接收消息

Seam远程让你根据客户边JavaScript订阅一个JMS主题。这在第25章 远程描述。

 

分享到:
评论

相关推荐

    精通WindowsAPI.pdf

    第1章 Windows应用程序开发入门..........................................................................................16 1.1 第一个实例程序...............................................................

    第7章三相异步电动机复习练习题.pdf

    三相异步电动机的起动通常包括直接起动和降压起动,如Y-Δ起动、自耦变压器降压起动等,以减小起动电流。反转可以通过改变定子绕组的相序实现。 【调速方法】 调速方法包括改变电源频率(变频调速)、改变定子电压...

    Head.First.C#.中文版.图文皆译.第十一章.事件和委托.翻译完毕.PDF下载

    第十一章.事件和委托 #### 核心概念:事件与委托 本章节重点介绍了面向对象编程语言C#中的两个关键概念——事件和委托。这两个概念对于构建复杂的软件系统尤其重要,它们能够帮助开发者实现对象间的通信,同时确保...

    Node与Express开发.pdf

    第 1 章 初识 Express .......................................................................................................................1 1.1 JavaScript 革命 .........................................

    Sybase ASE 15.7 开发文档:系统管理指南(卷二)

    第 6 章 创建和管理用户数据库 第 7 章 装入和卸下数据库 第 8 章 分布式事务管理 第 9 章 创建和使用段 第 10 章 使用 reorg 命令 第 11 章 检查数据库一致性 第 12 章 制定备份和恢复计划 第 13 章 备份和恢复用户...

    计算机网络第3章.pdf

    【计算机网络第3章知识点详解】 1. 物理层是计算机网络的底层,它不直接涉及具体的物理设备,如电缆或接口,而是提供一个在物理媒体上传输原始比特流的逻辑连接。这个物理媒体可以是双绞线、光纤、无线等。 2. ...

    复旦nois教材01.rar

    第二章 SOPC Builder开发环境......................................................................................................8 2.1 创建Quartus II工程..................................................

    java NIO 简介(中文)

    第二章 缓冲区 ...................................................................................................................... 22 2.1 缓冲区基础 ....................................................

    数字电子技术基础 第7章.pptx

    《数字电子技术基础》第七章主要探讨了集成计数器这一关键主题,集成计数器在现代电子系统中扮演着重要角色,因其功能多样、通用性好、能耗低、速度快和可扩展性强等特点,被广泛应用于各种数字电路设计中。...

    linux网路编程 中文 23M 版

    第1 章Linux操作系统概述................... .......................................................................... 2 1.1 Linux发展历史........................................................ 2 ...

    电力电子技术:第二十讲第六章.ppt

    这一技术在多种应用中都有所应用,如电炉温度控制、灯光亮度调节以及异步电机的软启动和速度控制。 相控式交流调压电路主要利用晶闸管等半导体器件的相位控制来调整输出电压。例如,在异步电机的软启动中,这种电路...

    数字逻辑第一到第四章复习思维导图自制免费版.zip

    如果你希望更深入地学习或复习这四章内容,可以依次查看压缩包内的“第一章.pdf”、“第二章.pdf”、“第三章.pdf”和“第四章.pdf”。通过仔细研究这些思维导图,你可以巩固已学知识,查漏补缺,并为后续章节的学习...

    Linux网络编程

    第一章概论 ..............1 ...第十二章远程过程调用.............249 12.1 引言........249 12.2 远程过程调用模型......249 12.3 传统过程调用和远程过程调用的比较.....250 12.4 远程过程调用的...

    LPC17xx中文用户手册

    第二十二章 重复中断定时器0~3 第二十三章 系统节拍定时器0~3 第二十四章 脉宽调制器 第二十五章 电机控制PWM 第二十六章 正交编码接口(QEI) 第二十七章 实时时钟和备用寄存器 第二十八章 看门狗定时器 第二十九章 ...

    高性能并行计算

    第十二章HPL程序实例剖析. . . . . . . . . . . . . . . . . . 117 参考文献. . . . . . . . . . . . . . . . . . . . . . . . . .119 附录一并行程序开发工具与高性能程序库. . . . . . . . . . . .121 A.1 BLAS...

    javaScript与jQuery第十二章上机作业.zip

    JavaScript和jQuery是Web开发中的重要工具,用于创建交互式的网页和动态内容。在这个18级在校生的上机作业中,我们看到他正通过实践来深入理解和应用这两门技术。以下将详细介绍JavaScript和jQuery的一些核心知识点...

    蓝牙协议及其源代码分析.rar

    2. 第二章无线技术协议............... PAGEREF _TOC120615571 \H 27 2.1 概述........ PAGEREF _TOC120615572 \H 27 2.2 频段及信道分配.... PAGEREF _TOC120615573 \H 27 2.3 发射机特性............. PAGEREF _...

Global site tag (gtag.js) - Google Analytics