分享
 
 
 

通过压缩SOAP改善XML Web service性能

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

压缩文本是一个可以减少文本内容尺寸达80%的过程。这意味着存储压缩的文本将会比存储没有压缩的文本少80%的空间。也意味着在网络上传输内容需要更少的时间,对于使用文本通信的客户端服务器应用程序来说,将会表现出更高的效率,例如XML Web services。

本文的主要目的就是寻找在客户端和服务器之间使交换的数据尺寸最小化的方法。一些有经验的开发者会使用高级的技术来优化通过网络特别是互联网传送的数据,这样的做法在许多分布式系统中都存在瓶颈。解决这个问题的一个方法是获取更多的带宽,但这是不现实的。另一个方法是通过压缩的方法使得被传输的数据达到最小。

当内容是文本的时候,通过压缩,它的尺寸可以减少80%。这就意味着在客户端和服务器之间带宽的需求也可以减少类似的百分比。为了压缩和解压缩,服务端和客户端则占用了CPU的额外资源。但升级服务器的CPU一般都会比增加带宽便宜,所以压缩是提高传输效率的最有效的方法。

XML/SOAP在网络中

让我们仔细看看SOAP在请求或响应XML Web service的时候,是什么在网络上传输。我们创建一个XML Web service,它包含一个 add 方法。这个方法有两个输入参数并返回这两个数的和:

<WebMethod()> Public Function add(ByVal a As Integer, ByVal b As _Integer) As Integer

add = a + b

End Function

当 XML Web service 消费端调用这个方法的时候,它确实发送了一个SOAP请求到服务器:

<?xml version="1.0" encoding="utf-8"?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<soap:Body><add xmlns="1020http://tempuri.org/"><a>10</a><b>20</b></add>

</soap:Body></soap:Envelope>

服务端使用一个SOAP响应来回应这个SOAP请求:

<?xml version="1.0" encoding="utf-8"?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<soap:Body><addResponse xmlns="http://tempuri.org/">

<addResult>30</addResult></addResponse>

</soap:Body></soap:Envelope>

这是调用XML Web service的方法后,在网络上传输的实际信息。在更复杂的XML Web service中,SOAP响应可能是一个很大的数据集。例如,当Northwind中的表orders中的内容被序列化为XML后,数据可能达到454KB。如果我们创建一个应用通过XML Web service来获取这个数据集,那么SOAP响应将会包含所有的数据。

为了提高效率,在传输之前,我们可以压缩这些文本内容。我们怎样才能做到呢?当然是使用SOAP扩展!

SOAP 扩展

SOAP扩展是ASP.NET的Web方法调用的一个拦截机制,它能够在SOAP请求或响应被传输之前操纵它们。开发者可以写一段代码在这些消息序列化之前和之后执行。(SOAP扩展提供底层的API来实现各种各样的应用。)

使用SOAP扩展,当客户端从XML Web service调用一个方法的时候,我们能够减小SOAP信息在网络上传输的尺寸。许多时候,SOAP请求要比SOAP响应小很多(例如,一个大的数据集),因此在我们的例子中,仅对SOAP响应进行压缩。就像你在图1中所看到的,在服务端,当SOAP响应被序列化后,它会被压缩,然后传输到网络上。在客户端,SOAP信息反序列化之前,为了使反序列化成功,SOAP信息会被解压缩。

图 1. SOAP信息在序列化后被压缩(服务端),在反序列化前被解压缩(客户端)

我们也可以压缩SOAP请求,但在这个例子中,这样做的效率增加是不明显的。

为了压缩我们的Web service的SOAP响应,我们需要做两件事情:

· 在服务端序列化SOAP响应信息后压缩它。

· 在客户端反序列化SOAP信息前解压缩它。

这个工作将由SOAP扩展来完成。在下面的段落中,你可以看到所有的客户端和服务端的代码。

首先,这里是一个返回大数据集的XML Web service:

Imports System.Web.Services

<WebService(Namespace := "http://tempuri.org/")> _

Public Class Service1

Inherits System.Web.Services.WebService

<WebMethod()> Public Function getorders() As DataSet

Dim OleDbConnection1 = New System.Data.OleDb.OleDbConnection()

OleDbConnection1.ConnectionString = "Provider=SQLOLEDB.1; _

