您现在的位置: 启天教育 >> 技术中心 >> XML >> 正文
今天是:
[组图]压缩SOAP Message的方法
压缩SOAP Message的方法
作者:未知    文章来源:转载    点击数:    更新时间:2006-5-15

  随着Web Service如火如荼的应用,SOAP(Simple Object Access Protocol)作为一种数据交换协议,也得到了大量的推广。然而用SOAP进行数据传输时,实际上传输的是XML文本信息,对于大部分企业数据传输来说,文本数据传输量就非常庞大。现在的很多企业应用都架构在广域网上,庞大数据传输量就限制了SOAP的应用。那么如何提高SOAP Message在网间的传输效率呢?一个可行的方式是对SOAP Message进行压缩。本文主要介绍压缩SOAP Message的方法。

  SOAP简介及压缩的基本原理

  SOAP是一种基于XML的简单对象交换协议,SOAP Message实际上是一段XML文本,可以用多种协议进行传输,如HTTP、 FTP甚至SMTP,总之只要能把文本发送给服务器端便可。如此看来,要加速SOAP传输,减少传输的数据量,可以从两个方面处理:一是对XML进行处理,使其文本信息更紧凑;二是对网络传输中的二进制数据流进行处理,使其传输数据量减少。下面将分别介绍两种方式。

  1. SOAP Message文本信息的压缩

  以下的SOAP文档可以帮助我们了解SOAP Message:

<?xml version=‘1.0’encoding=‘UTF-8’?>
<SOAP-ENV:Envelope
  xmlns:SOAP-ENV=“http://schemas.xmlsoap.org/soap/envelope/”
  xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
  xmlns:xsd=“http://www.w3.org/2001/XMLSchema”>
  <SOAP-ENV:Header>
  <ns1:PaymentAccount xmlns:ns1=“urn:ecerami”SOAP-ENV:
    mustUnderstand=“true”>
  orsenigo473
  </ns1:PaymentAccount >
  </SOAP-ENV:Header>  
  <SOAP-ENV:Body>
    <ns1:getTempResponse
    xmlns:ns1=“urn:xmethods-Temperature”
    SOAP-ENV:encodingStyle=“http://schemas.xmlsoap.org/soap/encoding/”>
    <return xsi:type=“xsd:float”>71.0</return>
   </ns1:getTempResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

  它包含了Header、Body两个部分,<SOAP-ENV:Header>……</SOAP-ENV:Header>是SOAP Message的Header部分。在SOAP中,其存在的意义是提供一个灵活的框架,以满足应用程序级的附加需求,例如在此提供权限验证、事务管理等信息。对于SOAP Message来说,它是一个可选的部分,<SOAP-ENV:Body>……</SOAP-ENV:Body>是SOAP Message的Body部分。这里包含着应用程序调用信息。我们要传递的信息通常放在此处。因为这里的内容最长,所以通常将这一部分的内容压缩。将原内容重新编码形成一个新的压缩文本字符串,将其加入到SOAP Message的附件域中(也可将其放入XML元素的的Cdata域),其示意图见图1。

  图1 基于文本的SOAP压缩示意图

  先将SOAP Body域的内容压缩成二进制数据,再采用BASE64将二进制数据编码成MIME(Multipurpose Internet Mail Extensions,多用途Internet邮件扩展)文本,压缩后的MIME文本内容放入SOAP Message的Attachment域中,再在SOAP Body中加入压缩标记,形成类似于下面的SOAP文本:

<SOAP-ENV:Envelope xmlns:SOAP-ENV=“http://schemas。
  xmlSOAP。org/SOAP/envelope/”>
