在本期的J2EE探索者(J2EE pathfinder)中,Java 开发者和咨询顾问Kyle Gabhart 解释了为什么消息服务对于企业的体系结构来说是至关重要的,您的解决方案必须克服什么类型的障碍,以及除了Java消息服务(Java Message Service ,JMS)之外,还有哪些替代的解决方案。在本文的结尾,他分析了三种可用的解决方案(简单JMS客户机(SimpleJMS Client)、结合JMS使用的会话bean, 以及消息驱动bean),并且提供了一些特定的指导方针来衡量这些选项。
说到对消息传递解决方案的选择,您可以像配手套那样找到适合于企业的解决方案。您的消息传递框架必须能够在一组应用和企业资源之间进行通信。而且,该消息传递框架必须快速、可靠地完成这一任务。此外,面对日复一日的骚扰和紧急情况,它必须表现得天衣无缝。
为了根据您的需求选择适当的消息传递解决方案,首先应该对您企业当前的组成结构以及将来的发展趋势有一个清醒的认识。此外,如果能够透彻地理解消息传递框架在完成目标的过程中必须克服的障碍,那么对于解决方案的选择将会很有帮助。最后,您要知道 可选择的范围:有哪些可用的技术,以及各种技术对于不同的环境和需求的适用情况如何。
在 本期的J2EE 探索者中,我们将一一阐述以上要点。首先是对企业消息传递的概述,在此我们将着眼于消息传递在您企业中所扮演的角色,并在建立可靠的通信方面给出了一些挑 战。接着,我们将从体系结构的角度来快速 了解一下,在一个典型的企业网络环境中,J2EE 消息传递技术如何与面向消息的中间件协同工作。然后,我们将针对Java消息服务(Java Message Service ,JMS)和J2EE的消息传递包展开更具体的讨论。我们将分别论述三种类型的J2EE消息传递客户机的基本目标和功能,而且您也可以了解到每种类型的 J2EE消息传递客户机各自的优势和劣势所在。最后,我们将分析一些常见的消息传递场景和解决方案,这将有助于您为您的企业选择最好的J2EE消息传递解 决方案。
企业消息传递框架被设计用于使得一个或多个应用能够克服各种障碍进行通信。常见的屏障包括:两个系统同时运行(同步通信)的需求,多个应用获取同一条消息(多重传输)的需求,大多数系统都彼此异构,以及网络故障等。
M许多企业的体系结构依赖于面向消息的中间件系统(MOM)来为不同类型的系统提供消息通道。MOM为应用提供了一种公共的、可靠的方式,使这些应用能够轻松地创建、交换和处理消息,而无需考虑消息传递客户机的实现细节。消息被发送到服务器目的地和域 (domain),而不是发送到物理地址。消息传递客户机只需简单地声明对某个特定的域和目的地感兴趣,提供适当的安全性令牌(security token)以获得访问该域的权限,然后通过那个目的地与消息传递服务器进行交互。
从概念上来讲,这与现实生活中真实邮件的传递方式没什么不同。消息的发送方只负责使用正确的包装,提供正确的地址,并附上适当的邮资。 邮局(这里是一个MOM系统)处理 与消息的安全可靠的传递相关的所有问题,而不 管出现在他们面前的任何障碍(机器故障,天气恶劣,等等)。
在 一个MOM系统中,客户机之间的耦合性比较弱,这允许它们不必真正地全天“在线”便能维持服务的最佳质量。如果能够去掉应用一直处于可用状态这一需求,那 么维护和伸缩性将变得更加易于管理。可以在一天中的任何时候将应用离线,更新应用,或者作为例行的维护工作刷新应用,而不必担心会影响服务的质量。
![]() ![]() |
MOM 服务器允许不同类型的系统交换消息,但是每个MOM供应商都有其特有的处理消息的API。这种标准化的缺乏对于Java技术开发范例来说是不可接受的。为了充分利用已有的MOM基础设施,同时又不失标准化,J2EE平台提供了JMS。
![]() |
|
JMS 定义了在Java企业系统中传递消息的规则,并且声明了一些方便应用组件和消息传递系统(通常是MOM)之间的消息交换的接口。JMS客户机向MOM服务 器上的目的地开放连接,然后在那个目的地上发送和接收消息。JMS卸下了 保证传送(guaranteed delivery)、消息通知(message notification)、消息耐久性(message durability)以及消息传递系统中所有底层网络和路由问题的负担。JMS和MOM能够很好地协同工作,因为它们都划清了消息传递客户机和服务器之 间的责任界限。
JMS 支持两种基本的消息传递机制。第一种机制是点到点的消息传递(point-to-point messaging),在这种机制下,消息由一个发布者(发送方)发送,由订阅者(接收方)接收。另一种机制是发布-订阅式的消息传递(publish- subscribe messaging),在这种机制下,消息由一个或多个发布者发送,由一个或多个订阅者接收。尽管这两种机制是JMS的 实际基础,很多人还是按照三种消息传递模型来看待这项技术:
- 一对一的消息传递(One-to-one messaging )是一种点到点的模型。消息由一个JMS客户机(发布者)发送到服务器上的一个目的地,即一个队列(queue)。而另一个JMS客户机(订阅者)则可以 访问这个队列,并从该服务器获取这条消息。在队列中可以存放多条消息,但每次只能获取一条消息。
- 一对多的消息传递(One-to-many messaging) 是一种发布-订阅模型。这里仍然是由一个JMS客户机将一条消息发布到服务器上的一个目的地上,但是这次这个目的地叫做一个主题(topic)。这里关键 的不同在于放在一个主题中的消息包括了一个参数,这个参数定义了该消息的耐久性(它能够在服务器上等待订阅者多长时间)。该消息将一直维持在主题中,直到 这个主题的所有订阅者都取走了该消息的一个副本,或者该消息的耐久性时间已到期,不管发生的是上述中的哪种情况,该消息都将被从这个主题中删除。
- 多对多的消息传递(Many-to-many messaging),这也是一种发布-订阅模型,同时还扩展了一对多的消息传递模型。除了支持多个订阅者外,该模型还支持一个主题有多个发布者。多对多消息传递的一个很好的例子就是e-mail listserve:多个发布者可以将多条消息投递到一个主题,而所有的订阅者将获取每一条消息。
JMS消息的结构相当直观。其中有一个部分(section)用于路由、寻址和消息识别;还有一个可选的部分,在这个部分中可以传递一些特定于应用(application-specific)的参数;第三个 部分存放的是消息的有效负荷(文本 、字节、值映射(value map)、对象,等等)。这三个部分分别被称为头部、属性和主体,如图 1 所示。
图 1. JMS 消息结构
![JMS message structure](https://i-blog.csdnimg.cn/blog_migrate/395590feddcfe3085b310f4a845a563e.png)
您 所使用的消息传递模型的类型取决于您企业的需要。在一个企业中同时使用不止一个的消息传递策略并不少见。在后面的一些小节中,我们将研究一下用于Java 平台的三种JMS解决方案:简单JMS客户机,结合JMS使用的会话bean,以及消息驱动bean。我们将重新回顾每种类型,它们各自的优点和缺点,以 及它们对不同企业场景的适用情况。
![]() ![]() |
尽 管JMS是与Java 2 Enterprise Edition一起发布的一种企业级的技术,您仍可以轻松地将一个标准Java客户机转换成一个支持JMS的应用。将企业消息传递功能添加到Java applet、命令行应用程序、Swing应用程序或者Java WebStart都非常简单。 您只需简单地将少量方法调用添加到J2SE应用代码中,然后将一个包含了JNDI(也是J2EE的一部分)的接口和实现类的JAR文件添加到类路径 (classpath)中。如果您已经在客户机上装载了这个JAR文件,并且将它添加到了您的应用的类路径中,那么您就可以使用JNDI来访问JMS提供 者(参见下面的 参考资料 ,以获得关于JMS消息传递的更多信息的链接)。
简单JMS 客户机方法有许多优点,最明显的优点就是它的简单性和普遍性。所有的J2SE应用都可以毫不费力地扩展为可以与一个JMS消息传递系统进行交互。此外,使 用JMS的新应用部署起来只需对客户端进行少量的配置,甚至不需要配置。简单JMS客户机是对几乎任何Java体系结构的简单、灵活和轻量级的一个扩展。
从 消极的方面来看,我们会遇到安全性、事务处理以及可伸缩性等问题。对于一个简单JMS客户机,您只能选择将安全性和事务处理外包给某个供应商,也就是说, 这些问题将是以一种特定于 供应商的方式来处理的。如果您的简单JMS客户机既要处理传进来的消息,又要发送消息,那么就会碰到可伸缩性问题。JMS没有能够一次处理多于一个传进来 的请求的内建机制。为了支持并发请求,您需要扩展JMS客户机,使其产生多个线程,或者启动多个JVM实例,让这些线程或实例各自运行应用。此外,还需要 将JMS提供者配置为在一些适当的目的地上可以有多个订阅者。这时,您(或者您的开发小组)就会质疑简单JMS客户机解决方案是否真的具有简单性。
![]() ![]() |
将 会话bean与JMS结合起来使用是一种可行的面向企业的解决方案。会话bean被设计用来履行对业务服务的请求。必须查询企业消息传递系统来履行 这样的一个请求,就这一点来说,企业消息传递系统可以由一个会话bean透明地来访问。使用会话bean作为JMS客户机还允许将JMS通信合并到一个大 型企业事务的上下文环境中。例如,可以建立一个J2EE事务来从一个JMS提供者那里获取一条消息,从该消息中提取数据,并尝试更新数据库。如果更新操作 失败并且事务回滚 (rollback),则再发送一条消息到一个单独的目的地上的JMS提供者那里,同时给出对事务失败的原因的描述。
企业JavaBeans 技术使用资源管理器连接工厂来访问额外容器(extra-container )资源。这些资源是标准的企业组件,但不是J2EE容器的核心部分,它们包括数据源、JMS会话、JavaMail会话、URL连接以及Java连接器体系结构( Java Connector Architecture ,JCA)适配器。资源管理器是J2EE容器的一个组件,它管理着某一特定类型的资源的整个生命周期,这些资源包括连接池、事务支持以及实现实际连接所必需的任何网络协议。
企业bean通过三个步骤来获得一个JMS会话的连接:通过JNDI查找获得一个连接工厂引用,通过工厂引用获得一个连接,然后以一种常规的JMS的方式使用主题或者队列连接对象。因为JMS必须有遵从J2EE规范的应用服务器的支持,因此不再需要附加的库或者组件。
将JMS和会话bean结合起来使用,这在企业功能性方面是一个进步,而在简单性和灵活性方面却又是一个退步。通过使用会话bean,应用开发者可以访问 EJB容器所提供的整个范围的J2EE功能,包括JNDI、宣告式事务语义、自动并发支持、资源管理、宣告式安全性以及对诸如实体bean、数据源、 JavaMail和JCA适配器之类的企业资源的访问。从消息传递的立场来看(跟MDB不一样),会话bean与JMS的联手并没有对您的bean所能访 问的主题和队列强加任何数量上的限制。
作为增强企业特性的代价,您牺牲了简单性,客户机占用空间(client footprint)也不再像以前那么小了,而且也没有了异步性。前两项损失倒没什么好奇怪的,如果您已经关注探索者 系列有一段时间了,那么您就更能理解这一点。会话bean要求有一个成熟的J2EE EJB容器,这让您的开发小组(针对EJB开发而言)和您的整个系统体系结构(针对客户机占用空间而言)背上了沉重的包袱。
异 步性是使用像JMS这样的企业消息传递技术的主要优势之一,而且,在取得这一优势的同时它并没有失去什么。有了JMS,消息传递客户机可以通过提供者发送 消息,消息发送出去之后便可以离线,而让提供者从容地传送这条消息。接收消息的客户机可以周期性地上线并检查新的消息,或者也可以设立一个侦听器组件,令 其一直处于在线状态以等待来自提供者的消息。会话bean是同步的,因而不能支持“一直在线(always-on)”侦听器组件。与前一种客户机不同,同 步的Java客户机必须调用一个会话bean方法。然后由该会话bean方法打开与一个消息传递提供者的连接,以便发送和接收消息。
![]() ![]() |
EJB 2.0 规范定义了一种新的企业bean,以期弥补其他四种类型的企业bean(两种会话bean和两种实体bean)的不足。这种新的bean就是消息驱动bean(message-driven bean,MDB),人们期望用它来提供可重用的消息传递组件,以便利用已有的在J2EE应用服务器方面的投资,尤其是利用已有的EJB技术。
MDB 只能通过一条JMS消息异步地进行调用。因此,它并不具有其他bean所具有的本地和远程接口。相反,MDB实现两种特殊的接口:一个与EJB容器之间的 接口(javax.ejb.MessageDrivenBean),以及一个消息传递接口(javax.jms.MessageListener)。作为 一种成熟的JMS客户机,MDB通过一个MOM服务器既可以发送消息,又可以接收消息。作为一种企业bean,MDB由容器来管理,并且通过一个EJB部 署描述符进行宣告式的配置。
MDB允许开发者利用已有的在EJB技术方面的投资,但是仍然可以将这些投资整合到一个异步消息传递的上下文环境中。例如,JMS客户机可以发送一条消息 给一个MDB(该MDB一直在线等待传进来的消息),而后者可以访问一个会话bean或者一些实体bean。通过这种方式,MDB可以被用作一种异步包装 器,提供对业务流程的访问途径,而之前这些业务流程只能通过一个同步的RMI/IIOP调用来访问。
消息驱动bean本身也是一种强大的消息传递解决方案。由于MDB被专门设计用来作为消息的消费者,并 且仍然是由EJB容器管理的,因此它们在可伸缩性方面提供了巨大的优势。由于消息bean是无状态的,并且由容器进行管理,因此它们并发地发送和接收消息 (容器只是简单地将另一个bean从池中提出)。这一点,加上EJB应用服务器所固有的可伸缩性,构成了一种极其健壮的、可伸缩的企业消息传递解决方案。
另一方面,MDB相对来说还是一种很新鲜的事物,没有经过很多的检验。因而,并不是所有的J2EE供应商都支持它们,即使是支持MDB的那些供应商也只是最近才实现它们的。可以预见,MDB的不成熟意味着 供应商实现在稳定性和可靠性方面还有一段很长的路要走。而且,MDB社区也需要经历更多的锤炼,以获得一套成形的使用MDB的最佳实践。
抛 开MDB的相对不成熟性不提,理解它是为专门的目的而设计的(即作为JMS消息的消费者)十分重要。MDB只能通过JMS消息来调用,其他方式都不管用。 这意味着它们作为消息的消费者非常理想,但未必适合作为消息的生产者。消息驱动bean当然可以发送消息,但前提是它首先要让一条传进来的请求调用它。而 且,当前设计的MDB只能映射到单个的目的地。它们只能在那个目的地上侦听消息。这一限制在以后的版本中可以改变,但目前您只能为每个您想侦听的目的地定 义一个MDB。
![]() ![]() |
如前所述,选择适当的解决方案时,大部分要做的工作就是衡量您企业的特定需求,包括目前的需求和可预见的将来的需求。如果能记住多种企业消息传递解决方案 可以结合使用,那么也会很有帮助。在下一节,我们将看一些常见的消息传递场景和每个场景潜在的JMS解决方案。当您为您的企业选择适当的消息传递技术,或 者混合使用多种技术时,这些内容可以 提供一般的指导方针。
从一个组件访问多个主题和队列
如果您的业务流程规定了消息目的地只能有条件地访问(换句话说,如果x<5,则访问主题A,如果x>5,则访问主题B),那么您将不能使用 MDB。不过,您可以使用一个简单JMS客户机,或者将会话bean与JMS结合起来使用。为了在这两种选择之间作出决定,您必须在简单JMS客户机的轻 量级特性(特别适合于applet、Swing应用程序和独立的控制台应用程序)以及J2EE容器的健壮性( 包括透明的事务性支持、宣告式安全性和其他EJB型资源管理功能)之间进行权衡。
异步地访问会话bean和实体bean
有两种方式来处理这一场景。比较明显的一种方式就是使用一个消息驱动bean,但前提是您的供应商支持MDB技术。MDB被设计用来 消费异步消息并代表消息发送方访问企业功能性。此外,应用服务器可以维护一个MDB的多个实例,来处理并发的服务请求。如果您不能使用MDB,您可以创建 一个简单JMS客户机,以此来作为一个侦听器。 当收到一条消息时,该客户机便可以与应用服务器建立一个同步的RMI连接,并按照常规的方式调用会话bean或者实体bean。不过,MDB是更值得推荐 的解决方案。
构建尽可能瘦的JMS客户机
在这一方面,简单JMS客户机显然是赢家。如果对您来说更重要的是提供一个轻量级的消息传递客户机,而不是拥有像会话bean这样的J2EE客户机的可伸缩性和健壮性,那么简单JMS是 正确的选择。无论是会话bean与JMS的组合,还是MDB,两者都需要一个J2EE应用服务器,这使得两种选择都不适合瘦客户机实现。
并发地发送和接收消息
这里惟一合适的选择是使用消息驱动bean。消息驱动bean是专门为这一场景量身定做的。从技术上讲,简单JMS客户机也可以利用多线程技术来提供类似的支持,但这种解决方案开发起来很复杂,并且最后开发出来的东西并没有很好的可伸缩性。
将消息传递合并到J2EE过程中
您或许已经在J2EE体系结构中有所投入,并且认识到将企业消息传递合并到这些J2EE过程中的需要。在大多数情况下,您最好的解决方案是将会话bean 与JMS资源连接结合起来使用。您可以采用已有的会话bean来访问一个或多个主题和队列,而不必重建整个基础设施。这些会话bean将继续履行传统的对 业务服务的请求,并且还 担负起了消息传递的任务。在某些情况下,将您已有的业务服务暴露给一个JMS客户机是有好处的,这从前面的讨论中也可以发现。
![]() ![]() |
就今天技术前景的多样性和日趋于异步的特点来说,消息传递是一种激动人心的、日益流行的解决方案。Java 消息服务提供了一种独立于 供应商和平台的媒介,通过使用企业消息传递将多个系统系在一起。在本文中,我们简要地浏览了企业消息传递和JMS,并且给出了一些指导方针,以方便您选择 最适合您企业的解决方案。