深蓝海域KMPRO

Web服务的(革)创新,第3部分

2002-08-26 10:10

Web服务的(革)创新,第3部分

--SOAP 是如何工作的


Graham Glass

CEO/首席设计师,The Mind Electric

2001 年 1 月

这篇文章向您解释 SOAP 是如何工作的,包括有关它的线上协议和如何处理消息的信息。本文还解释对象是如何通过值在 Web 服务间被传递的,并涉及到性能和安全性问题。

欢迎进入本专栏的第 3 部分,本专栏重点讲述 Web 服务技术的革新和创新方面。在第 2 部分中,我说明了如何使用 Apache 简单对象访问协议 (SOAP) 来建立、部署和调用一个简单的 Web 服务。在这个部分中,我将说明 SOAP 如何在后台工作,从技术角度来讲这很有趣,它将有助于揭开我们将要讨论的未来标准的神秘面纱,例如 Web 服务描述语言 (WSDL) 和通用描述、发现和集成 (UDDI) 标准(请参阅参考资料)。

工具和安装

由于我们使用和前一部分相同的工具,因此无需安装新软件。另外,我们将沿用前一部分的示例,所以请确定您还存有 \demo1 目录 下的 IExchange.java、Exchange.java、Client.java 源文件。

这次我要介绍的一个新的软件工具是 TCP tunneling GUI,它可以让您看到 TCP 消息如何在客户端和服务器间传送。使用此工具可检查 SOAP 线上协议甚至检验相互竞争的各种 SOAP 实现是否是符合标准的。

初探 SOAP

TCP tunneling GUI 通过作为一个 TCP 路由器来使您“初探” SOAP 消息。为了看它的实际运行,先启动在 \demo1 目录下的 Apache Tomcat 服务器(可以在参考资料中找到它最新的发行版),输入:

> cd \demo1
> tomcat run

Tomcat 缺省在 8080 端口启动。然后在另一个窗口启动 TCP tunneler,输入:

> java org.apache.soap.util.net.TcpTunnelGui 8070 localhost 8080

这会在 8070 端口启动一个 TCP 服务器,它在任意一个客户端与本机的 8080 端口(在本例中,由 Tomcat 提供)之间扮演一个中介的角色。其 GUI(请参阅图 1)将在左边的面板里显示外发消息并在右边的面板里显示接收的回复消息。

图 1:TCP tunneler界面


现在编辑以前示例中的 Client.java 代码,将 Web 服务 URL 改为使用 8070 端口而不是 8080 端口。客户端程序的第一行应该如下:

URL url = new URL( "http://localhost:8070/soap/servlet/rpcrouter" );

然后编译和运行客户端。GUI 显示如图 2。

图 2:运行tunneler


如你们所见,左手边的面板显示了外发的 SOAP 请求,它包含 5 行标准 HTTP 头,后面接着是代表客户端服务调用的 XML 文档。右手边的面板显示了 SOAP 响应的结果,它包含 6 行标准 HTTP 头,接着是 XML 文档表示的服务器响应。即使没有 SOAP 格式的解释,您也可以猜出它的大部分意思。您可以将这与二进制、非自描述的和难于跟踪的 CORBA 和 DCOM 协议进行比较。我较早接触这两个协议,也曾写过一个 CORBA ORB。

SOAP 请求剖析

首先要提的是尽管这个特殊的设置是使用 HTTP 传递 SOAP 消息的,但 SOAP 可以运行在任何其它传输协议上。例如,您可以使用 SMTP,即因特网电子邮件协议来传递 SOAP 消息。在传输层之间的头是不同的,但 XML 有效负载保持相同。清单 1 所示的是一个完整的 SOAP/HTTP 请求,为了更直观一些,XML 内容全部是缩进格式的。

一个 SOAP 请求作为 HTTP POST 被发送,同时其内容类型设成 text/xml,一个叫 SOAPAction 的字段设为空字符串或 SOAP 方法的命名。SOAPAction 字段允许负责接收的Web服务器检测该请求是一个SOAP 消息并潜在地发送或过滤这个消息。

SOAP 请求的 XML 部分包含三个主要部分:

Envelope 定义各个 SOAP 消息的余下部分会使用的 namespaces,典型的有 xmlns:SOAP-ENV(SOAP Envelope namespace)、xmlns:xsi(XML Schema for Instances) 和 xmlns:xsd(XML Schema for DataTypes)。

