提高 Web Service 數據傳輸效率的基本方法

背景

Web Service 現如今已經成爲 SOA 實現標準之一。很多公司已經或者正在參與到 Web Service 項目的實現和部署中。Web Service 的優點在於鬆散的處理異構系統之間的通信和數據交換,可以隨機應變的處理企業各個系統之間的整合問題。但是同時,Web Service 採用 XML 標準進行系統間的數據傳輸,加大了傳輸的數據量,尤其是在傳輸一些具有比較嚴格結構的數據時,會使得傳輸效率有所下降。所以,如何提高 Web Service 傳輸效率成爲很多公司進行項目部署時非常關心的問題。

目的

本文介紹了在 Web Service 實施和開發過程中,提高系統效率的一些方法,實踐證明,這些方法都是非常有效且易於實現的。不同的方法都有其應用領域和優缺點,我們會分別進行討論。文章的主要目的在於,提供給讀者多種方式的基本解決方案,使得讀者在 Web Service 項目部署時,擁有更多的思路。

原因分析

Web Service 是採用 XML 標準進行數據傳輸的。XML 在傳輸過程中,會附帶很多數據的相關信息,並以標籤的形式表現出來。在傳輸過程中,一些情況下,這些標籤會佔用一半以上甚至更多的數據傳輸量,例如,要傳輸一個表格信息,如下(表格中的人名爲虛構的):


表 1. 將要進行傳輸的表格示例
Name City Apartment
Air Wang Beijing Some Place

表 1 中的數據在傳輸過程中,有可能會生成下面的 XML 文件: 
清單 1. 對應於表 1 數據所傳輸的 XML 文件示例

                    
<Heading>
  <column> Name</column>
  <column>City</column>
  <column>Apartment</column>
</Heading>
<Data Grid>
  <row>
    <column>Air Wang</column>
    <column>Beijing</column>
    <column>IBM CDL</column>
  </row>
</Data Grid>
      

如果上面的表格中還帶有格式的信息(比如字體,背景顏色等等)的話,那麼相應的 XML 就會更加複雜了。從上述 XML 中我們可以看出,除了數據之外,XML 會附加很多標籤信息,這就使得傳輸的數據量增大,當所需要傳輸的數據比較多的時候,XML的標籤就會帶來比較大的效率問題。

解決方案一: 壓縮與解壓縮

Web Service 在網絡中傳輸的是以 XML 爲基礎的消息的請求和響應。大量的數據傳輸會使網絡成爲瓶頸。一個最直接的解決方案就是對傳輸的消息進行壓縮。對於不同規模的數據量,壓縮應該有不同的解決方案,下面分別介紹如下:

1. 對於整個 XML 傳輸文件進行壓縮
數據壓縮已經發展了很多年,有很多成熟的技術,算法以及工具包。經常用於對數據壓縮的 API 有 gzip 等方式。對文件進行壓縮的做法非常簡單,就是在發送 XML 之前對 XML 進行壓縮,經過壓縮以後,再在 XML 接收端對已經壓縮的文件進行解壓縮。
優點: 
該方法的優點在於,使用了成熟的壓縮和解壓縮技術,當數據量比較大的時候,可以大大提高傳輸效率。對於純文本的 XML,壓縮可以減少其80%以上的體積。
缺點:
壓縮和解壓縮雖然可以使得 XML 的體積大大減少,但是其過程卻是十分耗費系統資源的。壓縮和解壓縮往往會具有很大的 CPU 佔有率以及內存佔有率。對於配置不高的客戶端甚至是服務器端,都會造成不小的壓力。
應用場景:
該技術應用於網絡瓶頸非常嚴重的情況或是主機配置比較高的情況。
舉例: 
正如本小節最開始已經介紹的,現在已經有很多成熟的壓縮與解壓縮的 API 提供給開發人員進行使用,我們選取其中最常用的 gzip 方式舉例說明。一般來講,系統請求 XML 的體積相對較小,沒有必要使用壓縮和解壓縮的方法處理請求 XML。而對於系統響應 XML 來講,一般都包含大量的數據,導致其體積龐大,需要進行壓縮處理。 對響應 XML 進行壓縮的流程如下:
服務器端數據模型-->序列化操作-->利用 gzip 方式對序列化後的 XML 進行壓縮-->返回到客戶端-->以 gzip 方式進行解壓縮-->對解壓縮後的 XML 進行反序列化操作-->客戶端數據模型
這裏需要說明的一點是,客戶端以及服務器端的數據模型需要實現 Serializable 接口。 
清單 2. gzip 方式壓縮部分實現代碼示例(java 實現)

                    
import java.io.*;
import java.util.zip.*;
public class Compress
{
	public String gzip(OutputStream pStream)
	{
	    …
		try
		{		
			GZIPOutputStream stream = new GZIPOutputStream(pStream);
			return stream.toString();
		}catch(IOException e){…}
		…
		return null;
	}
…
}