Integrated Security=SSPI;Initial Catalog=Northwind; _

Data Source=.;Workstation ID=T-MNIKIT;"

Dim OleDbCommand1 = New System.Data.OleDb.OleDbCommand()

OleDbCommand1.Connection = OleDbConnection1

OleDbConnection1.Open()

Dim OleDbDataAdapter1 = New System.Data.OleDb.OleDbDataAdapter()

OleDbDataAdapter1.SelectCommand = OleDbCommand1

OleDbCommand1.CommandText = "Select * from orders"

Dim objsampleset As New DataSet()

OleDbDataAdapter1.Fill(objsampleset, "Orders")

OleDbConnection1.Close()

Return objsampleset

End Function

End Class

在客户端,我们构建了一个Windows应用程序来调用上面的XML Web service,获取那个数据集并显示在DataGrid中:

Public Class Form1

Inherits System.Windows.Forms.Form

'This function invokes the XML Web service, which returns the dataset

'without using compression.

Private Sub Button1_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles Button1.Click

Dim ws As New wstest.Service1()

Dim test1 As New ClsTimer()

'Start time counting…

test1.StartTiming()

'Fill datagrid with the dataset

DataGrid1.DataSource = ws.getorders()

test1.StopTiming()

'Stop time counting…

TextBox5.Text = "Total time: " & test1.TotalTime.ToString & "msec"

End Sub

'This function invokes the XML Web service, which returns the dataset

'using compression.

Private Sub Button2_Click(ByVal sender As System.Object, _

ByVal e As System.EventArgs) Handles Button2.Click

Dim ws As New wstest2.Service1()

Dim test1 As New ClsTimer()

'Start time counting…

test1.StartTiming()

'Fill datagrid with dataset

DataGrid1.DataSource = ws.getorders()

test1.StopTiming()

'Stop time counting…

TextBox4.Text = "Total time: " & test1.TotalTime.ToString & "msec"

End Sub

End Class

客户端调用了两个不同的XML Web services, 仅其中的一个使用了SOAP压缩。下面的Timer类是用来计算调用时间的:

Public Class ClsTimer

' Simple high resolution timer class

'

' Methods:

' StartTiming reset timer and start timing

' StopTiming stop timer

'

'Properties

' TotalTime Time in milliseconds

'Windows API function declarations

Private Declare Function timeGetTime Lib "winmm" () As Long

'Local variable declarations

Private lngStartTime As Integer

Private lngTotalTime As Integer

Private lngCurTime As Integer

Public ReadOnly Property TotalTime() As String

Get

TotalTime = lngTotalTime

End Get

End Property

Public Sub StartTiming()

lngTotalTime = 0

lngStartTime = timeGetTime()

End Sub

Public Sub StopTiming()

lngCurTime = timeGetTime()

lngTotalTime = (lngCurTime - lngStartTime)

End Sub

End Class

服务端的SOAP扩展

在服务端,为了减小SOAP响应的尺寸,它被压缩。下面这段告诉你怎么做:

第一步

使用Microsoft Visual Studio .NET, 我们创建一个新的Visual Basic .NET 类库项目(使用"ServerSoapExtension"作为项目名称),添加下面的类:

Imports System

Imports System.Web.Services

Imports System.Web.Services.Protocols

Imports System.IO

Imports zipper

Public Class myextension

Inherits SoapExtension

Private networkStream As Stream

Private newStream As Stream

Public Overloads Overrides Function GetInitializer(ByVal _

methodInfo As LogicalMethodInfo, _

ByVal attribute As SoapExtensionAttribute) As Object

Return System.DBNull.Value

End Function

Public Overloads Overrides Function GetInitializer(ByVal _

WebServiceType As Type) As Object

Return System.DBNull.Value

End Function

Public Overrides Sub Initialize(ByVal initializer As Object)

End Sub

Public Overrides Sub ProcessMessage(ByVal message As SoapMessage)

Select Case message.Stage

Case SoapMessageStage.BeforeSerialize

Case SoapMessageStage.AfterSerialize

AfterSerialize(message)

Case SoapMessageStage.BeforeDeserialize

BeforeDeserialize(message)

Case SoapMessageStage.AfterDeserialize

Case Else