Header 是可选的元素,它携带认证、事务处理和支付的辅助信息。一个 SOAP 处理链中的任一元素可增加或删除 Header 里的项;元素也可选择忽略它们不认识的项。如果 Header被使用,它必须是 Envelope 的第一个子元素。因为我们的示例简单,不涉及路由器,所以不需要 Header。
Body 是消息的主要有效载体。当 SOAP 被用于执行一个 RPC 调用时,Body 包含一个单独元素,这个元素包含方法名、参数和 Web 服务的目标地址。元素的 namespace 等于目标地址,根名是方法名。在这个示例中,ns1:getRate 表示目标地址是 urn:demo1:exchange (ns1 的扩展形式),方法名是 getRate。如果有 Header,Body 必须紧接其后,否则它必须是 Envelope 的第一个子元素。

当使用 SOAP 作为一个远程过程调用 (RPC) 系统时,SOAP 参数可以是有类型的或无类型的。当前版本的 Apache 只接受有类型参数,正在开发的某个版本将来也许可以完全允许无类型参数。缺省的 SOAP 编码模式使用 xsi:type 属性来表示一个 XSD 类型。XSD 定义这几个基本类型:int、byte、short、 boolean、string、float、double、 date、time 和 URL。它也指定了发送数组和不透明数据块的格式。

因为我们希望SOAP平台和语言无关,所以 XSD 没有为某种个别语言才有的编码对象和结构定义格式。在本文的后面,我将会展示如何在都运行 Apache SOAP 2.0 的两台机器之间发送 java 对象。

SOAP/HTTP 消息都被运行在一台 Web 服务器上的 servlet 所接受。在本示例中,Apache 2.0 在 Tomcat 的soap/servlet/rpcrouter下安装了一个 servlet,它负责接受 HTTP 请求。当此 servlet 得到一个请求,它先检查这个请求是否有 SOAPAction 字段。如果有, 它将其转发给 Apache SOAP 引擎,然后此引擎分析 XML 有效载体,并在其本地注册表(启动时从 DeployedServices.ds 文件读入)上使用目标 Web 服务地址来执行查询。然后它使用内省机制来确定服务位置,并调用此服务上的指定的方法以获得结果。

下一节描述了用于将结果返回给客户端的 SOAP/HTTP 响应的格式 。

SOAP 响应剖析

一个 SOAP/HTTP 响应(请参阅清单 2)在一个标准 HTTP 回应内以一个 XML 文档返回,标准 HTTP 回应的内容类型被设定为 text/xml。除了它的 Body 包含被编码的方法结果之外,XML 文档结构和请求时的结构很像。结果的命名空间是原始目标对象 URI,根名是被调用的方法名。XSI/XSD 标记模式被选择性地用来指示结果类型(请参阅参考资料)。SOAP 标准没有详细指出从一个 void 方法中返回什么,目前大多数的实现省略了 Body 的 部分。

SOAP 异常

如果消息处理过程中有异常出现,一个 SOAP 异常就被扔出。SOAP 异常的编码方式和常规 SOAP 响应相似,不同的是由 Body 来包含有关异常的信息。为了阐明这一点,请编辑 Exchange.java 文件,它描述了我们的货币兑换 Web 服务并迫使它抛出一个异常,如清单 3 所示。

清单 3:一个 SOAP 异常

public class Exchange implements IExchange
{
public float getRate( String country1, String country2 )
{
throw new RuntimeException( "cannot calculate rate" );
/*
System.out.println( "getRate( " + country1 + ", " + country2 + " )" );
return 144.52F; // always return the same value for now
*/
}
}

然后停掉并重启 Tomcat ,这样它就使用新版本的 Web 服务。使用 Client 程序调用服务。TCP tunneling GUI 的输出显示在清单 3 中。

清单 3:SOAP 异常的tunneler输出

标准的 HTTP 回应头使用状态码 500 来表示一个异常。就像一个常规响应一样,XML 有效载体包含一个 Envelope 和 Body,不同的是 Body 的内容是一个 Fault 结构,它的字段定义如下:

Faultcode 表示错误类型的代码。有效值是 SOAP-ENV:Client (错误的构造消息)、SOAP-ENV:Server (传递问题)、SOAP-ENV:VersionMismatch (Envelope 元素的非法命名空间)和 SOAP-ENV:MustUnderstand (错误处理 header 内容)。
Faultstring让人易读的错误的描述。

Faultactor 是可选的字段,它表示错误源的 URI。

