内容


压缩 SOAP

支持 GZIP 的 Apache Axis

Comments

HTTP 传输协议上的 GZIP 编码是一种众所周知的用来改进 Web 应用程序性能的技术。大流量的 Web 站点使用 GZIP 来让用户感受更快的速度,而压缩被广泛用于缩小下载和交换的文件。实际上,在 XML 所涉及的技术中,GZIP 甚至不是我们所能用到的最新的、优秀的压缩格式。新技术,如 ATT 的 XMill,声称在几乎同样的时间内可以完成 GZIP 压缩量的两倍。然而,GZIP 是 Java 平台的核心组件,许多 Web 服务器能够压缩内容而不论所提供的是什么文件或应用程序。由于这个原因,本文将讨论把 GZIP 和 Axis SOAP 实现结合在一起会发生什么情况。对于目前需要超常性能的项目和解决方案,以及您希望能减少以后与 SOAP 实现的后续发行版进行集成时所花的时间的项目和解决方案,这种办法被证明是很有用的。此外,本文将讨论 servlet 级别上的编码,它使您可以实现不同的内容编码方案。

开始

要执行任何这些改变,您必须要有 Apache Axis 的源代码。但您应该明白这种修改对于不同的 SOAP 实现是类似的。我将要修改底层的 HTTPSender 和 AxisServlet 代码。如果您的服务器可以处理压缩,servlet 将是不必要的;然而,在修改 servlet 时,框架恰能让您访问其他编码解决方案。

我们的目标是添加 GZIP 压缩而不必从功能上改变底层的 SOAP 实现。这相对比较简单,因为 java.util.zip 是基于流的。此外,Axis 和其他解决方案中用于建立 HTTP 连接的底层代码也是基于流的。

Java 技术和 GZIP 简述

在本文中,我将不涵盖关于使用 java.util.zip 包的详细信息。Sun 托管相关的内容,您可以在很多地方找到记载该内容的文档(请参阅 参考资料)。

java.util.zip 包提供对其他流进行编码时需要的所有东西。具体地说,您将使用 GZIPInputStreamGZIPOutputStream 对象。这两个对象都接受流作为自变量,同时只要您能够检查内容编码,您就可以恰当地对流进行包装。复杂的压缩和解压缩工作已经帮您做好。如果您希望以其他格式进行编码和解码,我建议您继承输入和输出流,这是很自然的事情。

在服务器端有两种处理内容编码的方式。首先,也是更为合适的,是由应用程序服务器前端的 Web 服务器处理压缩。您可以配置 Web 服务器,使它标识能够支持压缩内容编码的客户机,然后对以这种格式发出请求的输出内容进行编码。这种方式将动态地执行,而且在大多数情况下,在现有的硬件上运行时,这将添加数量不大的开销。可用的第二种方法是在应用程序自身中对内容进行编码。相对于第一种方法,这种方法可能在性能、所需的代码数量和必要的维护方面需要更大的开销。但您可以很容易地利用 CGI 或 servlet 来判断接受到的编码类型,然后发送采用适当编码的响应。如果您使用的是在 Web 服务器中不常见的编码类型,这种方法将会很有用。最后,负担将从网络转移给服务器和客户机。然而,这个负担是否比因通过数据线传送一半左右的数据而获得的性能小,还存在争议。

我们的目的是实现透明化;拦截输入流和输出流,并通过相应的 GZIP 流进行传送。简单极了,所以我要开始啦!

从服务器着手:修改 AxisServlet

这个部分涵盖了如何修改 AxisServlet 来支持压缩。如果您无法访问 HTTP 服务器配置或服务器无法支持压缩,您将会希望使用这个方法。这个技术可以很容易地成为以其他风格进行编码的起点。

在应用程序层而非 Web 服务器层进行编码是很简单的。如果您打算使用常用的服务器/模块不支持的编码方案,这种方法将会特别有用。就像本文的所有代码修改一样,这些修改可以通过不同途径实现。例如,您可以继承 AxisServlet 并实现 HTTPServletResponse 来拦截传出的输出流。

首先,修改 doPost 方法来测试 Accept-Encoding 头。如果客户机显示它接受 GZIP 编码,那我就将这件事交给客户机去做。查看 user-agent 头来确认只为能处理压缩的客户机进行了压缩。但这种关心没什么必要,因为大多数浏览器不会用到 RPCRouter。如果 GZIP 编码被接受,我将把布尔变量 supportsGzip 设置为 true。代码见 清单 1

清单 1. AxisServlet(doPost)

String encoding = req.getHeader("Accept-Encoding"); 
        
if (encoding != null) {
if (encoding.toLowerCase().indexOf("gzip") > -1){supportsGzip = true;}
}

然后,浏览找到 sendResponse 方法。我将添加一些代码来查看当前的连接是否支持 GZIP。如果支持,我将把响应头 Content-Encoding 设置为 gzip 并用 GZIP 输出流把响应输出流包装起来。从这一点来说,除了您将需要清空输出流(否则流缓冲区的最后一位可能会未被发送)之外,一切都很相似。不管调用什么样的底层 Web 服务,这一修改都能提供 GZIP 编码。支持专门的内容编码应是相当容易的。请参阅 清单 2中的代码。

清单 2. AxisServlet(sendResponse)

