级别: 初级 Graham Glass, CEO/首席设计师
2001 年 1 月 01 日 这篇文章向您解释 SOAP 是如何工作的。包括有关它的线上协议和如何处理消息的信息。本文还解释对象是如何通过值在 Web 服务间被传递的,并涉及到性能和安全性问题。这篇文章向您解释 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
服务器(可以在
参考资料中找到它最新的发行版),输入:
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 的
<return> 部分。
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
和他联系。
|
对本文的评价
|