Throw New Exception("invalid stage")

End Select

End Sub

' Save the stream representing the SOAP request or SOAP response into a

' local memory buffer.

Public Overrides Function ChainStream(ByVal stream As Stream) As Stream

networkStream = stream

newStream = New MemoryStream()

Return newStream

End Function

' Write the compressed SOAP message out to a file at

'the server's file system..

Public Sub AfterSerialize(ByVal message As SoapMessage)

newStream.Position = 0

Dim fs As New FileStream("c:\temp\server_soap.txt", _

FileMode.Append, FileAccess.Write)

Dim w As New StreamWriter(fs)

w.WriteLine("-----Response at " + DateTime.Now.ToString())

w.Flush()

'Compress stream and save it to a file

Comp(newStream, fs)

w.Close()

newStream.Position = 0

'Compress stream and send it to the wire

Comp(newStream, networkStream)

End Sub

' Write the SOAP request message out to a file at the server's file system.

Public Sub BeforeDeserialize(ByVal message As SoapMessage)

Copy(networkStream, newStream)

Dim fs As New FileStream("c:\temp\server_soap.txt", _

FileMode.Create, FileAccess.Write)

Dim w As New StreamWriter(fs)

w.WriteLine("----- Request at " + DateTime.Now.ToString())

w.Flush()

newStream.Position = 0

Copy(newStream, fs)

w.Close()

newStream.Position = 0

End Sub

Sub Copy(ByVal fromStream As Stream, ByVal toStream As Stream)

Dim reader As New StreamReader(fromStream)

Dim writer As New StreamWriter(toStream)

writer.WriteLine(reader.ReadToEnd())

writer.Flush()

End Sub

Sub Comp(ByVal fromStream As Stream, ByVal toStream As Stream)

Dim reader As New StreamReader(fromStream)

Dim writer As New StreamWriter(toStream)

Dim test1 As String

Dim test2 As String

test1 = reader.ReadToEnd

'String compression using NZIPLIB

test2 = zipper.Class1.Compress(test1)

writer.WriteLine(test2)

writer.Flush()

End Sub

End Class

' Create a SoapExtensionAttribute for the SOAP extension that can be

' applied to an XML Web service method.

<AttributeUsage(AttributeTargets.Method)> _

Public Class myextensionattribute

Inherits SoapExtensionAttribute

Public Overrides ReadOnly Property ExtensionType() As Type

Get

Return GetType(myextension)

End Get

End Property

Public Overrides Property Priority() As Integer

Get

Return 1

End Get

Set(ByVal Value As Integer)

End Set

End Property

End Class

第二步

我们增加ServerSoapExtension.dll程序集作为引用,并且在web.config声明SOAP扩展:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<system.web>

<webServices>

<soapExtensionTypes>

<add type="ServerSoapExtension.myextension,

ServerSoapExtension" priority="1" group="0"/>

</soapExtensionTypes>

</webServices>

...

</system.web>

</configuration>

就象你在代码中看到的那样,我们使用了一个临时目录("c:\temp")来捕获SOAP请求和压缩过的SOAP响应到文本文件("c:\temp\server_soap.txt")中 。

客户端的SOAP扩展

在客户端,从服务器来的SOAP响应被解压缩,这样就可以获取原始的响应内容。下面就一步一步告诉你怎么做:

第一步

使用Visual Studio .NET, 我们创建一个新的Visual Basic .NET类库项目(使用 "ClientSoapExtension"作为项目名称),并且添加下面的类:

Imports System

Imports System.Web.Services

Imports System.Web.Services.Protocols

Imports System.IO

Imports zipper

Public Class myextension

Inherits SoapExtension

Private networkStream As Stream

Private newStream As Stream

Public Overloads Overrides Function GetInitializer(ByVal _

methodInfo As LogicalMethodInfo, _

ByVal attribute As SoapExtensionAttribute) As Object

Return System.DBNull.Value

End Function

Public Overloads Overrides Function GetInitializer(ByVal _

WebServiceType As Type) As Object

Return System.DBNull.Value

End Function

Public Overrides Sub Initialize(ByVal initializer As Object)

End Sub

Public Overrides Sub ProcessMessage(ByVal message As SoapMessage)

Select Case message.Stage