在程序將對象模型序列化成 XML 之前,可以使用上面的壓縮方法,對數據流進行壓縮。部分代碼如下: 
清單 3. 對象模型序列化後再進行壓縮的實現代碼示例

                    
public class XMLSerializerHelper
{
…
	public static String saveOBJtoString(Object inputOBJ) throws ConverException
	{
		……
		ByteArrayOutputStream outputStream = null;
		try{
			outputStream = new java.io.ByteArrayOutputStream();
			m_serializer.save((IXMLSerializable) inputObject, outputStream);
			return Compress.gzip(outputStream);
		}catch(Exception e){…}
		……
	}
…
}

解壓縮的過程也類似於上述代碼。測試表明,採用 gzip 壓縮可以減少60%以上的網絡所帶來的消耗。

2. 對於特定的數據進行特殊的處理
在企業日常的數據傳輸中,往往大量的數據具有很多共同的特點。數據和數據之間往往具有很多相同的地方,或者說,具有很多重複的地方。例如,在一個以 Web Service 爲構架的報表處理系統中,報表往往會含有很多的空數據,或者相同屬性和值域的數據,對於這樣的情況,可以在代碼中對特殊情況進行特殊的處理。我們同樣以傳輸一個表格作爲例子,如下:


表 2. 將要傳輸的含有多個空值的表格示例
Software sold Hardware sold System sold Others
120 - - -
- - 90 -
- 110 - -

可以看到,上述表格具有很多的空值,那麼在 XML 中完全可以把空值的部分統一處理,這樣就能大大減少網絡傳輸的數量,其對應的部分 XML 如下: 
清單 4. 對空值進行處理後的簡化 XML 示例

                    
……
<NULL Value>(1,2),(1,3),(1,4),(2,1),(2,2),(2,4)(3,1),(3,3),(3,4)</NULL Value>
……

優點:
對於重複性的數據來說,該方法可以幾十倍甚至上百倍的減少傳輸的數據量(這取決於數據重複的數量大小),相對於第一種壓縮方式,由於只是對固定形式的數據進行處理,所以不會佔用很大的 CPU 以及內存。
缺點:
數據的特點不容易把握,能夠處理的情況比較簡單和單一。對於空值或者某些值重複較多的情況,可以採用本方法。

解決方案二: 減少多次調用,儘量使用一次性的調用方式。

傳統的 RPC 調用,在多次使用的時候會產生很大的效率問題。用戶在進行每次遠程調用的時候都要等待網絡傳輸所耗費的時間。而對於 Web Service 來講,很多用戶仍然將其作爲傳統的 RPC 來進行調用。每次都調用 Web Service Provider 所提供的函數,造成了效率的極大浪費。Web Service 的一大特點在於,可以在本地進行一次性的設置,再把生成的 XML 統一的發送給服務的另外一端。對於用戶來講,所有的設置工作都是在本地進行的,用戶完全感覺不到網絡所帶來的瓶頸,而在最後的數據傳輸過程中,多消耗一些時間也是值得的。
應用場景:
對於同用戶交互的情況,儘可能使用這樣的處理,即減少多次的遠程調用,儘量使得程序僅需完成一次調用。舉一個簡單的例子來說明問題:在一個用戶界面上(User Interface),需要進行很多的設置,而這些設置的每一步都是需要進行遠程調用的,這樣對於用戶來說,就會在每一次的設置過程中都等待網絡傳輸所耗費的時間。而對於 Web Service 而言,客戶端所有的工作就是生成請求 XML,所有的設置工作都可以統一生成一份 XML 文件,然後將其傳送給服務器端,這樣一來,用戶只用等待一次的數據傳輸時間(可能相對時間較長,但是用戶只用等待一次,還是值得的),而其他的工作都在服務器端進行處理。
優點:
所有數據操作在本地進行,用戶在處理數據時不會感到網絡所帶來的停頓。最終所有操作請求統一發送給服務器端,使得原本多次等待的遠程操作只需要一次數據傳輸就能完成。
舉例:
下面的兩幅圖(圖 1 和圖 2)是某數據導入嚮導中的兩個步驟,用戶通過界面設置數據庫以及數據表的信息,從而得到目的數據。


圖 1. 數據導入設置嚮導示例一
數據導入嚮導示例圖 

圖 2. 數據導入設置嚮導示例二
數據導入嚮導示例圖 

