kapok

垃圾桶,嘿嘿,我藏的这么深你们还能找到啊,真牛!

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  455 随笔 :: 0 文章 :: 76 评论 :: 0 Trackbacks
废话就不要看了,看看线程和command的部分设计就可以了,还不错的想法。

http://dev2dev.bea.com.cn/techdoc/webser/2005060104.html


在请求驱动的环境中解耦合和延迟处理是创建健壮和可伸缩的分布式应用程序的关键战略之一。许多服务都单独依赖于集群来确保可伸缩性,但是当新发现的需求使应用程序的复杂性增长时,它们常常会遇到麻烦。

  尽管服务器集群是推动可伸缩性的基本技术,但是当所有的处理都同步完成时,它可能变得很低效。吞吐量可能会增加,但是响应性却会变得不可救药。

  在本文中,我讨论了异步处理,并举例说明了,巧妙的任务管理会如何提高您应用程序的性能、可用性、可伸缩性和可管理性。我们将以一种高度可配置的方式,创建一个一般的任务分配框架,该框架可以发送任何任务给您的集群中的一台或每台服务器。通过使用多态和 Java 消息服务( Java Message Service , JMS ),我们的框架将实现著名的 Command 模式。

解耦合在实际中的意义

  当服务器收到客户端请求时,它通常需要在返回响应之前执行几个单独的任务。解耦合( decoupling )意味着不会一次执行所有的任务,而是把一些任务放入队列并异步地进行处理。因为排队通常是一项低开销的操作,同步的请求会更快地结束。

解耦合的优点

  按照顺序和并行地处理任务,通常会比随机地处理它们要更加高效(客户端偶然发出请求的时候)。正面的影响比随即表现出来的要大。理论上,解耦合可以提高以下各个方面的性能:

  • 健壮性 提高,因为请求将依赖的可能失效的过程更少。
  • 响应性 请求的部分后处理减少了接收请求和返回响应之间的时间。
  • 可伸缩性 所有解耦合后的过程在复杂性方面可能会增加,但不会有降低响应度的危险。
  • 可用性 可以在服务器永远不知道故障原因的情况下处理故障。

  在子系统不可用的情况下,配置自动重试要更加容易。

  自然,理论与实践之间的差别在每个应用程序上的体现各不相同。然而,显然,几乎每种实现都至少具有前述的某些优点。

解耦合的缺陷

  和大部分好东西一样,解耦合也存在缺点。其中最严重的缺点之一就是,如果您不能确保您拥有足够强大的硬件来清空繁忙的处理队列,那么您可能会发现,它的可用性实际上降低了。如果进入的异步请求比您的系统能够处理的要多,队列就会非常迅速地增长。您必须注意设计过程,而对队列实行自动监控无疑是可取的做法。另一个明显的问题是,在请求驱动的环境中,大多数过程都不是很适合于解耦合。事实上,大多数处理都可能被要求返回响应。有时,它会需要一些开箱即用的思想,甚至可能是您为您的客户端所提供的服务方式的变化。

哪些过程可以解耦合

  从纯技术的角度来讲,几乎所有的过程都可以解耦合。例如,您可以把一个所购买物品及客户详细信息的清单放入队列,从而可以解耦合一个订单事务——异步处理将负责余下的工作。不足之处在于您不能在响应中包含任何处理细节。因此,谨慎地预先验证数据是很重要的,这样可以确保不会出现问题。

  一种越来越流行的实现是,马上把请求放入队列,然后持续轮询服务器,以便了解何时可以获得响应。尽管这种方法在本质上实际是同步的,而且不会增加请求的处理时间,但是它在心理上具有优势,因为可以在轮询期间显示进度条。

  除了解耦合完整的业务逻辑(这是一个巨大的难题)之外,更少的集中处理,比如日志记录和发送电子邮件,是可以考虑的良好选择。当性能变得及其重要时,没有理由让客户端等待这种任务的完成。特别的电子邮件是用于解耦合的一个良好选择。让我们做进一步的了解。

实例研究:异步的电子邮件

  以传统的方式发送电子邮件(作为同步请求的一部分)会引起一些问题。首先,连接到电子邮件服务器需要一次网络往返,速度可能会很慢,尤其是在服务器非常繁忙的时候。过载的电子邮件服务器甚至可以使依赖于电子邮件的服务暂时不可用。

XA 事务支持
  另一个显而易见的问题是,电子邮件服务器通常在本质上是非事务性的。当事务被回滚时,这可以导致出现不一致的通知——把一条消息放入队列之后不能取消它。幸运的是, JMS 支持事务,而且可以通过把消息的发送延迟到提交底层事务的时候来解决这个问题。

  考虑访问数据库和事务感知的 JMS 时,您将需要使用 XA 和两阶段提交( 2PC )事务。可以使用非 XA 资源来模拟 XA ,但是您可能会得到不一致的数据。启用 XA 只是一个配置问题,而且通常不需要修改代码。参见 WebLogic 文档以获得相关的详细信息。