if (supportsGzip == true) {
        
// Set the response header.
// Note that response is an HttpResponse instance.

        res.setHeader("Content-Encoding", "gzip");
    
        GZIPOutputStream gzos = new GZIPOutputStream(res.getOutputStream());
            
        responseMsg.writeTo(gzos);
        gzos.flush();
        gzos.close();
            
  
        //gzip code end 
               
} else {
              
        res.setContentType(contentType);
        /* My understand of Content-Length
         * HTTP 1.0
         *   -Required for requests, but optional for responses.
         * HTTP 1.1
         *  - Either Content-Length or HTTP Chunking is required.
         *   Most servlet engines will do chunking 
             if content-length is not specified.
         */
                
         responseMsg.writeTo(res.getOutputStream());         
}

从客户机着手:修改 HTTPSender

如果您不熟悉 Axis 源代码,您将会注意到一件事:开发者不可能仅继承其他人的 HTTP 客户机对象。实际上他们自己编写 HTTP 客户机的套接字级别的实现。我确信这么做有许多理由,其中一个明显的理由是:它使修改(就像我一会儿要做的修改那样)不费吹灰之力。我感兴趣的两个方法是:writeToSocket 和 readFromSocket 。

Hello world,我接受 GZIP!


我首先需要做的是修改客户机来向服务器表示该客户机可以处理压缩。这里需切记的是:并不是所有的 Web 客户机都可以处理它们宣称的编码。如果您使用的服务器不允许您验证它引用的编码规则,我建议您对在应用程序级别上编码的 servlet 进行测试。很可能服务器被设置来匹配特定的用户客户机,也就是我不打算复制的那些。如果您访问了这样的服务器,请确认服务器是针对您的用户代理进行编码的。简单地看一下日志或 SOAP 跟踪很可能就会发现:用户代理头未被设置。如果想让事情更简单,就应对用户代理头进行设置。不过,我将把这些留给您自己去完成。

我将把这个头设置成 Accept-Encoding 。这个 HTTP 头存储字符串值,并且它通常为一系列值。请参阅 清单 3。我将使用 gzip 值。因此,如果我希望添加头,我将需要浏览 HTTPSender 代码。在这里对头进行处理。迅速浏览一下,您可以看到有一个用来添加头的框架,但此时 Accept-Encoding 并非开发者需要处理的头。在本文中,我将按硬编码方式处理编码。如何集成或设置 HTTP 头完全取决于您。有许多解决这个问题的方法。我的做法是插入 清单 4中的代码片段。在强制转型后,代码通过拉操作获得请求头并将它们强制转型为散列表。不论是由 Web 服务器还是应用程序处理,我将添加一个头来在服务器端触发压缩过程。

清单 3. 从 Internet Explorer 传到 www.ibm.com 的样本 HTTP GET。

GET / HTTP/1.1
Accept: */*
Accept-Language: en-us
        Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705)
Host: www.ibm.com:80
Connection: Keep-Alive

清单 4. HTTPSender.java(writeToSocket)中的代码片段

//process user defined headers for information.
Hashtable userHeaderTable = 
(Hashtable) msgContext.getProperty(HTTPConstants.REQUEST_HEADERS);
                
//Adding Accept-Encoding header to the hashtable.
if(userHeaderTable == null){userHeaderTable = 
new Hashtable();} userHeaderTable.put("Accept-Encoding", "gzip");

获取您所希望的!


通过修改 HTTP 请求添加值为 GZIP 的 Accept-Encoding 头之后,我需要修改用来读取内容的方法。我也需要检查 HTTP 头 content-encoding 来看看服务器是否用 GZIP 响应。如果是,以 GZIP 输入流的形式来包装输入流。另一方面,如果 GZIP 编码未被检测到,我们就不对输入流再作处理。这段代码放在什么地方非常重要,因为您不希望干扰 Axis 开发者使用的当前进程。我们希望恰好在对 HTTP 消息体进行解码之前,而在解析了头之后,对输入流进行修改。请记住,在包装套接字之前您需要了解内容编码。 清单 5 中的代码显示了应将 GZIP 代码插入在 readFromSocket 方法的什么地方。请注意我声明了一个 InputStream 。它将可能是 GZIPInputStream 类型或通过参数传入的 InputStream 类型。Axis 无法从这一点知道它是不是正从压缩流中读取数据,它也不应该知道。您现在有了一个以对底层代码透明的方式请求和处理 GZIP 编码的 Axis 客户机了。

清单 5. HTTPSender.java(readFromSocket)中的代码片段

if (null != transferEncoding && 
transferEncoding.trim().equals(HTTPConstants.HEADER_TRANSFER_ENCODING_CHUNKED)) {
  inp = new ChunkedInputStream(inp);
}
        
//Check the content encoding. If it is gzip then
//wrapper the input stream as a GZIPInputStream

   
String zip = (String)headers.get("content-encoding");
InputStream is = null;
if(zip.indexOf("gzip") != -1){
  GZIPInputStream zipIn = new GZIPInputStream(inp);
  is = zipIn;
} else {
        is = inp;
}      
        //end gzip code
outMsg = 
new Message( new SocketInputStream(is, sock), false, contentType, contentLocation);

结束语

正如我们所知,HTTP 上的 GZIP 编码是 Web 技术的一个部分。下一步自然是在现有的 Web 服务框架中使用它。然而,每天都在按这些 SOAP 实现设计、构建和部署各种解决方案。在许多情况下,能够对 SOAP 信封进行 GZIP 编码将能够以相对较少的开销换回更短的事务完成时间。这一性能升级现在可以通过一些简单代码修改来实现。目前,在 SOAP 环境中支持 GZIP 编码将允许您利用压缩,同时您可以耐心地等待把集成加入到我们需要的实现中。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=SOA and web services
ArticleID=49614
ArticleTitle=压缩 SOAP
publish-date=03012003