很多初學者容易進入的一個誤區是在數據庫選擇以後,在程序中直接連接數據庫,通過用戶提供的用戶密碼建立數據庫連接,然後在後續的每一個步驟中(如圖中的設定數據表條件)都進行遠程函數調用,以至於用戶每一次點擊向導中的“下一步”按鈕時都會感覺到網絡帶來的瓶頸,例如停頓感,或者更嚴重的程序一段時間的沒有響應等等。 事實上,對於 Web Service 來講,每一步都可以對請求 XML 進行設置(這種設置是在本地進行的),當所有步驟都完成以後,再將 XML 統一發送給服務器端進行處理。這樣一來,用戶在每一步之間的操作都是在本地進行的,不會帶來多餘的網絡響應等待時間,使得整個嚮導的設置工作能夠很快的進行。而最後一步完成以後,用戶對於網絡的一次性等待是相對值得的。 在這個例子中,請求XML的部分結構如下: 
清單 5. 客戶端進行數據導入設置的 XML 示例

                    
……
<Data Retrieving>
<Database>
  <Type>Toolbox</Type>
  <Pattern>TCP/IP</Pattern>
  <UserName>Wang Yun</UserName>
  </Password>
</Database>
<Table>
  <Condition method=’more than’ value=’table’ >2500</Condition>
  <Condition method=’equal to’ value=’table’>Wang Yun</Condition>
  <Condition method=’less than’ value=’table’>10 days</Condition>
</Table>
<Data Retrieving>
……

系統客戶端對 XML 進行設置的部分示例代碼如下(將對象模型序列化的過程,僅列出 Database 節點): 
清單 6. 對象模型序列化成 XML 示例

                    
public class DataBaseLoginOBJ
{
  …
  public Element persistToXML(DataObject pOBJ)
  {
    …
    Element dbElement = pOBJ.createElement(“Database”);
    Element typeElement = pOBJ.createElement(“Type”);
    Element patternElement = pOBJ.createElement(“Pattern”);
    Element userElement = pOBJ.createElement(“UserName”);
    Element pwdElement = pOBJ.createElement(“Password”);
    dbElement.appendChild(typeElement);
    dbElement.appendChild(patternElement);
    dbElement.appendChild(userElement);
    dbElement.appendChild(pwdElement);
    …
  }
  …
}

DataObject實現了org.w3c.dom.Document的接口, Element實現了org.w3c.dom.Element接口。在完成了整個請求XML的設置之後,就將其統一傳輸至服務器端進行處理。

解決方案三: XML 解析器的選擇和優化

現在軟件領域有很多種類的 XML 解析器,最基本的方式有兩種:DOM 和 SAX。對於不同級別的XML文件,應該使用不同的解析器。有關 XML 解析器的介紹,請參考其他相關文檔。下面列出他們使用的場景以及優缺點:


表 3. SAX 與 DOM 解析方式的基本比較
種類 優點 缺點 使用場景
DOM 1.XML樹在內存中完整存儲,因此可以直接修改其數據和結構。 2.可以通過該解析器隨時訪問XML樹中的任何一個節點。 3.DOM解析器的API在使用上也相對比較簡單。 如果XML文檔體積比較大時,將文檔讀入內存是非常消耗系統資源的。 DOM 是用與平臺和語言無關的方式表示 XML 文檔的官方 W3C 標準。DOM 是以層次結構組織的節點的集合。這個層次結構允許開發人員在樹中尋找特定信息。分析該結構通常需要加載整個文檔和構造層次結構,然後才能進行任何工作。DOM是基於對象層次結構的。
SAX SAX 對內存的要求比較低,因爲它讓開發人員自己來決定所要處理的標籤。特別是當開發人員只需要處理文檔中所包含的部分數據時,SAX 這種擴展能力得到了更好的體現。 用SAX方式進行XML解析時,需要順序執行,所以很難訪問到同一文檔中的不同數據。此外,在基於該方式的解析編碼過程也相對複雜。 對於含有數據量十分巨大,而又不用對文檔的所有數據進行遍歷或者分析的時候,使用該方法十分有效。該方法不用將整個文檔讀入內存,而只需讀取到程序所需的文檔標籤處即可。

正確的選擇XML解析器的種類,對於Web Service系統的效率會有很大的幫助。現在有很多廠家提供了基於這兩種類型的很多XML解析器,在選擇的時候,應該仔細閱讀說明文檔,慎重進行選擇。另外,往往在一個系統中可以同時使用這兩種方式,以達到解析的最高效率。一般來講,對於請求XML可以採用DOM的解析方式進行解析,而對於響應XML可以使用SAX的方式進行解析。

解決方案四: 簡化標籤

我們知道,Web Service 解決方案在網絡中傳輸 XML 具有比較複雜的結構。在傳輸過程中,不僅僅必要的數據被髮送和接收,同時也會由於 XML 的結構過於冗雜而附加了更多的信息進行傳輸。舉例如下:
系統客戶端從服務器端得到的返回數據的響應 XML 結構如下: 
清單 7. 在網絡中傳輸的表格數據對應的 XML

                    
<Cells>
  <Heading Cells>
    <Column>
      <Row> Data </Row>
    </Column>
    <Column>
      <Row/>
    </Column>