Case SoapMessageStage.BeforeSerialize

Case SoapMessageStage.AfterSerialize

AfterSerialize(message)

Case SoapMessageStage.BeforeDeserialize

BeforeDeserialize(message)

Case SoapMessageStage.AfterDeserialize

Case Else

Throw New Exception("invalid stage")

End Select

End Sub

' Save the stream representing the SOAP request or SOAP response

' into a local memory buffer.

Public Overrides Function ChainStream(ByVal stream As Stream) _

As Stream

networkStream = stream

newStream = New MemoryStream()

Return newStream

End Function

' Write the SOAP request message out to a file at

' the client's file system.

Public Sub AfterSerialize(ByVal message As SoapMessage)

newStream.Position = 0

Dim fs As New FileStream("c:\temp\client_soap.txt", _

FileMode.Create, FileAccess.Write)

Dim w As New StreamWriter(fs)

w.WriteLine("----- Request at " + DateTime.Now.ToString())

w.Flush()

Copy(newStream, fs)

w.Close()

newStream.Position = 0

Copy(newStream, networkStream)

End Sub

' Write the uncompressed SOAP message out to a file

' at the client's file system..

Public Sub BeforeDeserialize(ByVal message As SoapMessage)

'Decompress the stream from the wire

DeComp(networkStream, newStream)

Dim fs As New FileStream("c:\temp\client_soap.txt", _

FileMode.Append, FileAccess.Write)

Dim w As New StreamWriter(fs)

w.WriteLine("-----Response at " + DateTime.Now.ToString())

w.Flush()

newStream.Position = 0

'Store the uncompressed stream to a file

Copy(newStream, fs)

w.Close()

newStream.Position = 0

End Sub

Sub Copy(ByVal fromStream As Stream, ByVal toStream As Stream)

Dim reader As New StreamReader(fromStream)

Dim writer As New StreamWriter(toStream)

writer.WriteLine(reader.ReadToEnd())

writer.Flush()

End Sub

Sub DeComp(ByVal fromStream As Stream, ByVal toStream As Stream)

Dim reader As New StreamReader(fromStream)

Dim writer As New StreamWriter(toStream)

Dim test1 As String

Dim test2 As String

test1 = reader.ReadToEnd

'String decompression using NZIPLIB

test2 = zipper.Class1.DeCompress(test1)

writer.WriteLine(test2)

writer.Flush()

End Sub

End Class

' Create a SoapExtensionAttribute for the SOAP extension that can be

' applied to an XML Web service method.

<AttributeUsage(AttributeTargets.Method)> _

Public Class myextensionattribute

Inherits SoapExtensionAttribute

Public Overrides ReadOnly Property ExtensionType() As Type

Get

Return GetType(myextension)

End Get

End Property

Public Overrides Property Priority() As Integer

Get

Return 1

End Get

Set(ByVal Value As Integer)

End Set

End Property

End Class

就象你在代码中看到的那样,我们使用了一个临时目录("c:\temp") 来捕获SOAP请求和解压缩的SOAP响应到文本文件("c:\temp\client_soap.txt")中。

第二步

我们添加ClientSoapExtension.dll程序集作为引用,并且在我们的应用程序的XML Web service引用中声明SOAP扩展:

'-------------------------------------------------------------------------

' <autogenerated>

' This code was generated by a tool.

' Runtime Version: 1.0.3705.209

'

' Changes to this file may cause incorrect behavior and will be lost if

' the code is regenerated.

' </autogenerated>

'-------------------------------------------------------------------------

Option Strict Off

Option Explicit On

Imports System

Imports System.ComponentModel

Imports System.Diagnostics

Imports System.Web.Services

Imports System.Web.Services.Protocols

Imports System.Xml.Serialization

'

'This source code was auto-generated by Microsoft.VSDesigner,

'Version 1.0.3705.209.

'

Namespace wstest2

'<remarks/>

<System.Diagnostics.DebuggerStepThroughAttribute(), _

System.ComponentModel.DesignerCategoryAttribute("code"), _

System.Web.Services.WebServiceBindingAttribute(Name:="Service1Soap", _

[Namespace]:="http://tempuri.org/")> _

Public Class Service1

Inherits System.Web.Services.Protocols.SoapHttpClientProtocol