通过 JMS 发送电子邮件
  为了使用 JMS 来发送电子邮件,我们需要配置 JMS 组件(比如, JMS 服务器, JMS 队列和连接工厂)。我们还需要编写一个消息驱动 Bean ( Message Driven Bean , MDB )来执行信件的实际发送。当我们想要在我们的代码中发送电子邮件时,需要创建一条包含信件的属性和内容的 JMS 消息。之后,我们把它发送给处理队列。

  这样做的工作量很大!幸运的是, BEA WebLogic JMS 为我们提供了创建一个可以解耦合几乎任何过程的框架所需的全部内容。

用于异步执行的框架

  是动手看看一些代码的时候了。我们将创建一个框架,这个框架支持在集群中的一台或所有服务器上,异步地执行代码的任何部分。实现起来的确需要花费些力气,但是一旦框架完成,异步执行就是再容易不过的事情了。

  这个思路是编写一些类,这些类包含一个带有可运行代码的公共方法和另一个用于初始化参数的方法——可能是构造器。封装在 JMS 对象消息中之后,这些预先编写好的类的实例(命令消息)就被发送给在您的服务器上配置的 JMS 队列。至此,消费者把它们取出,然后异步地执行它们(参见图 1 )。

  让我们逐个看看这个框架的所有部分:

  • JMS 队列 应该在每台服务器上配置一个用于接收命令消息的 JMS 队列。还应该为重复保存故障消息配置错误队列。
  • JMS 连接工厂 为了支持事务性行为的运行时选择,应该配置两个连接工厂:一个支持 XA ,而另一个不支持 XA 。
  • 命令对象接口 (CommandMessage) 这是一个所有命令对象都需要实现的简单 Java 接口。它扩展了 java.io.Serializable 接口,该接口对于在 JMS 对象消息中嵌入我们的命令来说是必需的。现在,因为我们想要在不知道命令的确切类型的情况下运行它们,我们还要实现 java.lang.Runnable 接口,稍后把它们简单地转换为 Runnable 对象,并执行它们的运行方法。我们在不知道我们运行的确切内容的情况下运行了代码。这是最理想的多态。
  • 命令执行程序 (CommandExecutionManager) 我们将使用一个 MDB 来处理命令。实例池化防止了 JMS 初始化重复出现,这使得 MDB 成为功能非常强大的消息监听器,非常适合于这项任务。编写 Bean 类不需要很大的工作量,我们只需要在 onMessage 方法中编写数行代码(参见清单 1 )。

  这样就把收到的消息传递给一个 ObjectMessage ,获得嵌入的命令对象,然后执行它的运行方法。通过在 config.xml 文件中,把队列的重新发送限制设置为一个大于 0 的值,您可以配置一个重试计数器。从您的命令对象抛出一个运行时异常,便可触发重新发送的动作。此外,通过配置重新发送延迟,您还可以控制重试的频率。

用于发送消息的一个帮助器类 (TaskDistributor)

  从技术上说,这个部分并不是完全必要的,每次都可以手动进行 JMS 排队。然而,这是一个冗长乏味的过程,而且实际上是帮助器使这个框架变得如此实用。帮助器是一个常规的 Java 类,带有用于对命令消息进行排队的静态方法。您可以针对处理不同的场景编写单独的方法,但是为了简明起见,我选择了编写一个可以处理大多数情况的方法:

static void execute(CommandMessage cm, long delay, b oo lean runEverywhere, b oo lean persisted, b oo lean
enableXA, int priority)

  这个静态方法带有几个用于精确执行控制的参数。让我们逐个讨论这些参数:

  • CommandMessage cm 一个命令消息实例。
  • long delay 代表发送属性的时间,借助 weblogic.jms.extensions.WLMessageProducer 类进行设置。这样,就可以在夜间或者其他方便的时间执行命令。接受一个 Date 对象也是可以的。
  • b oo lean runEverywhere 决定是否发送要执行的消息给集群中的一台随机选中的访问器或者所有的服务器。
  • b oo lean persisted 将通过使用队列发送程序的 setDeliveryMode 方法选择发送模式。应该始终保持业务关键型的消息,从而在访问器崩溃的时候,这些消息不会丢失。然而,持久性始终是以性能损失为代价的,这也应该纳入考虑的范围内。
  • b oo lean enableXA 将选择方法是否使用支持 XA 的 JMS 连接工厂。此参数设置为 true 时,排队将参与底层事务(如果存在的话),在提交事务之前不会对消息进行排队。
  • int priority 决定消息的 JMS 优先级。在发送之前,将使用给定的值调用 javax.jms.Message 类的 setJMSPriority 方法。有效的范围是 0-9 。对于大多数应用程序来说,给命令消息指派不同的优先级似乎有些过头,但是出于完整性方面的考虑,我还是在这里包括了这个选择。

  应该针对您的特定执行的需要,来量身打造 TaskDistributor 帮助器类的实现。在本文中,要包含一个例子似乎太长了,但是您可以从 WLDJ Web 站点 www.sys-con.com/wldj/sourcec.cfm 下载一个。还可以添加几个额外的参数来控制更高精度的执行,但是另一方面,您可能已经满足于较少的几个选项。