…
  </Heading Cells>
  <DataGrid Cells>
…
  </DataGrid Cells>
</Cells>

由上面的 XML 我們可以看出,每一個返回數值,都至少有 <Column>, </Column>, <Row>, </Row> 來標識它,也就是說,如果我們要傳輸一個表格,那麼表格中每一個數據都要伴隨傳輸相應的這四個標籤。假設表格中的每一個數據的字節數爲8,那麼標籤所帶來的附加字節就將近爲數據的4倍。如果要傳輸的數據量十分巨大的話,效率自然會降低許多。 所以我們可以採用一種十分簡單的方式,有效的減少XML所帶來的附加字節數,即簡化XML標籤。可以將上面所說的XML改寫成下面的格式: 
清單 8. 對應於清單 7 的簡化後的 XML

                    
<Cells>
  <Heading Cells>
   <C>
      <R> Data </R>
   </C>
   <C>
      <R/>
   </C>
…
  </Heading Cells>
  <DataGrid Cells>
…
  </DataGrid Cells>
</Cells>

優點:
測試證明,採用這樣的改進方式,可以使得整個系統的效率提高近一倍甚至更多。而且這樣的處理方式簡單易行,只是修改標籤就可以了。
缺點:
使XML自身代表的意義變得不那麼容易讀懂,彌補的方式是一般會有一個對照表,來說明具體簡化後的 XML 文件的含義。
舉例:
該方法比較簡單直觀,這裏列出對照表的一個例子如下:


表 4. 簡化前後 XML 的對照表及其含義
簡化前標籤 簡化後標籤 含義
Row R 表示表格中的行
Column C 表示表格中的列
... ... ...

類似上面這樣的聲明表格應該作爲文檔提供給用戶。

應用場景:
這裏需要說明一點,XML 能夠清晰的表示出整個數據結構,而很多軟件開發人員往往希望利用 XML 來觀察數據的情況,而標籤就是協助他們更好的完成這樣的工作。所以,一般來講,標籤需要儘可能的有意義,由於 C 和 R 可以比較明確的表示 Column 和 Row,並且他們在數據傳輸過程中是重複的最多的(佔傳輸標籤總數的80%),所以我們進行了上述的改動。但是對於像 Cells 這樣的標籤,由於其重複率不是很高,而且我們需要這個標籤具有字面上的意義,所以,類似於這樣的情況,我們給予保留。

解決方案五: 緩存機制

前面介紹的四種提高效率的方法都是基於 XML 傳輸的,也就是說,都是在如何提高 XML 傳輸效率上進行介紹的。而第五種機制對於任何的軟件解決方案都適用,而對於 Web Service 解決方案來講,更是具有錦上添花的作用。緩存機制最通用的一個思想是,將使用次數比較多的數據緩存起來,如果再有相同請求時,就將緩存中的數據返回。這樣會減少數據邏輯處理所帶來的時間。我們以用戶的一次刷新數據的操作爲例,介紹一種比較通用的緩存方式,如圖:


圖 3. 某數據刷新操作的緩存流程
某數據刷新操作的緩存流程 

由圖中可知,當用戶進行刷新操作時,服務器端先檢查請求 XML,取出要刷新數據源的 CUID。根據該 CUID,系統檢查在緩存中是否存在該 CUID 對應的數據對象,如果有,則取出其刷新狀態,並與對應該 CUID 的數據源的刷新狀態進行比較。如果比較一致,則直接返回數據給客戶端,如果比較不一致,則需要打開數據源進行重新刷新,並將刷新以後的結果集的對象保存在緩存中。 如果緩存已經滿了,則利用最近最少使用原則,清除緩存中的一部分數據對象。另外,緩存的大小設置是可以通過屬性文件進行設置的,在程序中,對緩存進行初始化時,首先讀取了屬性文件來進行緩存大小的設定。緩存數據的好處在於,在對數據進行多次重複刷新時,系統不需要重新打開數據源(有時候連接並打開數據源是非常消耗時間的),從而提高了系統的效率。
優點:
對於多次的相同方式的數據操作,數據直接從緩存返回,可以很大程度的提高系統效率。
缺點:
編程的工作量比較大,需要測試的部分比較多。
應用場景:
一般的 Web Service 部署程序都可以採用緩存機制的處理。

其他解決方案

對於 Web Service 服務器,各個廠商都提供了很多服務器設置,以提高 Web Service 的性能和效率。具體請參考相關服務器的文檔。

小結

對於提高 Web Service 的效率,本文給出了一些解決方案,從各個不同方面對 Web Service 效率的提高進行了討論。對於不同的應用系統,應該酌情分析系統建立的環境以及系統的使用人羣和範圍,以最終決定採用何種解決方案。


參考資料


發佈了6 篇原創文章 · 獲贊 3 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章