SOAP-簡單對象訪問協議(Simple Object Access Protocol)。SOAP是一種輕量的、簡單的、基於 XML 的協議,它被設計成在 WEB 上交換結構化的和固化的信息。 SOAP 可以和現存的許多因特網協議和格式結合使用,包括超文本傳輸協議(HTTP),簡單郵件傳輸協議(SMTP),多用途網際郵件擴充協議(MIME)。它還支持從消息系統到遠程過程調用(RPC)等大量的應用程序。
SOAP爲在一個鬆散的、分佈的環境中使用XML對等地交換結構化的和類型化的信息提供了一個簡單的輕量級機制。SOAP本身並不定義任何應用語義,如編程模型或特定語義實現,它只是定義了一種簡單的機制,通過一個模塊化的包裝模型和對模塊中特定格式編碼的數據重編碼機制來表示應用語義。SOAP的這項能力使得它可被很多類型的系統用於從消息系統到RPC(Remote Procedure Call)的延伸。
目前W3C已經發布SOAP1.2版本標準,但是在目前廣泛應用的還是SOAP1.1版本,詳細規
SOAP V1.1:http://www.w3.org/TR/2000/NOTE-SOAP-20000508/
SOAP V1.2:http://www.w3.org/TR/2001/WD-soap12-20010709/
SOAP規範主要由四個部分組成
- SOAP信封(envelop),它定義了一個SOAP消息表示框架,該框架描述了消息中的內容是什麼,誰發送的,誰應當處理並處理它,以及這些操作是可選的還是必須的等。
- SOAP編碼規則(encoding rules),定義了一個數據的編碼機制,通過這樣一個編碼機制來定義應用程序中需要使用的數據類型,並可用於交換由這些應用程序定義的數據類型所衍生的實例。例如可能應訂單服務的需要,使用SOAP編碼規則定義了訂單的數據類型,並可以在訂單生成的客戶端與訂單服務之間交換訂單實例。
- SOAP RPC 表示(RPC representation),定義了一個用於表示遠端過程調用和響應的約定,例如如何使用HTTP或SMTP協議與SOAP綁定,如何傳輸過程調用,在具體傳輸協議的哪個部分傳輸過程響應,如我們可以在HTTP的響應的時候傳遞過程響應。
- SOAP綁定(binding),定義了一個使用底層傳輸協議來完成在結點間交換SOAP信封的約定。
爲了簡化本規範,這四部分在功能上是正交的。特別的,信封和編碼規則是被定義在不同的命名空間(namespace)中,這樣有利於通過模塊化獲得簡明性。
規範中還定義了兩種SOAP綁定(binding),用於描述SOAP消息(message)如何通過帶或不帶HTTP擴展框架[6](HTTP Extension Framework)的HTTP[5]消息(message)進行傳輸。
SOAP 消息基本上是從發送端到接收端的單向傳輸,但它們常常結合起來執行類似於請求/應答的模式。所有的SOAP消息都使用XML編碼。一條SOAP消息就是一個包含有一個必需的SOAP的封裝包,一個可選的SOAP標頭和一個必需的SOAP體塊的XML文檔。
SOAP消息組成部分如下圖所示:
圖1.1 SOAP消息組成部分
SOAP 請求的 XML 部分包含三個主要部分:
- Envelope 定義各個 SOAP 消息的餘下部分會使用的 namespaces ,典型的有 xmlns:SOAP-ENV ( SOAP Envelope namespace )、 xmlns:xsi ( XML Schema for Instances ) 和 xmlns:xsd ( XML Schema for DataTypes )。
- Header 是可選的元素,它攜帶認證、事務處理和支付的輔助信息。一個 SOAP 處理鏈中的任一元素可增加或刪除 Header 裏的項;元素也可選擇忽略它們不認識的項。如果 Header 被使用,它必須是 Envelope 的第一個子元素。
- Body 是消息的主要有效載體。當 SOAP 被用於執行一個 RPC 調用時, Body 包含一個單獨元素,這個元素包含方法名、參數和 Web 服務的目標地址。元素的 namespace 等於目標地址,根名是方法名。
-
- 在HTTP中使用SOAP
HTTP-超文本傳輸協議(Hypertext Transfer Protocol)用來時機傳輸WWW(World Wide Web)上的所有通信。HTTP是一個客戶機-服務器模型:客戶機向服務器提交一個請求,然後服務器由發送回一個應答。就現在而言,由於HTTP比較簡單、穩定以及被廣泛應用,而且大部分防火牆對HTTP協議的80端口是開放的,所以HTTP是服務運輸層的最流行的協議。
把SOAP綁定到HTTP提供了同時利用SOAP的樣式和分散的靈活性的特點以及HTTP的豐富的特徵庫的優點。在HTTP上傳送SOAP並不是說SOAP會覆蓋現有的HTTP 語義,而是HTTP上的SOAP語義會自然的映射到HTTP語義。在使用HTTP作爲協議綁定的場合中, RPC請求映射到HTTP請求上,而RPC應答映射到HTTP應答。然而在 RPC 上使用SOAP並不僅限於HTTP協議綁定。
HTTP 請求和響應消息的 Content-Type 標頭都必須設爲 text/xml (在 SOAP 1.2 中是 application/soap+xml)。 對於請求消息,它必須使用 POST 作爲動詞,而 URI 應該識別 SOAP 處理器。 SOAP 規範還定義了一個名爲 SOAPAction 的新 HTTP 標頭,所有 SOAP HTTP 請求(即使是空的)都必須包含該標頭。 SOAPAction 標頭旨在表明該消息的意圖。 對於 HTTP 響應,如果沒有發生任何錯誤,它應該使用 200 狀態碼,如果包含 SOAP 錯誤,則應使用 500。
下面爲SOAP綁定HTTP的一個示例,在本例中,一個GetLastTradePrice SOAP請求被髮送給一個StackQuote服務,該請求接受一個字符串參數即蜂鳴器符號symbol,並在SOAP應答中返回一個浮點數即價格Price。SOAP Envelop元素是表示SOAP消息的XML文檔的頂級元素。XML名稱空間用來消除應用相關標識符與SOAP標識符的歧義。
示例1 嵌套在HTTP請求中的SOAP消息
POST /StockQuote HTTP/1.1
Host: www.stockquoteserver.com
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
SOAPAction: "Some-URI"
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<m:GetLastTradePrice xmlns:m="Some-URI">
<symbol>DIS</symbol>
</m:GetLastTradePrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
示例2 嵌套在HTTP應答中的SOAP消息
HTTP/1.1 200 OK
Content-Type: text/xml; charset="utf-8"
Content-Length: nnnn
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
<SOAP-ENV:Body>
<m:GetLastTradePriceResponse xmlns:m="Some-URI">
<Price>34.5</Price>
</m:GetLastTradePriceResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
從表面上看,Web service 就是一個應用程序,它向外界暴露出一個能夠通過Web進行調用的API。這就是說,你能夠用編程的方法通過Web來調用這個應用程序。我們把調用這個Web service 的應用程序叫做客戶。
從深層次上看,Web Service是一種新的Web應用程序分支,它們是自包含、自描述、模塊化的應用,可以在網絡(通常爲Web)中被描述、發佈、查找以及通過Web來調用。
Web Service是基於網絡的、分佈式的模塊化組件,它執行特定的任務,遵守具體的技術規範,這些規範使得Web Service能與其他兼容的組件進行互操作。它可以使用標準的互聯網協議,像超文本傳輸協議HTTP和XML,將功能體現在互聯網和企業內部網上。Web Service平臺是一套標準,它定義了應用程序如何在Web上實現互操作性。你可以用你喜歡的任何語言,在你喜歡的任何平臺上寫Web Service。
Web service 更精確的解釋: Web services是建立可互操作的分佈式應用程序的新平臺。Web service平臺是一套標準,它定義了應用程序如何在Web上實現互操作性。你可以用任何你喜歡的語言,在任何你喜歡的平臺上寫Web service ,只要我們可以通過Web service標準對這些服務進行查詢和訪問。
Web Service 三個基本技術
- Web Service通過標準通信協議,在互聯網上發佈有用的程序模塊(以服務的方式),目前大部分是用SOAP來作通信協議。
- Web Service提供一份詳細的接口說明書,來幫助用戶構建應用程序,這個接口說明書叫作WSDL(Web Service Description Language)。
- 通常已發佈的Web Service要註冊到管理服務器,這樣便於使用者查詢和使用。這個是通過UDDI(Universal Discovery Description and Integration)來完成的。
SOAP是一種基於XML的不依賴傳輸協議的表示層協議(參見ISO 7層參考模型,TCP/IP大致相當於傳輸層和網絡層,HTTP相當於會話層),用來在應用程序之間方便地以對象的形式交換數據。
在SOAP的下層,可以是HTTP/HTTPS(現在用得最多),也可以是SMTP/POP3、還可以是爲一些應用而專門設計的特殊通信協議。
SOAP應用系統可以兩種模式工作,一種被稱爲RPC(Remote Procedure Call),另一種叫法不統一,在Microsoft的文檔中稱作document oriented,而在Apache的文檔中,被稱爲message-oriented,這是一種可以利用XML交換更爲複雜的結構數據的應用,而且,潛在地可用於B2B中長事務處理等領域。在本文中,我們將集中討論RPC,關於後者,我希望我能夠在今後的文章中爲大家介紹。關於RPC的計算架構見圖1.2。
圖1.2 RPC計算架構模型
從圖1.2中,我們可以看到,SOAP RPC的工作原理非常類似於WEB的請求/應答方式,無非用的是符合SOAP規範的XML代替HTML,HTTP是個無狀態協議,但是今天通過Session來管理狀態已經是一個衆所共知的技術了,無狀態協議非常適合鬆偶合系統,而且對於負載平衡等問題都有潛在的優勢和貢獻。
- 客戶程序創建了一個XML文檔,它包含了提供服務的URI、客戶端請求調用的方法名和參數信息。如果參數時對象,則必須進行序列化操作。
- 目標服務器接收到客戶程序發送的XML文檔,對齊進行解析,如果參數時對象,先對其進行反序列化操作,然後執行客戶端請求的方法。
- 目標服務器執行方法完畢後,如果方法的返回值時對象,則進行序列化操作,然後把返回值以XML文檔的形式返回給客戶。
- 客戶程序收到服務器發來的XML文檔,如果返回值時對象,則先進行反序列化操作,最後獲取返回值。
XML Parser指的是XML解析器,DOM(Document Object Model)接口指的是文檔對象模型接口。
目前HLR接口機在消息交互機制上是請求/應答,採用Socket(TCP)通信方式,對外接口協議是自定義的MML協議規範,這種基於Socket的通信在通信層實現較爲簡單,但是自定義的MML協議規範在靈活性和開放性上欠缺,自定義MML協議是的BOSS或其他對接系統在解析時都需要花費很多的成本和精力。
SOAP可以和許多英特網協議綁定,但是目前來說技術最成熟和應用最廣泛的就是和HTTP協議綁定。在HTTP協議上綁定SOAP方式,以其開放型、穩定性、可縮放性和安全的Internet特點,在提高我們HLR產品對外接口的易用性和易維護性上都有好處。
Axis框架來自 Apache 開放源代碼組織,它是基於JAVA語言的最新的 SOAP 規範(SOAP 1.2)和 SOAP with Attachments 規範(來自 Apache Group )的開放源代碼實現。有很多流行的開發工具都使用AXIS作爲其實現支持Web服務的功能,例如JBuilder以及著名的Eclipse J2EE插件Lomboz。AXIS的最新版本,可以從http://ws.apache.org/axis/index.html下載。Axis總體上是一個SOAP引擎,但又不僅僅是個引擎,它還以下功能:
- 是一個簡單的獨立的服務器;
- 是一個可插入到servlet引擎(如Tomcat)中的服務;
- 可擴展的支持WSDL;
- 能根據WSDL產生JAVA文件/類;
- 包括一些例子程序;
- 包括一個可以監控TCP/IP包的工具。
Axis起源於IBM的SOAP4J,是Apache SOAP的第三代產品,相對於以前的版本,它有如下特性:
- 快速,它使用了基於事件的SAX解析機制;
- 靈活,用戶可以靈活定製擴展;
- 穩定,接口將會變動很小;
- 基於組件開發
- 支持WSDL1.1。
Tomcat服務器是一種Servlet/JSP容器,作爲Servlet容器負責處理客戶請求,吧請求傳送給Servlet並把結果返回給客戶。Tomcat和AXIS組合時,Tomcat充當Apache-AXIS Web應用容器,而Apahce-AXIS Web又充當了SOAP服務的容器。SOAP客戶端程序可以通過Apache AXIS API來發送RPC請求。訪問SOAP服務,如圖2.1所示:
圖2.1 SOAP客戶和SOAP服務
創建基於RPC的SOAP服務包括兩個步驟:
- 創建提供SOAP服務的Java類;
- 創建SOAP服務的發佈描述文件。
下載相關文件:
- 安裝Tomcat5.0.28,這是當前穩定版本,目前最新版本爲5.517版本。Tomcat運行在80端口(默認安裝是8080端口,可以通過修改conf\server.xml文件來修改http服務監聽的端口),訪問http://localhost/檢查Tomcat是否安裝成功。
- 下載AXIS:
- 官方站:http://ws.apache.org/axis/
- 下載軟件:axis-bin-1_4(目前Java語言的最新版本)
- 下載相關包:
- mail.jar 下載地址:http://java.sun.com/products/javamail
- activation.jar 下載地址:http://java.sun.com/products/javabeans/glasgow/jaf.html
- xerces.jar 下載地址:http://xml.apache.org/xerces-j/index.html
- xmlsec.jar 下載地址:http://xml.apache.org/security/
安裝步驟如下:
- 將四個相關包全部拷到<CATALINA_HOME>/common/lib目錄(即Tomcat安裝目錄下的common/lib)下。
- 解壓axis-bin-1_2_1.tar.gz,將壓縮包裏的webapps/axis拷到網站根目錄下(如果Tomcat是默認配置,那就是<CATALINA_HOME>/webapps目錄)。
修改Tomcat的配置文件server.xml(<CATALINA_HOME>/conf/server.conf)
在</Host>前加入:<Context path="/axis" docBase="C:\Program Files\Apache Software Foundation\Tomcat\webapps\axis" debug="0" reloadable="true" ></Context>(docBase裏是你的實際axis存放的目錄)
注意:此步至關重要,AXIS的安裝文檔中沒有提到此步,導致很多人安裝不成功。如果沒有此步,訪問http://localhost/axis時,提示如下錯誤:Can't find bundle for base name i18n, locale en_US(zh_CN)。
表面是上看i18n國際化的問題,但既使i18n.properties,i18n_zh_cn.properties存在,也無法找到。網上很多朋友安裝到此,無法繼續下去,我也找了很多資料,都沒有解決。後來查看Tomcat的文檔,猜測是Tomcat配置的問題,經測試終於成功。
另外,也可以將axis/WEB-INF/classes/*,axis/WEB-INF/lib/*的所有文件,拷到<CATALINA_HOME>/common/lib/這個方法比較麻煩。
注:AXIS客戶端,需要拷貝axis/WEB-INF/lib/*到<CATALINA_HOME>/common/lib/。
Axis支持三種web service的部署和開發,分別爲:
- Dynamic Invocation Interface ( DII)
- Dynamic Proxy方式
- Stubs方式
前兩種方式配置比較簡單,但是有兩個缺點不利於版本發佈
- 不能指定package,所有的服務必須拷貝到同一目錄,不能指定包;
- 需要有Service的源碼,AXIS服務必須使用java的源代碼文件,僅僅是把源代碼文件改名爲jws,客戶端訪問例如http://localhost:8080/axis/HelloService.jws方式,不利於保密;
以下章節重點介紹第三種方式,客戶端和服務器之間消息。
編寫服務器代碼如下:
package demo;
import java.io.*;
public class HelloService
{
public String sayHello(String username,int a)
{
System.out.println(username+a);
return "Hello:"+username+"~~~~~~~~~~square="+a*a;
}
}
將編譯後的文件拷貝到Axis /WEB-INF/classes下創建一個新目錄demo,
如:D:\tomcat\webapps\axis\WEB-INF\classes\demo
在D:\tomcat\webapps\axis\WEB-INF目錄下創建文件deploy.txt
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<service name="HelloService" provider="java:RPC">
<parameter name="className" value="demo.HelloService"/>
<parameter name="allowedMethods" value="*"/>
</service>
</deployment>
使用java org.apache.axis.client.AdminClient deploy.txt,發佈服務。
發佈服務後,可以輸入http://localhost:8080/axis/services/HelloService?wsdl來查看該服務的wsdl文件,可以端可以根據wsdl文件來生成java代碼進行調用,也可以直接寫客戶端代碼如下:
1)直接編寫的客戶端代碼
import java.util.*;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.encoding.XMLType;
public class TestClient {
public static void main(String [] args) {
try {
String endpoint = "http://localhost:8080/axis/services/HelloService";
//String endpoint = "http://localhost:8080/axis/HelloService.jws";
Service service = new Service();
Call call = (Call) service.createCall();
call.setTargetEndpointAddress( new java.net.URL(endpoint) );
long timeStart, timeEnd;
String username = " XXX Corp."";
int a = 9;
call.setOperationName( "sayHello" );
call.addParameter("username",org.apache.axis.encoding.XMLType.XSD_STRING,javax.xml.rpc.ParameterMode.IN);
call.addParameter("a",org.apache.axis.encoding.XMLType.XSD_INT,javax.xml.rpc.ParameterMode.IN);
call.setReturnType( XMLType.XSD_STRING);
timeStart = Calendar.getInstance().getTimeInMillis();
for(int i=0;i<1;i++) {
String ret = (String)call.invoke( new Object[] {username,a} );
a++;
System.out.println("Got result : " + ret);
}
timeEnd = Calendar.getInstance().getTimeInMillis();
System.out.println(Long.toString( (timeEnd - timeStart)));
System.out.println(Float.toString(1000 * ( (float) 1) /(float) (timeEnd - timeStart)));
}
catch (Exception e) {
System.err.println(e.toString());
}
}
}
編譯後運行客戶端程序java TestCliet即可運行,輸出結果爲“Got result : Hello:ZTE Corp.~~~~~~~~~~square=81”。可以設置循環控制變量爲其他值,用於測試性能。
請求消息
POST /axis/services/HelloService HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.4
Host: localhost:8080
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: ""
Content-Length: 599
<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><sayHello soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><in0 xsi:type="xsd:string">ZTE Corp.</in0><in1 href="#id0"/></sayHello><multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">9</multiRef></soapenv:Body></soapenv:Envelope>
響應消息
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/xml;charset=utf-8
Date: Tue, 29 Aug 2006 10:41:10 GMT
Connection: close
<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><sayHelloResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><sayHelloReturn xsi:type="xsd:string">Hello:ZTE Corp.~~~~~~~~~~square=81</sayHelloReturn></sayHelloResponse></soapenv:Body></soapenv:Envelope>
2)有AXIS根據wsdl自動生產的接口文件
在DOS命令行中如下命令:
java org.apache.axis.wsdl.WSDL2Java -p client http://localhost:8080/axis/services/HelloService?wsdl
會在當前目錄下產生client文件夾,裏面包含四個java源碼文件,生成的stub client文件列表爲:
1. HelloServiceService.java
2. HelloService_PortType.java
3. HelloServiceServiceLocator.java
4. HelloServiceSoapBindingStub.java
客戶端代碼如下(放置在client同級目錄編譯並運行):
import java.io.*;
import client.*;
import java.net.*;
import java.util.*;
public class HelloServiceClient{
public static void main(String[] args){
try{
long timeStart, timeEnd;
HelloServiceService service = new client.HelloServiceServiceLocator();
int a=9;
timeStart = Calendar.getInstance().getTimeInMillis();
for(int i=0;i<1;i++) {
client.HelloService_PortType client1 = service.getHelloService();
String retValue=client1.sayHello("ZTE Corp.",a);
System.out.println(retValue);
a++;
}
timeEnd = Calendar.getInstance().getTimeInMillis();
System.out.println(Long.toString( (timeEnd - timeStart)));
System.out.println(Float.toString(1000 * ( (float) 1) /(float) (timeEnd - timeStart)));
}
catch (Exception e){
System.err.println("Execution failed. Exception: " + e);
}
}
}
編譯運行即可,輸出結果爲“Hello:ZTE Corp. ~~~~~~~~~~square=81”。 可以設置循環控制變量爲其他值,用於測試性能。
請求消息
POST /axis/services/HelloService HTTP/1.0
Content-Type: text/xml; charset=utf-8
Accept: application/soap+xml, application/dime, multipart/related, text/*
User-Agent: Axis/1.4
Host: localhost:8070
Cache-Control: no-cache
Pragma: no-cache
SOAPAction: ""
Content-Length: 631
<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><ns1:sayHello soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://demo"><in0 xsi:type="xsd:string">ZTE Corp.</in0><in1 href="#id0"/></ns1:sayHello><multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:int" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">9</multiRef></soapenv:Body></soapenv:Envelope>
響應消息
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/xml;charset=utf-8
Date: Tue, 29 Aug 2006 10:42:07 GMT
Connection: close
<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><ns1:sayHelloResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://demo"><sayHelloReturn xsi:type="xsd:string">Hello:ZTE Corp.~~~~~~~~~~square=81</sayHelloReturn></ns1:sayHelloResponse></soapenv:Body></soapenv:Envelope>
Java實現的SOAP,繼承了Java的可部署的特點,任何class文件都可以在線編輯和替換,對於後續的維護工作十分方便,但是消息發送和處理性能不十分理想,簡單測試結果爲每秒90次請求/應答,可能在性能上對接口的要求滿足不夠,是否具有進一步優化的餘地還需要進一步研究。
使用AXIS做客戶端,由於沒有找到如何把請求的http協議版本改爲1.1,所以http消息不支持keep-alive,每次都使用close方式,可能也會對性能影響較大。
gSOAP編譯工具提供了一個SOAP/XML 關於C/C++ 語言的實現,從而讓C/C++語言開發web服務或客戶端程序的工作變得輕鬆了很多。絕大多數的C++web服務工具包提供一組API函數類庫來處理特定的SOAP數據結構,這樣就使得用戶必須改變程序結構來適應相關的類庫。與之相反,gSOAP利用編譯器技術提供了一組透明化的SOAP API,並將與開發無關的SOAP實現細節相關的內容對用戶隱藏起來。gSOAP的編譯器能夠自動的將用戶定義的本地化的C或C++數據類型轉變爲符合XML語法的數據結構,反之亦然。這樣,只用一組簡單的API就將用戶從SOAP細節實現工作中解脫了出來,可以專注與應用程序邏輯的實現工作了。gSOAP編譯器可以集成C/C++和Fortran代碼(通過一個Fortran到C的接口),嵌入式系統,其他SOAP程序提供的實時軟件的資源和信息;可以跨越多個操作系統,語言環境以及在防火牆後的不同組織。
gSOAP使編寫web服務的工作最小化了。gSOAP編譯器生成SOAP的代碼來序列化或反序列化C/C++的數據結構。gSOAP包含一個WSDL生成器,用它來爲你的web服務生成web服務的解釋。gSOAP的解釋器及導入器可以使用戶不需要分析web服務的細節就可以實現一個客戶端或服務端程序。
-
-
-
- gSOAP+VC開發客戶端
-
-
gSOAP是開放的C/C++源碼的soap服務器實現,本章節簡單介紹使用gSOAP開發2.2.1.3中的AXIS服務器的客戶程序。
下載gSOAP工具的代碼地址,當前最新版本是2.7.8c版本:
http://sourceforge.net/project/showfiles.php?group_id=52781
解壓縮本地目錄,進入bin目錄
根據wsdl生成頭文件方式有以下幾種:
生成C++代碼
$ wsdl2h -o testClient.h http://localhost:8080/axis/services/HelloService?wsdl
生成C++代碼,不是用STL
$ wsdl2h -s -o testClient.h http://localhost:8080/axis/services/HelloService?wsdl
生成純C代碼
$ wsdl2h -c -o testClient.h http://localhost:8080/axis/services/HelloService?wsdl
本例使用C++代碼(含STL)方式
生成客戶端代碼,使用如下命令(不帶-C參數生成客戶端和服務器代碼)
soapcpp2 -C -I../import testClient.h
打開VC,創建一個Win32的控制檯程序testClient,"Project", Settings", select the "Link" tab (the project file needs to be selected in the file view) and add "wsock32.lib。
把剛剛生成的以下代碼添加到工程中去:
soapStub.h soapH.h soapC.cpp soapClient.cpp stdsoap2.h stdsoap2.cpp
在main函數中寫入代碼testClient.cpp:
// soap.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "soapH.h"
#include "HelloServiceSoapBinding.nsmap"
int main(int argc, char* argv[])
{
struct soap soap;
std::string a="ZTE Corp.";
int b=0;
std::string result;
if (argc < 3)
{ fprintf(stderr, "Usage: string num\n");
exit(0);
}
soap_init(&soap);
soap_init2(&soap,SOAP_IO_KEEPALIVE,SOAP_IO_KEEPALIVE);
a=argv[1];
b = atoi(argv[2]);
DWORD begin= GetTickCount();
for (int i=0;i<1;i++)
{
soap_call_ns1__sayHello(&soap, "http://10.41.25.70:8080/axis/services/HelloService", "", a, b, result);
if (soap.error)
{
soap_print_fault(&soap, stderr);
exit(1);
}
else
printf("result = %s\n", result.c_str());
b++;
}
DWORD end= GetTickCount();
printf("每秒處理%d個\n", 1*1000/(end-begin));
soap_destroy(&soap);
soap_end(&soap);
soap_done(&soap);
system("pause");
return 0;
}
編譯完成後,直接以命令行方式運行程序testClient “ZTE Corp.” 9
輸出結果爲result = Hello:“ZTE~~~~~~~~~~square=0,設置循環控制變量可以簡單用於測試性能。
代碼中使用soap_init2可以設置http1.1消息頭的connection屬性爲keep-alive,當設置爲keep-alive時,每秒交互可以達到360次以上,如果使用close方式,每秒交互大約200次以上。
發出的請求消息如下:
POST /axis/services/HelloService HTTP/1.1
Host: 10.41.25.70:8080
User-Agent: gSOAP/2.7
Content-Type: text/xml; charset=utf-8
Content-Length: 478
Connection: close
SOAPAction: ""
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://demo"><SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><ns1:sayHello><in0>ZTE Corp.</in0><in1>9</in1></ns1:sayHello></SOAP-ENV:Body></SOAP-ENV:Envelope>
響應消息:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/xml;charset=utf-8
Date: Tue, 29 Aug 2006 10:36:45 GMT
Connection: close
<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><ns1:sayHelloResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://demo"><sayHelloReturn xsi:type="xsd:string">Hello:ZTE Corp.~~~~~~~~~~square=81</sayHelloReturn></ns1:sayHelloResponse></soapenv:Body></soapenv:Envelope>
-
-
-
- gSOAP+VC開發服務器
-
-
gSOAP是開放的C/C++源碼的soap服務器實現,詳細介紹如下。
本例使用C++代碼(含STL)方式
生成服務端代碼,使用如下命令(不帶-S參數生成客戶端和服務器代碼)
soapcpp2 –S -I../import testClient.h
打開VC,創建一個Win32的控制檯程序soapWeb,"Project", Settings", select the "Link" tab (the project file needs to be selected in the file view) and add "wsock32.lib。
把剛剛生成的以下代碼添加到工程中去:
soapStub.h soapH.h soapC.cpp soapServer.cpp stdsoap2.h stdsoap2.cpp
在main函數中寫入代碼soapWeb.cpp:
/ // soapWeb.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "soapH.h"
#include "HelloServiceSoapBinding.nsmap"
int main(int argc, char* argv[])
{
struct soap soap;
int m, s; // master and slave sockets
soap_init(&soap);
soap_init2(&soap,SOAP_IO_KEEPALIVE,SOAP_IO_KEEPALIVE);
m = soap_bind(&soap, "10.41.25.70", 8080, 100);
if (m < 0)
soap_print_fault(&soap, stderr);
else
{
fprintf(stderr, "Socket connection successful: master socket = %d\n", m);
for (int i = 1; ; i++)
{
s = soap_accept(&soap);
if (s < 0)
{
soap_print_fault(&soap, stderr);
break;
}
fprintf(stderr, "%d: accepted connection from IP=%d.%d.%d.%d socket=%d", i,
(soap.ip >> 24)&0xFF, (soap.ip >> 16)&0xFF, (soap.ip >> 8)&0xFF, soap.ip&0xFF, s);
if (soap_serve(&soap) != SOAP_OK) // process RPC request
soap_print_fault(&soap, stderr); // print error
fprintf(stderr, "request served\n");
soap_destroy(&soap); // clean up class instances
soap_end(&soap); // clean up everything and close socket
}
}
soap_done(&soap); // close master socket and detach environment
return 0;
}
SOAP_FMAC5 int SOAP_FMAC6 ns1__sayHello(struct soap*, std::string _in0, int _in1, std::string &_sayHelloReturn)
{
_sayHelloReturn = "Hello:"+_in0+"~~~~~C++~~~~~~";
int tmp=_in1*_in1;
char tmpStr[255]={0};
itoa(tmp,tmpStr,10);
_sayHelloReturn += tmpStr;
return SOAP_OK;
}
調用後,客戶端返回Hello:111111~~~~~C++~~~~~~4032064
使用gSOAP+VC客戶端兩千次循環測試結果,服務端和客戶端代碼中有無對soap_init2性能有很大影響
服務器和客戶端都有soap_init2的大約每秒1420次;
服務器有soap_init2,客戶端無soap_init2的大約每秒360次
服務器無soap_init2,客戶端有soap_init2的大約每秒360次
服務器和客戶端都無soap_init2的大約每秒360次;
也就說HTTP1.1消息,當服務器和客戶端connection對爲keep-alive時,性能提高很大。
使用gSOAP做服務器可以肯定比AXIS性能有明顯提高,附上網上有一篇關於RPC性能測試的對比報告。
安裝了AXIS並在環境變量classpath中配置了AXIS.jar,運行以下命令可以對端口進行監控,監控接收和發送的http包:
java org.apache.axis.utils.tcpmon 8080 localhost 8011
比較AXIS和gSOAP兩種SOAP容器,各有特點。AXIS主要需要通過java來進行編碼,在維護比較方便,例如做補丁程序可以在線替換,部署也很簡單,但是性能上可能無法滿足要求,在目前版本中還沒有JAVA的應用,而且前後臺通信上也很難套用現在的支撐系統。gSOAP實現的服務器和客戶端性能上可以達到要求,開源的代碼實現起來也不復雜,C/C++的實現方式,比較符合我們產品的主流,所以外部接口需要使用SOAP實現的話,建議採用gSOAP。
本文僅僅是對SOAP的初步研究,對於SOAP技術還有許多技術細節和相關技術知識,本文只作爲入門文檔供大家參考。
- 附錄 RPC性能對比測試
今天寫個程序測試了一下Axis(SOAP), Hessian(Binary), Burlap(XML-RPC), REST的性能。
服務端的是一個簡單的加密、解密方法,各種協議使用同一個實現的代碼。客戶端是獨立的java程序,分別用各種協議對服務端的方法進行調用。每一種協議循環調用n次,然後取平均值。
循環1,000次的測試
第一次
Axis --------------->> Total time: 11123 ms, Avg time: 11.123 ms
Burlap ------------->> Total time: 866 ms, Avg time: 0.866 ms
Hessian ------------>> Total time: 581 ms, Avg time: 0.581 ms
REST --------------->> Total time: 929 ms, Avg time: 0.929 ms
AxisUsingWSDL2Java ->> Total time: 11998 ms, Avg time: 11.998 ms
第二次
Axis --------------->> Total time: 11256 ms, Avg time: 11.256 ms
Burlap ------------->> Total time: 816 ms, Avg time: 0.816 ms
Hessian ------------>> Total time: 582 ms, Avg time: 0.582 ms
REST --------------->> Total time: 919 ms, Avg time: 0.919 ms
AxisUsingWSDL2Java ->> Total time: 11908 ms, Avg time: 11.908 ms
循環10,000次的測試
第一次
Axis --------------->> Total time: 88013 ms, Avg time: 8.8013 ms
Burlap ------------->> Total time: 5789 ms, Avg time: 0.5789 ms
Hessian ------------>> Total time: 5162 ms, Avg time: 0.5162 ms
REST --------------->> Total time: 8316 ms, Avg time: 0.8316 ms
AxisUsingWSDL2Java ->> Total time: 112801 ms, Avg time: 11.2801 ms
第二次
Axis --------------->> Total time: 87359 ms, Avg time: 8.7359 ms
Burlap ------------->> Total time: 5784 ms, Avg time: 0.5784 ms
Hessian ------------>> Total time: 5084 ms, Avg time: 0.5084 ms
REST --------------->> Total time: 7983 ms, Avg time: 0.7983 ms
AxisUsingWSDL2Java ->> Total time: 113234 ms, Avg time: 11.3234 ms
測試結果
Hessian最快,Burlap第二,REST第三,Axis最慢。前3種要比Axis快了10倍或者更多。
上面的測試,服務端用的是Resin-3.0.13,出於好奇,我又用Tomcat-5.5.9測試了一把,結果是Resin確實比Tomcat快些。
Tomcat-5.5.9 循環10,000次的測試
Axis --------------->> Total time: 122551 ms, Avg time: 12.2551ms
Burlap ------------->> Total time: 6401 ms, Avg time: 0.6401ms
Hessian ------------>> Total time: 5745 ms, Avg time: 0.5745ms
REST --------------->> Total time: 8090 ms, Avg time: 0.809ms
AxisUsingWSDL2Java ->> Total time: 156908 ms, Avg time: 15.6908ms
Java環境設置
winXP下jdk環境變量配置,如JAVA_HOME,PATH,CLASSPATH等。
若jdk的目錄是d:\jdk
則添加環境變量如下:
JAVA_HOME: d:\jdk;
CLASSPATH: .;%JAVA_HOME%\jre\lib\rt.jar;%JAVA_HOME%\lib\tools.jar;%JAVA_HOME%\lib;
PATH: 在原有字段後添加 %JAVA_HOME%\bin