<SOAP-ENV:Body>
<COMPRESSED>YES</COMPRESSED>
< CONTENTLOCATION >ATTACHMENT</ CONTENTLOCATION >
<m:hello xmlns:m=“http://hello。world”>Bar </m:hello>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
<!-- This is compressed part-->
-------------PART ----------
CONTENT-TYPE:=mime/binary
8A9ASDF/ASDFKLASD9FUASDFASDF
-------------PART ----------

  Client端将此SOAP Message通过某种方式传给Server端。于是,Server端收到SOAP Message之后,检查SOAP Body域的COMPRESSED标志,如果是“YES”,则对SOAP附件部分进行BASE64解码,然后解压,还原成原始的SOAP Message。

  2. 网络传输过程中二进制数据的压缩

  我们知道采用HTTP、FTP等基于连接的协议进行数据传输,其底层实际上都是基于流上的数据传输。既然如此,我们便可在流这一层实现压缩,将InputStream和OutputStream 用GZIPInputStream和GZIPOutputStream进行包裹,这样对于上层的数据读写完全透明。由于流是基于二进制传输的,所以无需进行Base64的编码与解码。实际测试用GZIPInputStream和GZIPOutputStream进行压缩和解压一个4Kb大小的 XML文档大约是7~8ms(P4:1.7GHz),而如果在中间加入Base64编码与解码,则要用120ms的时间。从时间效率来说,在流中对二进制数据进行压缩要比对SOAP Message的文本信息进行压缩高很多。下面的一段源码是使用GZIPInputStreamt与GZIPOutputStream进行压缩与解压的具体方法:

public static byte[] compressBytes(byte[] bytes)
        throws IOException {
        ByteArrayOutputStream baos = null;
        baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(new 
	  GZIPOutputStream(baos));
        dos.write(bytes);
        dos.close();
        return baos.toByteArray();
    }
    public static byte[] decompressBytes(byte[] bytes)
        throws IOException {
        GZIPInputStream gis = new GZIPInputStream(new 
	  ByteArrayInputStream(
            bytes));
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buf = new byte[1024];
        int len;
        while ( (len = gis.read(buf)) >= 0) {
            baos.write(buf, 0, len);
        }
        gis.close();
        baos.close();
        return baos.toByteArray();
    }

  Sun在Java Web Service Developer Pack中提供两个重要的开发包JAXM(Java API for XML Messaging)与JAX-RPC(Java API for XML-based RPC)。其中JAXM是一个相对底层的API,它提供了一组API来创建、使用在各种传输机制上的交换SOAP包。而JAX-RPC则与RMI比较类似,通过一些简单的程序与描述文件来生成代码。下面主要介绍基于JAXM的SOAP Message压缩。

  基于JAXM压缩方法的实现

  图2 JAXM压缩方法框架图

  图2描述了此实现方法的一个大的框架。此处采用Abstract Factory模式,由SOAPInvokerFactory的子类工厂来生产SOAPInvoker的实现类,SOAPInvoker的实现类负责具体的SOAP Message发送与接收。这样做的好处是可以适应以后Web Service调用方式的变化,而不是局限于使用JAXM。SOAPInvokerFactory类及SOAPInvoker接口的基本描述如下:

abstract class SOAPInvokerFactory{
 static  final public SOAPInvokerFactory getInstance();
 public SOAPInvoker createSOAPInvoker();
 .........
 .........
 public setInvokerMethod(...)//在此设置调用web service的方式
 public setCompressThreshold(int)//压缩阀限,即超过多少size才进行压缩

}
interface SOAPInvoker {
 public SOAPMessage call(SOAPMessage, URLEndpoint);
 public setInvokerMethod(...)//在此设置调用web service的方式
 public setCompressThreshold(int)//压缩阀限,即超过多少size才进行压缩
}

  在SOAPInvoker实现类中,对SOAP的压缩与发送进行封装。用户的Client端程序只需使用其提供的call方法来调用Web Service。例如在SOAPInvoker实现类可以封装上面提到的“SOAP Message的文本信息的压缩”,以及“网络传输过程中二进制数据的压缩”两种压缩方法。下面主要介绍以JAXM调用方式为主的基于二进制数据传输压缩方法的实现。先让我们来了解JAXM的调用环境。典型的JAXM 调用环境见图3。

  图3 JAXM 调用环境

  即client端使用JAXM API发送SOAP Message,由JAXM API的实现类(JAXM Implemention)通过SOAP/HTTP协议发送到服务端一个Servlet,服务端Servlet则最终通过JAXM API将SOAP Message发送到接收端。通过对JAXM Implemention的修改与继承可以实现下面的压缩方式:

  1. 对文本进行压缩

  根据上面提到的“SOAP Message文本信息的压缩”的原理对其进行对于SOAP Message的文本压缩,在服务端的实现类(JAXM Implemention)里对SOAP信息进行解压,再将调用信息发送给接收端。这里不做详细介绍。

  2. 网络传输过程中二进制数据的压缩

  要实现传输过程二进制数据压缩,就要在JAXM的传输层做文章。JAXM本身只是一个规范,很方便不同的厂商自己实现。在JAXM中, SOAPConnectionFactory、 javax.xml.soap.SOAPConnection都可以进行配置,即通过修改$JAVA_HOME\lib\jaxm.properties文件,可让JAXM API使用我们的实现类。 打开$JAVA_HOME\lib\jaxm.propertie(如果没有,可以新建此文件),在文件中加入javax.xml.soap.SOAPConnectionFactory= CompressedHttpSOAPConnectionFactory,这样在执行下面的程序时就可以用到CompressedHttpSOAPConnectionFactory,将用它来产生CompressedHTTPSOAPConnection,通过CompressedHTTPSOAPConnection将实现压缩及与Server端的通信。创建SOAPConnection的程序如下:

try {
      SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance();
      SOAPConnection connection = scf.createConnection();
   }
   catch (SOAPException ex1) {
   }
   catch (UnsupportedOperationException ex1)     
   {
   }

  在CompressedHTTPSOAPConnection内,只需在流一级用GZIPInputStream或GZIPoutputStream进行包裹,即可实现流一级的压缩。通过对Sun的jaxm.jar和saaj.jar进行反编译,发现对于Sun的JAXM实现,只需在Client端用一个类CompressedHttpSOAPConnection继承com.sun.xml.messaging.saaj.client.p2p.HttpSOAP-Connection,覆盖HttpSOAPConnection 类的call方法,在call方法实现压缩即可。而Server端实际上是对Servlet的一种扩充,用一个Compressed-JAXMServlet继承JAXMServlet,并覆盖它的doPost方法,在CompressedJAXMServlet的doPost方法内实现解压,则可实现服务端的接收。具体的实现类图见图4。各元素说明如下:  

  图4 实现基本类图

  (1) SOAPInvokerFactory将使用Abstract Factory Pattern,根据配置文件来选择不同的Web Service调用方法,如用JAXM,则缺省产生JAXMSOAPInvoker;

  (2)用户程序在使用时只需调用SOAPInvoker-Factory的实例createSOAPInvoker方法,返回 SOAPInvoker对象;

  (3)由JAXMSOAPInvoker负责SOAPConnection的创建;

  (4)CompressedHTTPSOAPConnection、CompressedHttpSOAPConnectionFactory是对HTTPSOAPConnectioy,SOAPConnectionFactory的实现,CompressedHTTPSOAPConnection在底层实现压缩,而CompressedHttpSOAPConnectionFactory则是产生CompressedHttpSOAPConnection的工厂;

  (5)CompressedJAXMServlet继承JAXMServlet,覆盖了doPost()方法,将被压缩的GZipInputStream进行解压,再将调用传递给用作Web Service的子类;

  (6)CustomizedReqRespService、CustomizeOneWaydService是jaxm web service,负责完成商业逻辑。

  整个压缩源码比较长,这里只介绍一下核心的压缩代码,即客户端在写出流时:

OutputStream out = new GZIPOutputStream(httpConnection.getOutputStream());
message.writeTo(out);
out.finish();

  服务端在接收流时的代码如下:

javax.servlet.ServletInputStream servletinputstream =
  httpservletrequest.getInputStream();
SOAPMessage SOAPmessage = msgFactory.createMessage(mimeheaders,
  new GZIPInputStream(servletinputstream));

  对于其它的SOAP传输方式,也可以参考上面的实现方式。


文章录入:junsan    责任编辑:junsan 

  • 上一篇文章:
  • 下一篇文章:
  • 最新热点 最新推荐 相关文章
    没有相关文章
     网友评论:(最新10条。只代表网友观点,与本站立场无关!)