Detail 是特定应用的 XML,它包括详细的错误信息。

一些 SOAP 执行使用 Detail 元素将有关远程异常的信息进行编码,比如它们的类型、数据和堆栈跟踪,这样它们可以在客户端再次被自动扔出。这允许使用 SOAP 来实现RMI风格的远程异常。当然,如果客户端和服务器不使用同样的 SOAP 实现,这个功能会被自动禁止。

性能

既然已经知道了 SOAP 是如何使用 HTTP 和 XML 来回传递消息的,那么考虑性能问题应该会很有趣。

CORBA、DCOM 和 RMI 对参数和返回值使用二进制编码。除此之外,他们假设发送端和接收端充分了解消息的前后关系,因此对诸如参数名称或类型的任何元信息都不编码。这种方法产生了良好的性能,但使中介很难处理消息。因为每个系统使用不同的二进制编码,所以建立互操作的系统很难。

而 SOAP 用 XML 将消息编码,因此在调用过程的任何一步都极易处理消息。另外,调试 SOAP 消息的方便性使各种 SOAP 执行能快速聚合在一起,这点很重要因为 SOAP 就是要达到大范围的协同工作。

表面看来,基于 XML 的模式本应比基于二进制的慢,但它并不像表面那么简单。首先,当 SOAP 被用于通过因特网发送消息时,在每个端点给消息编码/解码的时间与在端点间传输字节的时间相比较是微不足道的,所以这种情况下使用 XML 没太大问题。

其次,当 SOAP 用于封闭环境下的点对点间的消息传送,如在同一公司部门间的传送时,各端点可能将运行相同的 SOAP 执行。这样,这个特定执行就拥有专门的优化机会。例如,一个 SOAP 客户端可添加一个 HTTP header 标记到 SOAP 请求上,这个请求说明它支持一个特定的优化。如果 SOAP 服务器也支持那个优化,它会在第一个 SOAP 响应中返回一个 HTTP header 标记,告诉客户端可以在下面的通信中使用这种优化。接下来,客户端和服务器可以开始使用这种优化了。

在我对 Apache 的实验中,我通常在同样的机器上得到每秒 30 次 Java 程序间的消息往返。我还评测过另一个 SOAP 执行,它在同样的配置下进行了每秒 700 次消息往返,所以改进的余地显然很大。

传递对象

一旦建立和部署一个简单的服务的最初兴奋逐渐减弱,很多开发人员希望开始在客户端和服务器间发送对象。实现这项任务至少有两种方法。

如果能保证客户端和服务器配置相同的专有扩展名,您就能使用任何想要的方法在机器间传递对象。例如,客户端和服务器都在运行 Java 编程语言,它们可同意使用 Java 串行化来传送对象。

另一方面,如果想要系统使用任意客户端和服务器的组合,请在开始时为想要传送的对象指定 XML 模式。然后确定客户端和服务器都配置成可识别符合这些模式的 XML 文档,并在需要时能将它们转换为对象或从对象中转换出来。例如,如果 C++ 客户端想要传送一个对象至 Java 服务器,客户端必须能把 C++ 对象转换到指定模式或从指定模式中转换出,而服务器必须能把 Java 对象转换到指定模式或从指定模式中转换出。

通过包含一个特殊的映射注册表,Apache SOAP 2.0 包含一个可以在 SOAP 引擎之间传递对象的灵活模式。您可以注册自己的序列化对象来对数据加解码。当前的文档对怎样建立您自己的串行化的信息涉及较少,但还好它包含了一个有用的缺省 Java BeanSerializer,它可以串行化/去串行化任何符合 JavaBean 规范的类 -- 即它必须包含公共的缺省构造函数,并为它的所有属性声明公开的get/set方法。

下面的程序向您说明怎样发送和接收对象。在 Tomcat 服务器上部署一个购买的Web 服务,然后客户端以值的形式发送一个发货单对象。对象抵达服务器时被显示出来,返回到客户端时再被显示。您也可以在 SOAP 下通过引用传递对象,我在本专栏的下一部分中将对此作解释。

清单 4 至 7 给出了购买 Web 服务的源代码样本。

清单 4. IPurchasing.java 的代码

public interface IPurchasing
{
Invoice receive( Invoice invoice );
}

清单 5. Purchasing.java 的代码

public class Purchasing implements IPurchasing
{
public Invoice receive( Invoice invoice )
{
System.out.println( "got invoice " + invoice );
return invoice;
}
}

