IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  XML  >

构建基于 XML 的消息服务器

基于 XML 的消息服务器的说明性示例

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 初级

George Franciscus (George_Franciscus@yahoo.com), 负责人, Nexcel

2001 年 11 月 01 日

本文演示如何编码与传输协议无关的,基于 XML 的轻量级消息服务器,它不仅允许客户机放置并获取队列上的消息,还使用 XSL 传输消息。用 Java 编写的 8 个代码清单将涉及从打开客户机连接到调用消息上的 XSL 转换。

消息传递已经成为解决关于同类和异类主机之间通信的商业问题的常规策略。让我们考虑一个典型的供应商和购买者的之间的关系。购买者想在任何时候(无论白天或黑夜)给供应商下电子定单。但是,供应商的计算机可能不支持 24 小时服务或者不具有与购买者兼容的平台。对于这个问题,消息传递是极佳的解决方案。

使用消息传递,购买者向供应商发送包含订单信息的消息。因为购买者发送消息时,供应商可能不在线,所有直到供应商准备去请求这条消息,才将消息发送到存储消息的资源库。获取和处理定单后,通过将消息存储在资源库中,供应商向购买者发出确认消息。购买者经常检查资源库以从供应商获取确认消息。管理这个资源库的软件就是消息服务器。您认为这听起来有点深奥就象 IBM 的 MQSeries 的消息代理,这就对了。本文的主题是构建一个简单的基于 XML 的消息服务器,它模仿了 MQSeries 中的一些功能。

与邮政人员类似

许多技术概念都来源于日常生活,消息传递也不例外。拿邮局这个简单的例子就可以很好地说明在消息服务器中所发生的过程。考虑下面这种情况。想向某人发送一条消息。您很快在纸上写下这条消息并把它装在写有地址的信封里。然后,通过邮局发送信件,此时信是由写有地址的信封和里面的消息组成。当您的信进邮局大门后,邮政人员将查看信封的地址信息,并将它放到适当的信箱中。这封信将一直在这个信箱中,直到有人取走为止。收信人将会到邮局,并且向邮政人员索取他所期望信箱中的信。

看一下这里的角色和责任。邮局负责接收请求并将信件管理授权给邮政人员。在我们这个项目中,邮局就是消息服务器,邮政人员是队列管理器。保留信的信箱就是队列。信由信封和消息组成。在这种情况下,可以认为信封相当于消息头,而地址相当于队列名。信的内容称为 有效负载(payload)。我将介绍的另一个概念是 容器,它是一种能一次获取所有队列的简便的方法。





回页首


应用程序体系结构

让我们先把这种类似放在一边,来看一下图 1,它说明了消息服务器的体系结构。 MessageServer 检测来自客户机的套接字连接。然后,它委派 QueueManager 来读取该套接字并把这个消息放在适当的队列中。为释放这个接收端口,Java 网络 API 为这个端口重新分配一个可用端口。 MessageServer 对象创建一个 QueueManager 线程,将客户机的套接字对象和容器传递给该线程。现在,由 QueueManager 负责后面的处理工作。


图 1. 消息服务器体系结构
消息服务器体系结构

QueueManager 从该套接字中得到来自客户机的完整消息。然后,它使用由客户机传输的字节流来构建消息对象。从此之后,消息的所有操作都通过 Message 对象来完成。 QueueManager 检查这个对象以发现 messageIdqueueNamecommand 。由客户机指定的命令告诉 QueueManager 下一步要做什么。 PUT 命令指示 QueueManager 使用由客户机指定的 queueName 将消息放在队列中。客户机定义的消息标识将作为后续消息检索的键。客户机使用 GET 命令来请求检索消息。 DEL 命令从队列中除去所有消息。

消息由两部分组成:头和主体。头部分包含整个消息的元数据。这些头字段包括 statusCodecommandqueueName messageIdprocessingRule 。主体部分只包含一个字段 payload





回页首


XML 的强大性

在不基于 XML 的消息服务器中,对于消息大小,没有实际限制。然而,客户机和服务器必须遵守头部分的大小以及头部分中各字段的大小。XML 可以取消这种不灵活性。当以 XML 形式构建消息时,可以使用 XML 解析器来抽取消息字段的内容,而不考虑内容的大小。因此,如果必须加长消息标识以确保唯一性,则不需要编程更改。它还意味着可以方便地将新字段添加到头中,而不影响已经存储在队列中的消息。

这个消息服务器支持 XML 有效负载。通过允许客户机请求 XSL 转换,XML 格式可以很容易地支持有效负载转换。现在,客户机可以请求有效负载的子集,从而使下载次数降到最少,或者可以检索已排序的数据。通过添加附加的 XSL 程序,服务器可以扩充它的转换规则集合。在我们的服务器中,需要客户机来提供 XSL 文件名。添加一个新的转换规则与将 XSL 程序放入目录中一样简单。

典型的消息可能类似于清单 1。


清单 1. 典型消息
<message>
   <header>
      <status>OK</status>
      <command>GET</command>
      <queueName>inbound</queueName>
      <processingRule>sortinvoice</processingRule>
      <messageId>0000001</messageId>
   </header>
   <payload>
      <orders>
         <order>
            <itemNumber>83726</itemNumber>
            <quantity>2</quantity>
            <price>10.00</price>
         </order>
         <order>
            <itemNumber>83432</itemNumber>
            <quantity>2</quantity>
            <price>10.00</price>
         </order>
      </orders>
   </payload>
</message>

这个 XSL 样式表将按商品号对订单 XML 进行排序,如 清单 2所示。





回页首


消息服务器特性

消息服务器具有很多特性。遗憾的是,我们必须保持这个实现的基本框架。但是,这里简要概述一下消息服务器所具有的一些特性。消息服务器可以支持:

  • 持久性:通常,服务器崩溃时,消息服务器通过将消息写入磁盘来支持持久性。
  • 智能消息路由:这个功能为消息服务器提供了确定使用哪一个队列来存储消息的判断能力。它甚至可以将消息转发给另一个消息服务器。
  • 同步和异步通信:选择同步通信的客户机将等待响应。当另一个客户机立即拾取消息并在队列上放置响应消息来响应时,同步通信是适合的。异步通信允许客户机在任何适当的时候拾取响应。




回页首


实质

让我们研究一下具体细节。我将带您了解一下这个服务器关键部分的代码。如果您想尝试或扩展其功能,则可以下载代码(请参阅 参考资料)。下载包括服务器、客户机代码和调用客户机的示例。

让我们在 清单 3 中研究一下消息服务器本身。在它能够侦听客户机之前,必须进行一些内部操作。服务器将 XML 配置文件读入 Config 对象中以获取端口号、队列名和记录切换。 Config 对象将 config.xml 读入 DOM 对象并提供读方法(getter)以获取配置值。队列仅是散列表。 Container 也是散列表,这些队列作为对象放入容器散列表中。所有内部处理都在 MessageServer 构造器中完成。最后一个内部处理作业是以期望端口号绑定到服务器套接字上。

现在,服务器准备好侦听客户机连接。 清单 4 中显示的 MessageServer run() 方法建立了一个无限循环以等待客户机连接。当检测到客户机连接时,服务器启动传递给 queueContainerclientSocketloggingSwQueueManager 线程。在这背后,Java 网络 API 为该套接字重新分配一个未用的端口。释放这个服务器端口以侦听另一个连接,然后,这又会启动另一个 QueueManager 线程以为新的客户机提供服务。

