盡管用XML來傳遞消息存在巨大優(yōu)勢,但是其缺點是性能問題:由于XML的設(shè)計方式,有些數(shù)據(jù)類型不能很好的與XML集成。由于XML是基于文本的形式,最顯著的是二進(jìn)制數(shù)據(jù)(即不能被表示為Unicode字符集的任何東西)。
開發(fā)人員要做什么呢?
使用URL引用
最容易的解決辦法就是在你的XML中不包括這樣的數(shù)據(jù),而是像HTML中使用URL那樣在Web上引用它。例如,如果你的應(yīng)用程序的消息需要包含一個人的JPEG圖片,那么帶有嵌入式鏈接的XML可能如下所示:
<?xml version='1.0' ?> <soap:Envelope xmlns:soap="..."> <soap:Body> <Person name="bob"> <Picture>http://www.example.com/people/bob.jpg</Picture> </Person> </soap:Body> </soap:Envelope> |
如果數(shù)據(jù)是長時間穩(wěn)定且對消息的接收者而言是可用的,這種方式能夠發(fā)揮很好的作用。然而,如果數(shù)據(jù)是短暫的,或者數(shù)據(jù)的接收者沒有連接到Web,這就不是一個好的解決辦法。為了處理這些情況,數(shù)據(jù)必須隨著消息進(jìn)行傳送。
使用編碼
把二進(jìn)制的數(shù)據(jù)放入一條基于XML的消息的最簡單的方法,就是使用類似Base64的方式對其進(jìn)行編碼,把它轉(zhuǎn)變成對XML 安全的一串字符(以及7位的MIME傳輸,XML最初就是針對它設(shè)計的)。使用Base64編碼,我們的圖片XML 可能如下所示:
<?xml version='1.0' ?> <soap:Envelope xmlns:soap="..."> <soap:Body> <Person name="bob"> <Picture>Li4uYmluYXJ5IGpwZWcgaW1hZ2UuLi4=</Picture> </Person> </soap:Body> </soap:Envelope> |
XML Schema定義了一種base64Binary類型,這是一種足夠通用的方法,使您能夠照此識別已編碼的二進(jìn)制內(nèi)容(它也定義一種hexBinary類型,這是一個可選的編碼模式,但還不是很流行)。
這種編碼的不利方面是它的低效率;因為數(shù)據(jù)的二進(jìn)位形式使用有限范圍的字符集來表示豐富的數(shù)據(jù)流,它通常比base64形式更簡潔。通常,對于給定的數(shù)據(jù)流,base64編碼會引入33%的冗余尺寸,從而使XML消息更大。
另外,對二進(jìn)制數(shù)據(jù)進(jìn)行編碼和解碼會造成相當(dāng)大的處理開銷,這反過來會影響使用它的應(yīng)用程序的可擴展性和性能。
使用帶附件的SOAP消息
這些問題促成了帶附件的SOAP消息(SOAP Messages with Attachments (SwA))的開發(fā)。帶附件的SOAP消息是一種特定于Web Services的技術(shù),它使用MIME Multipart/Related數(shù)據(jù)包來隨XML消息發(fā)送二進(jìn)制數(shù)據(jù)和其它附件,從而避免了編碼的開銷。用于我們的圖片的一個簡化的SwA消息可能如下所示:
Content-Type: Multipart/Related; boundary=MIME_boundary; type=text/xml --MIME_boundary Content-Type: text/xml; charset=UTF-8 Content-Transfer-Encoding: 8bit <?xml version='1.0' ?> <soap:Envelope xmlns:soap="..."> <soap:Body> <Person name="bob"> <Picture>cid:bob@pictures.example.com</Picture> </Person> </soap:Body> </soap:Envelope> --MIME_boundary Content-Type: image/jpeg Content-Transfer-Encoding: binary Content-ID: <bob@pictures.example.com> ...binary JPEG image... --MIME_boundary-- |
我們可以看到,圖像數(shù)據(jù)在一個MIME附件中。它是從帶有一個cid(URL)的SOAP消息而被引用的,這個URI使用Content-ID MIME頭的值來找到正確的附件。
這樣避免了編碼的開銷和冗余,但是也帶來了一些新的問題。XML和Web Services的大部分價值在于使用generic XML工具來處理內(nèi)容的能力——像XPat、XQuery、XSLT、XML 加密和數(shù)字簽名以及XML schema一樣。這些工具不處理非XML的內(nèi)容;如果您想要對這些內(nèi)容進(jìn)行查詢、轉(zhuǎn)換、加密、簽名或者描述,您就需要使用一種不同的機制,甚至建立一種新的機制。
此外,由于SwA還存在相當(dāng)多的互操作性問題,以致于WS-I一直致力于研究(在寫作本文時)適合它們的特定的互操作性配置文件。
實際上,帶有附件的SOAP消息引進(jìn)了一種新的消息數(shù)據(jù)模型,因此,它不再是基于XML的消息傳遞了。在2003年的早期,BEA公司和Microsoft公司就開始關(guān)注并撰寫關(guān)于這個問題的白皮書,并且開始探索其他可能的選擇。
MTOM和XOP的引入
在找出與SwA相關(guān)的那些問題之后,我們開始研究制訂一個具體的解決方案。這項工作從Proposed Addendum to SOAP Messages with Attachments(PASWA)開始,并且W3C 的XML協(xié)議組(該組提出了SOAP 1.2)一直將它作為Message Transmission Optimization Mechanism(MTOM)和XML-binary Optimized Packaging(XOP)的規(guī)范加以研究。
上述內(nèi)容背后的思想很簡單。 XOP是XML的可選序列化方法,使您能夠?qū)⑷魏?/SPAN>XML文檔表示為XOP數(shù)據(jù)包。在XOP數(shù)據(jù)包里,任何被命名為base64字符串的事物都作為附件進(jìn)行編碼,其方法與SwA的方法非常相似。不過,數(shù)據(jù)和附件之間的鏈接不同:它不是依靠應(yīng)用程序進(jìn)行處理,而是由該格式自行處理。
例如,當(dāng)我們圖片文檔在作為一個XOP數(shù)據(jù)包而被序列化時,可能如下所示:
Content-Type: Multipart/Related; boundary=MIME_boundary; type=text/xml --MIME_boundary Content-Type: text/xml; charset=UTF-8 Content-Transfer-Encoding: 8bit <?xml version='1.0' ?> <soap:Envelope xmlns:soap="..." xmlns:xbinc="..."> <soap:Body> <Person name="bob"> <Picture><xbinc:Include href="cid:bob@pictures.example.com"/></Picture> </Person> </soap:Body> </soap:Envelope> --MIME_boundary Content-Type: image/jpeg Content-Transfer-Encoding: binary Content-ID: <bob@pictures.example.com> ...binary JPEG image... --MIME_boundary-- |
從XML觀點來看,該文檔與上面的base64版本同構(gòu);也就是說,其中任何一種都可以編碼為另外一種,而不會造成信息的丟失。與SwA不同,XOP使用xbinc:Include元素顯式地將內(nèi)容與正確的附件關(guān)聯(lián)起來,并避免了SwA中存在的許多歧義性。它也保持XML 消息的數(shù)據(jù)模型;因為它只是XML的一種可選編碼,實際上,可以將附件中的二進(jìn)制內(nèi)容視為XML自身中的base64編碼的數(shù)據(jù)。
XOP是一個通用的機制;我們能用它來序列化任何種類的XML。在SOAP中,MTOM使XOP串行化和反串行化成為可能,這是HTTP綁定的擴展。隨著其他綁定被定義出來,它們也將包含XOP支持。
從API角度來看,XOP隱含著一些有趣的內(nèi)容。如果一個XML棧能夠理解XOP編碼,那么您的應(yīng)用程序就根本不需要改變;例如,當(dāng)它需要訪問圖片時,它仍然能夠?qū)⑺@得內(nèi)容的字符值看作base64編碼字符串。如果XOP正在使用中,那么該實現(xiàn)可以即刻自動將其編碼。
這就能夠?qū)?/SPAN>XOP透明地逐步部署到應(yīng)用程序中,但是并不能產(chǎn)生期望的性能收益。為了產(chǎn)生期望的性能收益,應(yīng)用程序需要通過使棧顯式地為它執(zhí)行base64編碼和解碼來訪問二進(jìn)制內(nèi)容的值空間,而不是詞法空間。
實際上,這相當(dāng)容易做到。為了兼容XOP,需要用一種簡單方法來擴展XML API,從而訪問值空間。例如,SAX定義了characters()方法來處理字符數(shù)據(jù),包括我們的圖片元素。通過定義一種新方法——例如binary() 方法,自動地對base64編碼的內(nèi)容進(jìn)行合適的解碼, 或者當(dāng)xbinc:Include 存在時,取消對附件的引用。應(yīng)用程序可以更容易地實現(xiàn)由XOP提供的收益。
當(dāng)我們考慮類型感知API,(像XML beans)時,事情變得更有意思了。因為它提供了訪問XML 內(nèi)容的詞法空間和值空間的方法,所以有可能在類似XOP的類型感知編碼中進(jìn)行無形的分層。
建議
在寫作本文時,W3C仍然在開發(fā)XOP和MTOM,但是它們進(jìn)展迅速,并且擁有來自Web服務(wù)行業(yè)的各個巨頭的充分支持。因此,我們預(yù)計XOP和MTOM將成為主流的Web Services附件機制。
另外,XML API——包括DOM,SAX,StAX和XML beans——將需要進(jìn)行修改,以實現(xiàn)由XOP帶來的好處。由于XOP得到Web Services領(lǐng)域的一致認(rèn)同及其簡潔性,我們期望這種修改會迅速實現(xiàn)。
同時,最好的選擇是使用一個URI引用和編碼;對于某些應(yīng)用程序類型而言,URI引用總是有用的,并且編碼與XOP的透明兼容性意味著,當(dāng)XOP得到更廣泛的采用時,很容易進(jìn)行升級。