清单 6. Invoice.java 的代码

public class Invoice
{
String name;
int amount;

public Invoice()
{
}

public Invoice( String name, int amount )
{
this.name = name;
this.amount = amount;
}

public String toString()
{
return "Invoice( " + name + ", " + amount + " )";
}

public void setName( String name )
{
this.name = name;
}

public String getName()
{
return name;
}

public void setAmount( int amount )
{
this.amount = amount;
}

public int getAmount()
{
return amount;
}
}

清单 7. Client2.java 的代码

运行程序,需要执行如下步骤:

把以下的 Java 文件复制到一个新的目录:\demo2 中,并把 \demo2 加到您的 CLASSPATH 上。

编译源文件。

同前,运行 TCP tunneling GUI,侦听在 8070 端口和 8080 端口传递的消息。

在 \demo2 目录下启动 Tomcat。

打开浏览器,输入 URL
http://localhost:8080/soap

使用管理界面来部署一个购买服务。输入以下值:

ID=urn:demo2:purchasing
Scope=Request
Methods=receive
Provider Type=Java
Provider Class=Purchasing
Static=No
Number of Mappings=1
Encoding Style=SOAP
Namespace URI=urn:my_encoding
Local Part=Invoice
Java Type=Invoice
Java To XML Serializer=org.apache.soap.encoding.soapenc.BeanSerializer
XML To Java Deserializer=org.apache.soap.encoding.soapenc.BeanSerializer
运行客户端。

Namespace URI 和 Local Part 值被 Apache SOAP 引擎作为 xsi:type 的属性值。只要这些值不和任何其它您使用的映射冲突,它们是什么并不特别重要。

如果一切顺利,您该看到图 4 中 TCP GUI 的结果,以及图 5 中客户端的输出。

图 4:运行购买 Web 服务


图 5:购买 Web 服务客户端的输出窗口


安全性

安全性是个复杂的问题,目前确保 SOAP 的安全性需要采取分层的方法。

在最低层,SOAP 消息可通过 HTTPS 传递。既然 HTTPS 使用 SSL 传输,这就确保被编码消息内容可以避免被窃听,也确保客户端和服务器可互相验证身份。如今,基于 SSL 的 SOAP 解决方案出现了,但安装和配置还不太容易。

尽管 HTTPS 解决了对窃听者屏蔽消息的问题,但对那种需要更佳安全性来认证特殊用户的特定 Web 服务的帮助并不大。很多 Web 服务需要用户在一开始注册期间获得某种用户/密码组合,然后当以后访问时就使用这个认证信息。IBM、Microsoft 和 Ariba 在线托管的 UDDI Web 服务注册表就是需要用户在使用发布服务前先注册的 Web 服务例子。还没有标准规定 Web 服务如何支持注册和认证,但毫无疑问这个标准很快就会有了。Microsoft 和 Verisign 最近宣布了他们正在研究一个标准,叫做 XML 密钥管理规范 (XKMS),他们将把它递交给标准体系以便有可能运用于 SOAP 和其它系统的整合中(请参阅参考资料)。

我打算在此栏目的今后部分中投入更多时间讨论安全性的话题,到时希望在安全标准方面有所进展。

下一部分

在下一部分中,我将介绍 WSDL (Web 服务描述语言)和 UDDI (通用描述、发现和集成)这两个重要的标准,它们将有助于推动 Web 服务技术进入主流舞台。

参考资料

关于作者

Graham Glass 是 The Mind Electric 的创始人、CEO 和首席设计师。该公司设计、构建和特许前瞻性的分步式计算基础设施。他相信,因特网的演变将反映出生物思维的演变,协助人们和企业有效联网的体系结构能帮助人们理解将人脑联结在一起的体系结构。
在创建 The Mind Electric 之前,Graham 是 ObjectSpace 的主席、CTO 和联合发起人之一。该公司总部位于达拉斯,专门从事商家到商家的集成。Graham 还是 ObjectLesson(一家提供前沿技术培训的公司)的创办人。他为 Prentice Hall 撰写了两本有关 UNIX 和 STL 的书籍,并以他对新兴技术的热情和清晰阐述而成为受欢迎的演说家。可通过
graham-glass@mindspring.com 和他联系。

浏览:Web服务的(革)创新,第1部分

Web服务的(革)创新,第2部分

Web服务的(革)创新,第4部分

相关推荐