使用异步执行

  框架完成之后,我们就可以开始实现我们的命令消息了。让我们看一个简单的例子。首先,我们需要创建一个代表我们的命令消息的类(参见清单 2 )。为了调用执行,我们使用了 TaskDistributor 类(参见清单 3 )。

  当调用例子中的执行方法时,一个包含 DistributedLogger 类实例的 ObjectMessage ( JMS 优先级被设置为 4 )将在 1 秒延迟之后,被发送给集群中所有的服务器。随后,记录程序将在所有的服务器上打印一个字符串给 stdout 。借助已经完成的框架,异步执行变得非常易于实现,完全没有障碍。节点到节点的通信变得前所未有的容易。

容器托管的任务分配

  我们可以通过使用池化线程和虚拟内存队列,来创建一个类似的服务,以处理异步请求。然而,我们强烈建议让应用服务器来管理所有的线程。

  此外,因为 JMS 为我们提供了一个非常简洁和灵活的解决方案,没有理由不让我们的服务器来处理错综复杂的过程。事实上,我们可以调用这个方法容器托管的任务分配( Container-Managed Task Distribution )。

性能问题

  BEA WebLogic 可以处理很大的消息量,而且性能通常不是问题。尽管如此,当生产非常大量的命令用于处理时,还是推荐使用非持久性的消息和流水线操作。另外,消息流控制可以缓解:服务利用率的暂时高峰所导致的消息处理消耗过多资源。

并行处理

  使用 MDB 的一个巨大优点是,它们可以自动地并行处理消息。您可以通过显示池化消费者 Bean 的数量来调整消耗处理资源的数量。

  WebLogic 提供大量有价值的 JMS 扩展和配置选项——它们中有许多可以用在任务分配的各种实现中。选择和优化用于分页、重新发送、持久性和调节(流控制)的 JMS 参数时,应该格外注意。

  JMS 是一个非常完善的服务,对其功能进行仔细研究是值得的。想要了解有关提高性能的更多信息,请参见 WebLogic JMS 性能指南。

结束语

  我们已经讨论了解耦合和异步的消息收发。作为一条经验法则,我们可以说,处理异步请求的服务器,比专门处理同步请求的服务器的执行效率要高。尽管解耦合并不总是件容易的事情,或者甚至不总是个可行的方法,但在考虑周全地实现时,它仍然是一种功能非常强大的机制。我们获得的不仅仅是几个与性能相关的优点,而且能够设计更加灵活的应用程序。

  BEA WebLogic JMS 远远不止是个用于数据传输的服务。除了可配置性非常强之外,它还提供许多有用的功能,比如自动重新发送,消息的持久化,可调度性, XA 支持,调节,瞬时分页和循环故障消息的重定向。通过利用这种强大的通用性,我们可以创建一个功能强大而且可以扩展的框架,来处理需要进行异步处理的所有情形。

关于作者

  John-Axel Strahlman 是 Sanda Interactive Ltd ( www.stcinteractive.com ) 的创立者和 CEO 。 Sanda Interactive Ltd 是一家软件咨询公司,总部位于芬兰的 Espoo 。他是一位分布式系统专家,为他的客户的项目提供咨询已经超过 5 年。( 更多信息

清单 1 :命令处理器 MDB

public void onMessage(Message msg) {

  ObjectMessage om = 
  	(ObjectMessage) msg;

  Runnable command = 
  	(Runnable) om.getObject();

  command.run();
}

清单 2 :命令类示例

public class DistributedLogger
  implements CommandMessage {

  private String text = null;

  public void setText(String text) {
    this.text = text;
  }

  public void run() {
    System.out.println(text);
  }
}


清单 3 :框架使用示例

DistributedLogger logger =
  new DistributedLogger();

String text = 
  "Hello asynchronous execution!"

logger.setText(text);
TaskDistributor.execute(
  logger, //Command instance
  1000,   //delay
  true,   //runEverywhere
  false,  //persisted
  false,  //enableXA
  4);     //delay.

原文出处

http://sys-con.com/story/?storyid=48222&de=1

posted on 2005-06-07 10:07 笨笨 阅读(621) 评论(0)  编辑  收藏 所属分类: J2EEALLWeblogic Portal

只有注册用户登录后才能发表评论。


网站导航: