首页 > 学院 > 开发设计 > 正文

使用JMS在集群应用程序中分配任务(图)

2019-11-18 12:23:22
字体:
来源:转载
供稿:网友

  在请求驱动的环境中解耦合和延迟处理是创建健壮和可伸缩的分布式应用程序的要害战略之一。许多服务都单独依靠于集群来确保可伸缩性,但是当新发现的需求使应用程序的复杂性增长时,它们经常会碰到麻烦。
  
  尽管服务器集群是推动可伸缩性的基本技术,但是当所有的处理都同步完成时,它可能变得很低效。吞吐量可能会增加,但是响应性却会变得不可救药。
  
  在本文中,我讨论了异步处理,并举例说明了,巧妙的任务治理会如何提高您应用程序的性能、可用性、可伸缩性和可治理性。我们将以一种高度可配置的方式,创建一个一般的任务分配框架,该框架可以发送任何任务给您的集群中的一台或每台服务器。通过使用多态和 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 队列。还应该为重复保存故障消息配置错误队列。
  
  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.

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表