'<remarks/>

Public Sub New()

MyBase.New

Me.Url = "http://localhost/CompressionWS/Service1.asmx"

End Sub

'<remarks/>

<System.Web.Services.Protocols.SoapDocumentMethodAttribute _

("http://tempuri.org/getproducts",RequestNamespace:= _

"http://tempuri.org/", ResponseNamespace:="http://tempuri.org/", _

Use:=System.Web.Services.Description.SoapBindingUse.Literal,_

ParameterStyle:=System.Web.Services.Protocols.SoapParameterStyle _

.Wrapped), ClientSoapExtension.myextensionattribute()> _

Public Function getproducts() As System.Data.DataSet

Dim results() As Object = Me.Invoke("getproducts", _

New Object(-1) {})

Return CType(results(0), System.Data.DataSet)

End Function

'<remarks/>

Public Function Begingetproducts(ByVal callback As _

System.AsyncCallback, ByVal asyncState As Object) As System.IAsyncResult _

Return Me.BeginInvoke("getproducts", New Object(-1) {}, _

callback, asyncState)

End Function

'<remarks/>

Public Function Endgetproducts(ByVal asyncResult As _

System.IAsyncResult) As System.Data.DataSet

Dim results() As Object = Me.EndInvoke(asyncResult)

Return CType(results(0), System.Data.DataSet)

End Function

End Class

End Namespace

这里是zipper类的源代码,它是使用免费软件NZIPLIB库实现的:

using System;

using NZlib.GZip;

using NZlib.Compression;

using NZlib.Streams;

using System.IO;

using System.Net;

using System.Runtime.Serialization;

using System.Xml;

namespace zipper

{

public class Class1

{

public static string Compress(string uncompressedString)

{

byte[] bytData = System.Text.Encoding.UTF8.GetBytes(uncompressedString);

MemoryStream ms = new MemoryStream();

Stream s = new DeflaterOutputStream(ms);

s.Write(bytData, 0, bytData.Length);

s.Close();

byte[] compressedData = (byte[])ms.ToArray();

return System.Convert.ToBase64String(compressedData, 0, _

compressedData.Length);

}

public static string DeCompress(string compressedString)

{

string uncompressedString="";

int totalLength = 0;

byte[] bytInput = System.Convert.FromBase64String(compressedString);;

byte[] writeData = new byte[4096];

Stream s2 = new InflaterInputStream(new MemoryStream(bytInput));

while (true)

{

int size = s2.Read(writeData, 0, writeData.Length);

if (size > 0)

{

totalLength += size;

uncompressedString+=System.Text.Encoding.UTF8.GetString(writeData, _

0, size);

}

else

{

break;

}

}

s2.Close();

return uncompressedString;

}

}

}

分析

软件&硬件

· 客户方: Intel Pentium III 500 MHz, 512 MB RAM, Windows XP.

· 服务器方: Intel Pentium III 500 MHz, 512 MB RAM, Windows 2000 Server, Microsoft SQL Server 2000.

在客户端,一个Windows应用程序调用一个XML Web service。这个XML Web service 返回一个数据集并且填充客户端应用程序中的DataGrid。

图2. 这是一个我们通过使用和不使用SOAP压缩调用相同的XML Web Service的样例程序。这个XML Web service返回一个大的数据集。

CPU 使用记录

就象在图3中显示的那样,没有使用压缩的CPU使用时间是 29903 milliseconds.

图 3. 没有使用压缩的CPU使用记录

在我们的例子中,使用压缩的CPU使用时间显示在图4中, 是15182 milliseconds.

图 4. 使用压缩的CPU 使用记录

正如你看到的,我们在客户方获取这个数据集的时候,使用压缩与不使用压缩少用了近50%的CPU时间,仅仅在CPU加载时有一点影响。当客户端和服务端交换大的数据时,SOAP压缩能显著地增加XML Web Services效率。 在Web中,有许多改善效率的解决方案。我们的代码是获取最大成果但是最便宜的解决方案。SOAP扩展是通过压缩交换数据来改善XML Web Services性能的,这仅仅对CPU加载时间造成了一点点影响。并且值得提醒的是,大家可以使用更强大的和需要更小资源的压缩算法来获取更大的效果

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有