将处理消息的作业委派给 QueueManager 。让我们看一下 QueueManager 类。 QueueManager 的构造器有一个打开客户机输入和输出流的简单任务。实际工作在 run() 方法中完成,如 清单 5 所示。这个新线程从套接字中读取消息,并使用结果 String 来构建 Message 对象。该线程搜索 Container 以查找队列。如果在 Container 找到 Queue ,则线程将 Message 对象放在队列上( PUT ),或者从队列检索并除去 Message 对象( GET ),或者清除队列( DEL ),执行什么操作取决于由客户机发送的命令。 QueueManager 总是将确认以消息形式返回客户机。

清单 6 中显示的 readSocket() 是要讨论的一种有趣的方法。我们想使用同一个套接字连接来读取入站消息并发送回确认消息。因此,我们需要能表明我们已经读取了整个入站消息并且我们并不在等待客户机发送剩余的入站消息。我们如何知道客户机何时完成消息发送呢?我们采用与 HTTP 协议相同的方法。客户机先发送消息的长度,然后是回车符,接着是这条消息。逐字节读取套接字,直到收到规定长度的消息。HTTP 采取与此相同的方法:在 POST 请求上带有 Content-Length HTTP 头。

此时,我们的服务器可以检测客户机连接, QueueManager 可以存储并返回消息。消息被很好地封装在 Message 对象中。让我们研究一下 Message 类来查看它是什么。

Message 类实现两个构造器,如 清单 7 所示。单个 String 参数构造器使用字符串作为输入来构建 XML DOM 对象。不带参数的构造器构建一个标记中不包含任何值的 DOM 对象。每个 XML 标记具有一个读方法和一个设置器,它用于抽象化 Message 对象的 XML 实现。

检索 DOM 对象中的标志值,可以考虑使用两种方法。一种方法取决于 DOM 对象的 XML 结构的静态定义。另一种设计成采用灵活的定义。为了简洁起见,在 清单 8中实现了前一种方法。(关于这一主题的更好描述,请参阅 参考资料中的“Lowering the bar of the DOM API”)。





回页首


转换

客户机可以通过在 GET 请求上规定的处理规则来请求消息转换。在 清单 9 中显示的 QueueManager 检查处理规则以确定对于正在从队列检索的消息应用哪条规则。 QueueManager 将“.xsl”附加到处理规则以从文件系统检索 XSL 样式表。然后,调用 XSLT 处理器,这很简单,如 清单 10所示。





回页首


结束语

这个消息服务器是轻量级的,并且它满足办公室中任何一台老式机器上运行的需求。这个消息服务器之所以能做到这一点,是因为它不需要在传输协议(例如,HTTP 或 SMTP)上实现服务器。不需要 Web 服务器或 servlet 引擎。只需要支持 XSL 的 JVM 和 XML 解析器。

我们需要实现一个简单的传输协议来解决通常需要使用 HTTP 或其它传输协议才能解决的问题。客户机需要知道要发送和要接收什么。如果我们想增加命令集,那么,将需要对代码做一些更改。(通过使用 Java Reflection API 来发现实现命令的方法,可以将这些更改降低到最低程度)。

幸运的是,SOAP(简单对象应用程序协议)为我们提供了一种替代方法。如果我们愿意在传输协议(例如,HTTP 或 SMTP)上实现,则可以使整个事情更加简单。回报是我们现在可以使用一种流行的基于 XML 的协议,它使我们很容易地扩展命令集,而不必使用 Java Reflection API。SOAP 处理消息传递基础结构的格式,HTTP 处理传输需求。使用消息格式化和需要处理的传输需要,我们唯一担心的是有效负载。整个应用程序只需要本文中所提到的代码的一小部分。



参考资料



关于作者

George Franciscus 住在加拿大多伦多,是一名的技术和管理顾问,他在多个项目上使用 Java、XML、WebSphere 和 Lotus Domino。George 是 Sun 认证的 Java 程序员、IBM 认证的开发者 ― XML 和相关技术、R5 R4 Principle Domino 开发者、R5 R4 Principle Domino 管理员和 Microsoft 认证专家(MCP)。可以通过 George_Franciscus@yahoo.com与他联系。




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

建议?







回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款