精彩的人生

          好好工作,好好生活

          BlogJava 首頁 新隨筆 聯系 聚合 管理
            147 Posts :: 0 Stories :: 250 Comments :: 0 Trackbacks

          #

            Web services 是一種很有前途的技術,在面向服務的架構( Service Oriented Architectures , SOA )中起著重要的作用。這種正在興起的技術的一個關鍵方面就是提供了異步服務的能力。盡管現在的 web service 標準規范中包括了提供異步服務的內容,但客戶端應用程序前景的細節還有一些混亂和模糊。 Web services 回調是實現這些異步服務的一個重要因素。這篇文章為創建帶有回調操作的 Web services 的客戶應用程序提供了實踐指導。這篇文章中所有的代碼段都來自于您可以下載的例子。這些例子包含了這些代碼段的完整實現和使用指導。

          術語

            在開始討論支持回調的客戶端之前,闡明相關術語是很重要的。下圖就顯示了客戶端使用帶有回調操作的 Web service 時所涉及到的主要實體。

          Figure 1
          圖 1. 調用 Web service 的客戶端

            上圖描述了客戶端對 Web service 的調用。 Web service 能夠在一段時間后通過對客戶端的回調做出響應 。因此,包含回調操作的 Web service 客戶端的特別之處在于,客戶端本身必須提供一個端點。我們調用這一回調端點 ,并將這個端點定義為由 URI 確定的唯一地址, SOAP 請求消息將發送到這個 URI 。

            將 SOAP 消息發送到Web service 端點 之后,客戶端本身開始與 Web service 進行交互。由客戶端發送給 Web service 的相關請求消息及其相關響應消息構成了客戶端初始化操作 。如前所述,客戶能夠處理 Web service 發送到回調端點的請求消息。相關的請求和響應消息一起被稱為一個回調 操作。

            理解這些術語之后,讓我們走近一步考察 Web service 回調的概念,以及它與會話式 Web services 和異步 Web service 調用之間的關系。

          異步、回調和會話

            異步 Web service 調用的概念有時容易與 Web services 回調和會話式 Web services 相混淆。雖然這三個概念很相關,但卻不同。

            異步 Web services 調用 是指在不阻塞接收服務器發來的相應響應消息的情況下,客戶端能夠發送 SOAP 消息請求 。 BEA WebLogic Platform 8.1 web services 上進行異步 Web service 調用的過程已經詳細地 記錄在軟件文檔中了 ,因此在本文中不作進一步的討論。

            Web services 回調 是指 Web services 提供者向客戶端發回 SOAP 消息的情況。 Web Services Description Language (WSDL) specifications 將這種操作定義為一種“請求 / 響應”。支持回調操作的 Web services 客戶端本身必須有一個 Web services 端點,這樣 Web service 就可以利用這個 Web services 端點在任意時間發送回調請求,也就是說,可以是異步的。注意,這跟我們上面討論的異步調用 沒有關聯。

            如果一系列可在兩個端點之間來回傳送的消息可以被唯一會話 ID 追蹤,而這個 ID 又由特定的操作來標識消息流的始末,這種 Web services 就是 會話式 的。提供回調的 Web services 也定義為會話式的。這是因為正常情況下如果 Web services 能夠對客戶端進行回調訪問,它就必須有它自己的回調端點信息。這種情況只有客戶端做出了初始調用以后才會發生。因此,至少由客戶啟動的初始化操作和由 Web services 做出的回調操作是相互關聯的,并且由唯一的會話 ID 跟蹤。如果不是這樣,客戶端就無法分辨與不同初始調用相關聯的回調操作。

            我們現在將考慮這些問題并創建支持回調的客戶端,就像我們剛才所看到的,這些客戶端構成了請求 - 響應和會話式 Web services 的基礎。

          創建支持回調的客戶端

            正如前面討論的,支持回調的 Web services 客戶端需要提供一個能夠異步接收和處理回調操作消息的回調端點。為避免必須提供回調端點這類復雜的事情,一種叫做 polling (輪詢)的技術可以作為替代技術。然而這種技術要求客戶端周期性地調用服務端以校驗回調事件。如果這種事件很少發生,那么調用的開銷就太大了。如果客戶端提供一個回調端點并直接處理回調操作,這種開銷就可以避免。

            我們還應該注意到,盡管可以通過用 JMS 上的 Web services (如果提供了這種連接)創建支持回調的客戶端,但這種方法有一些限制,其中一個重要的限制就是客戶端要被迫采用與 Web services 提供者相同的 JMS 實現。因此我們將集中于經過 HTTP 傳輸來完成我們的任務。有兩個領域需要創建這樣的客戶端:創建適當的客戶端啟動 SOAP 消息,以及處理回調操作。

            當客戶端啟動有回調的 Web service 操作時,它需要以某種方式包含回調端點的 URI ,使其在請求消息中監聽。 Web Services Addressing SOAP Conversation Protocol 規范都定義了 SOAP 頭部元素,允許您實現這一目標。從理論上說,用于規定回調端點的規范并不重要。但是大多數 Web services 容器(包括 BEA WebLogic Server 8.1 )都還沒有包含 Web services Addressing 規范的實現形式。當前, BEA WebLogic Workshop 8.1 的 Web services 支持 SOAP Conversation Protocol 規范,我們將在例子客戶端中使用。

            根據 SOAP Conversation Protocol , SOAP 頭部在會話的不同階段是不同的。對于會話中的第一個客戶端啟動(開始)操作,我們需要通過 callbackLocation 頭部元素 提供有回調端點的 Web service 。所有操作(包括第一個操作)也將需要唯一的 ID ,這個 ID 在整個會話過程中都用在我們的 SOAP 消息里。這可通過 conversationID 元素 來完成。下面是一個啟動支持回調會話的 SOAP 請求消息的例子:

          <soapenv:Envelope soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" 
              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" 
              xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/" 
              xmlns:env="http://schemas.xmlsoap.org/soap/envelop/" 
              xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
            <soapenv:Header>
              <con:StartHeader soapenv:actor="http://schemas.xmlsoap.org/soap/actor/next" 
               soapenv:mustUnderstand="0" 
               xmlns:con="http://www.openuri.org/2002/04/soap/conversation/">
                <con:conversationID>[123456]:192.168.1.100:8181</con:conversationID>
                   <con:callbackLocation>
                    http://192.168.1.100:8181/StockNotificationCallback
                   </con:callbackLocation>
                </con:StartHeader>
           </soapenv:Header>
            <soapenv:Body>
              <n1:registerForThresholdNotif xmlns:n1="http://www.openuri.org/">
                <n1:stockTicker>CCC</n1:stockTicker>
                <n1:threshold>10</n1:threshold>
              </n1:registerForThresholdNotif>
            </soapenv:Body>
          </soapenv:Envelope>
          

            現有的大多數 Java Web service 工具包(例如 BEA WebLogic 的 clientgen 、 Apache Axis 和 JWSDP )都允許您創建一個代理庫,客戶端程序可以容易地用它來調用 Web services 操作。但是,這些框架中沒有一種支持回調操作,主要問題是它們的代理不生成所需的頭部。在能提供這種支持以前,通過擴展它們對回調操作的支持來利用這些框架(例如復雜類 XML 映射),這種益處還是很需要的。一種用來達到這種效果的技術就是應用 SOAP 消息處理程序

            上面提到的所有 Web services 工具包都能支持 SOAP 消息處理程序。消息處理程序是一種 Java 類,它實現了 javax.xml.rpc.handler.GenericHandler 界面,并且還包含一種稱為先送出(或后接收) SOAP 消息的方法。這里介紹我們客戶端中的消息處理程序,它能夠找出一個特定會話的當前階段,并據此擴展帶有所需頭部的請求消息。

            注意到這一點是很重要的,客戶端 SOAP 消息處理程序必須能確定消息屬于會話的哪個階段,以便創建合適的頭部元素。生成會話 ID 也是客戶端處理程序要完成的一個任務。

            一旦 Web services 端點收到擴展的請求消息,它就會將請求消息發送到由開始消息的 callbackLocation 元素規定的回調端點。在大多數情況下,客戶端過程自身就需要提供這個端點,并且恰當地處理回調消息。如果客戶端在 Web services 的容器中運行,這項工作就可以通過把有回調操作的 Web services 部署在同一個容器內來完成。然而,如果客戶端不是正在容器中運行,這項工作就要靠在一個嵌入在客戶端過程本身的輕量級容器中執行回調端點來完成。這使我們能夠調用客戶端生成的操作,并且處理同一過程上下文中的傳入回調操作。注意在回調端點背后(和在客戶端中)的過程實體要求不僅能夠分配對適當域的代碼操作,而且還能處理 XML 映射。

            當然,客戶端也可以選擇簡單地設置恰當的 callbackLocation 頭部元素來規定一個在容器中的回調端點,而這個容器與訪問過程相分離。

            正如我們已經看到的,為帶回調操作的 Web services 創建客戶端并不是毫無意義的,它包含了復雜元素,比如處理 SOAP 消息、管理會話(包括階段和 ID )、參數映射以及操作分配。當 Web service 通過 BEA WebLogic Workshop Service Control 訪問時,這些復雜性就都不存在了,它會自動為用戶執行所有操作。

          使用服務控件創建支持回調的客戶端

            通過 WebLogic Workshop Service Control 訪問 Web services 在 軟件文檔 中已經做了詳細描述。如果客戶端實體能夠使用控件(也就是 Java Process Definition 業務流程或其他控件; Page Flows 不能使用控件回調),這個解決方案就是最簡單的使用支持回調的 Web services 的方案了。采用這種方法,所有涉及訪問支持回調的 Web service 的復雜性就都迎刃而解了。

          股票通知服務例子

            本文的例子包括一個股票通知( Stock Notification )的會話式 Web service 和一個能闡明概念的示例客戶端。 Web service 提供的操作允許客戶端注冊股票接收機并設置一個相關的閾值價格。然后服務端就監視股票,只要股票價格超過了閾值價格就通過一個回調( onThresholdPassed() )通知客戶端。

          Figure 2
          圖 2. 本圖說明了這個例子的配置

            很顯然,輪詢技術不是創建客戶端的最佳解決方案:股票價格有可能經常超過閾值價格也可能極少超過閾值價格。輪詢間隔短就會造成很多不必要的訪問,而另一方面,輪詢間隔長又可能導致股票價格超過閾值價格很長時間后客戶端才被告知。

            客戶端應用程序中回調端點的實現應該是一種更好的解決方案。讓我們首先來看一下例子客戶端是如何將操作傳遞給 StockNotification Web service 的。我們已經采用兩種不同的技術實現了客戶端。第一種技術使用 WebLogic Workshop 生成的代理庫,并由 StockNotificationClient 類的 makeCallUsingBEAProxy() 方法來實現:

          public void makeCallUsingBEAProxy() {
              try {
                // Get the proxy instance so that we can start calling the web service operations
                StockNotificationSoap sns = null;
                StockNotification_Impl snsi = new StockNotification_Impl();
                sns = snsi.getStockNotificationSoap();
          
                // Register our conversational handler so that it can properly set our
                // our conversation headers with a callback location
                QName portName = new QName("http://www.openuri.org/",
                                           "StockNotificationSoap");
                HandlerRegistry registry = snsi.getHandlerRegistry();
                List handlerList = new ArrayList();
                handlerList.add(new HandlerInfo(ClientConversationHandler.class, null, null));
                registry.setHandlerChain(portName, handlerList);
          
                // Register, call some methods, then sleep a bit so that our callback
                // location can receive some notifications and finally finish the conversation.
                sns.registerForThresholdNotif("AAAAA", "100");
                sns.addToRegistration("BBBBB", "5");
                StockList stocks = sns.getAllRegisteredStocks();
          
                ...
          
                sns.endAllNotifications();
              }
              catch (Exception e) {
                throw new RuntimeException(e);
              }
          }
          

            正如從上述代碼段中看到的,我們可以使用 clientgen 生成的類。與沒有回調的 Web service 調用相比,這段代碼唯一強調的就是,在方法一開始就實例化和注冊了客戶端 SOAP 消息處理程序。這個處理程序截取了發出的 SOAP 消息,并加上必要的會話頭部。每當客戶端過程發出 SOAP 請求消息時,消息處理程序的 handleRequest() 方法就被調用,它利用了一個包含了輸出 SOAP 請求信息的 MessageContext 對象。下面是例子客戶端消息處理程序的代碼段:

          package weblogic.webservice.core.handler;
          
          ...
          
          public class ClientConversationHandler
              extends GenericHandler {
          
          public boolean handleRequest(MessageContext ctx) {  
            ...
            if (phase.equals("START")) {
              headerElement
                        = (SOAPHeaderElement) header
                        .addChildElement("StartHeader",
                                         "con",
                                         "http://www.openuri.org/2002/04/soap/conversation/");
          
              headerElement.addChildElement("con:callbackLocation")
                        .addTextNode(CALLBACK_URI);
            }
            else if (phase.equals("CONTINUE") || phase.equals("FINISH")) {
              headerElement
                        = (SOAPHeaderElement) header
                        .addChildElement("ContinueHeader",
                                         "con",
                                         "http://www.openuri.org/2002/04/soap/conversation/");
            }
          
            headerElement.addChildElement("con:conversationID").addTextNode(convID);
            ...
          }
          }
          

            BEA clientgen 工具生成的代理庫已經用了一個缺省的客戶端 SOAP 消息處理程序,可創建會話式 StartHeader 頭部元素。不幸的是,目前的 clientgen 輸出不支持回調操作,因此缺省的處理程序不會創建所需的 callbackLocation 次級元素。所以我們必須保證自己的消息處理程序重寫缺省的會話式處理程序,以便管理創建 callbackLocation 頭部的額外任務。我們通過確保處理程序在一個與缺省處理程序相同的程序包中創建來做到這一點,這個程序包就是 weblogic.webservice.core.handler ,并且它還有具體的相同類名稱 ClientConversationHandler 。注意,這種技術涉及到重寫現有會話消息處理程序,但它并不能保證是向前兼容的。這個例子展示了消息處理程序如何用于創建回調頭部,以及這種概念如何應用于任何其他支持 SOAP 消息處理程序的 Web services 框架中。

            有一種使用 SOAP 消息處理程序和代理類的可選技術,它使用 JAXM API 直接創建 SOAP 請求消息,并把它們送往 Web service 。這是我們例子客戶端采用的第二種技術,它在 StockNotificationClient 類的 makeCallsUsingJAXM() 和 callMethodFromFile() 方法中實現:

          public void makeCallsUsingJAXM() {
                callMethodFromFile("C:\\registerForNotifSOAP.xml");
                ...
                callMethodFromFile("C:\\endAllNotifSOAP.xml");
          }
          
          private void callMethodFromFile(String fileName) {
          
              try {
                // Create a SOAP connection object
                SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance();
                SOAPConnection conn = scf.createConnection();
          
                // Create the SOAP request message from the specified file
                MessageFactory mf = MessageFactory.newInstance();
                SOAPMessage msg = mf.createMessage();
                SOAPPart sp = msg.getSOAPPart();
          
                // Read the file content into a string and replace the conversation ID with
                // the current one.
                String fileContent = readFileIntoString(fileName);
                fileContent = fileContent.replaceFirst(CONV_ID_HOLDER,
                                                       ConvIDGenerator.getInstance().
                                                       getConversationID());
          
                StreamSource prepMsg = new StreamSource(new StringBufferInputStream(
                    fileContent));
                sp.setContent(prepMsg);
                msg.saveChanges();
          
                // Make the actual call
                conn.call(msg, TARGET_SERVICE_URI);
                conn.close();
              }
              catch (Exception ex) {
                throw new RuntimeException(ex);
              }
          }   
          

            callMethodFromFile() 方法從指定的文件讀取 SOAP 消息,用客戶端的當前會話 ID 取代消息會話 ID ,最后生成實際調用。當使用 JAXM 方法時, Java 到 XML 的映射操作參數和返回值必須由客戶端來完成。通過使用從文件中預先生成的 SOAP 消息(它已經包含了所需的參數值),我們已經避免了這一任務。盡管從概念上來說這種方法很好,但對于比較復雜的客戶端端來說,這不是一種可行的解決方案。這些客戶端必須從頭開始通過編程方式使用 JAXM 來創建消息。

            既然我們已經回顧了客戶端在生成 Web services 操作中的作用,下面讓我們轉向客戶端的回調端點的實現方式和對來自 Web services 回調消息的處理。因為回調端點需要能夠處理輸入的 HTTP 請求, servlet 就是一種合適的選擇。進一步說,我們非常需要一個 servlet ,它能夠從輸入的 HTTP 請求中提取 SOAP 消息,并以一種容易使用的方式提供給我們。 javax.xml.messaging 程序包里的 ReqRespListener 接口和 JAXMServlet 類提供給我們這個功能。只要安排合理,應用了這種接口和類的 servlet 將把所有的目標 HTTP post 請求自動轉換成 SOAPMessage 實例,并通過 onMessage() 方法傳遞給我們。下面的代碼顯示了 onMessage() 方法如何幫助例子客戶端實現這些功能。

          public class CallBackHandlerServlet
              extends JAXMServlet
              implements ReqRespListener {
          
          
            public SOAPMessage onMessage(SOAPMessage message) {
              try {
                // We have just received a SOAP message at the callback
                // endpoint. In this example we will just assume that
                // the message received is in fact the onThresholdPassed
                // callback request. However, a servlet handling
                // multiple callbacks cannot make this assumption and
                // should process the SOAP message to see what callback
                // it relates to and do as appropriate for each.
                String stockTicker = extractOnThresholdPassedArgument(message);
          
          	System.out.println("[DEV2DEV CALLBACK EXAMPLE]: Received treshold notification 
          	                                for: " + stockTicker);
          
                // Now we have to create a proper response to the callback
                // request. Returning this response as part of the onMessage()
                // method will ensure that the SOAP response gets back to the server.
                SOAPMessage response = createThresholdPassedResponse();
          
                return response;
              }
              catch (Exception e) {
                throw new RuntimeException(e);
              }
            }
            ...
          }
          

            一旦回調請求消息被 onMessage() 方法所接收,客戶端就從股票接收機中提取參數,將它輸出到標準設備上,并生成一條相應的響應消息,它還會通過 servlet 返回到 Web service 。使用帶有多個回調操作的 Web services 客戶端也需要把傳入的請求分發給客戶端代碼的合適部分,這個過程是基于相應的回調操作的。

            盡管 servlet 包含了合適代碼來處理來自 Web services 的傳入 onThresholdPassed() 回調消息,但它需要在監聽回調 URI 的 servlet 容器中部署完成,這樣才能完成回調端點的實現。 Jetty 開源項目有一個輕量級 servlet 容器,它可以嵌入在我們的客戶端中。將 CallBackHandlerServlet servlet 部署在一個嵌入式 Jetty 容器中,允許我們將回調端點駐留在客戶端 Java 進程中。 StockNotificationCallbackProcessor 類是一種客戶端使用的公共類,它用于啟動 Jetty 服務器和部署CallBackHandlerServlet servlet :

          public class StockNotificationCallbackProcessor {
          
            public static final String CALLBACK_SERVER_PORT = ":8181";
            public static final String CALLBACK_SERVLET_CLASS = 
                 "asynchwsconsumer.CallBackHandlerServlet";
            public static final String CALLBACK_SERVLET_NAME = "CallBackHandlerServlet";
          
            private HttpServer server;
          
            public StockNotificationCallbackProcessor() {}
          
            public void startListening() {
          
              try {
                // Configure the server so that we listen on the callback URI
                server = new Server();
                server.addListener(CALLBACK_SERVER_PORT);
                ServletHttpContext context = (ServletHttpContext) server.getContext("/");
                context.addServlet(CALLBACK_SERVLET_NAME, "/*",
                                   CALLBACK_SERVLET_CLASS);
          
                // Start the http server
                server.start();
              }
              catch (Exception e) {
                throw new RuntimeException(e);
              }
            }
          
            public void stopListening() {
              try {
                server.stop();
              }
              catch (Exception e) {
                throw new RuntimeException(e);
              }
            }
          }
          

            startListening() 方法把 CallBackHandlerServlet 部署在端口 8181 上并配置嵌入式 servlet 容器,這樣所有 HTTP 請求都將指向這個端口。這種方法還啟動了嵌入式 servlet 容器。

            現在我們已經考察了 Web service 操作的創建和回調操作的處理,下面我們來看看例子客戶端的 main() 方法是如何使用這些元素的:

          public static void main(String[] args) {
          
              StockNotificationClient snClient = new StockNotificationClient();
          
              snClient.snCallbackProcessor.startListening();
          
              snClient.makeCallUsingBEAProxy();
          
              ConvIDGenerator.getInstance().resetConversationID();
          
              snClient.makeCallsUsingJAXM();
          
              snClient.snCallbackProcessor.stopListening();
          }
          

            正如您所看到的,這種方法開始于建立一個回調端點(正如前面所看到的,它包括啟動嵌入式 servlet 容器和部署回調過程)。該方法接著使用 BEA clientgen 代理來調用股票通知的 Web service 。這樣,這種與 Web services 的交互就通過調用 endAllNotifications() 端操作來完成會話,客戶端需要改變會話 ID 以啟動新會話。客戶端使用 ConvIDGenerator 單一類來管理會話 ID ,而 ConvIDGenerator 類又使用 java.rmi.dgc.VMID 類來創建合適的格式化唯一會話 ID 。

            一旦會話 ID 發生了變化,客戶端就會用 JAXM 再一次調用股票通知 Web services 。最后,客戶端終止嵌入式 servlet 容器,這個過程就結束了。

            我們現在已經完成了對例子客戶端的分析,該例子客戶端可作為本文的一部分下載。當您運行客戶端和股票通知 Web services 的時候,客戶端和 Web service 都將把消息顯示在標準輸出設備上,用以描述會話的不同階段,包括對回調操作的處理。

          結束語

            本文展示了為包含回調操作的 Web services 創建獨立客戶端過程的步驟。這種客戶端需要管理 Web services 調用的會話方面,并提供一個端點用于 Web service 進行回調操作。

          下載

          CallbackExample.zip (1.7Mb) ——這個文件包括了本文講述的完整實例應用程序。

          參考資料

          原文出處

          http://dev2dev.bea.com/pub/a/ 2005/03/c allback_clients.html

          posted @ 2006-05-07 16:19 hopeshared 閱讀(1339) | 評論 (0)編輯 收藏

          摘要:

          到目前為止,web service交互作用是獨立同步的,同時本質上是應答式的。不過,很顯然同步應答類型在基于消息的應用中只是一個很小的子集。消息在耦合松散系統中是非常重要的,因此這種限制很關鍵。Web service規范,例如WS-addressing和WSDL,已經融入了消息的概念并且為包含一個相當大范圍的消息應用奠定了基礎。Apache Axis2 架構既不基于任一個消息交換模式,也不基于同步/異步行為。這篇文章解釋了消息概念和Axis2在幾種眾所周知的消息場合中怎樣被擴展使用。

          關于Apache Axis2的Web service消息

          作者:Srinath Perera, Ajith Ranabahu

          07/27/2005

          翻譯:doomsday

          版權聲明:可以任意轉載,轉載時請務必以超鏈接形式標明文章原始出處和作者信息及本聲明
          英文原文地址:
          http://www.onjava.com/pub/a/onjava/2005/07/27/axis2.html
          中文地址:
          http://www.matrix.org.cn/resource/article/43/43723_Apache_Axis2.html
          關鍵詞: Apache Axis2 Web service

          ??
          ???? 到目前為止,web service交互作用是獨立同步的,同時本質上是應答式的。不過,很顯然同步應答類型在基于消息的應用中只是一個很小的子集。消息在耦合松散系統中是非常重要的,因此這種限制很關鍵。Web service規范,例如WS-addressing和WSDL,已經融入了消息的概念并且為包含一個相當大范圍的消息應用奠定了基礎。Apache Axis2 架構既不基于任一個消息交換模式,也不基于同步/異步行為。這篇文章解釋了消息概念和Axis2在幾種眾所周知的消息場合中怎樣被擴展使用。

          消息的簡單介紹

          ????貫穿計算歷史,分布式運算是其中的一個很大的挑戰:當資源是分布式時,進程間的通信變得相當困難,研究人員仍然在尋找更好的解決方案。有趣的是,幾乎所有關于分布式計算機計算能力問題的解決方案來源于兩種概念基礎: 遠程過程調用(RPC) 和消息傳遞。

          ????毫無疑問,使用RPC在開發人員中是非常流行的技術,部分原因是是本地過程調用與它的類似之處。本地過程調用在程序員中很有人氣,所以在分布式系統中使用RPC是很自然的選擇,而另一方面,消息傳遞不是非常流行,當它被提起時很少有開發人員關注它。不過,在某些場合使用消息相比RPC系統有更好的優勢。

          RPC和消息框架的基本差異如下所示:

          ●消息完全不懂客戶端和服務器,因為一個消息框架(通常所說的面向消息的中間件,或message-oriented middleware,即MOM)集中于傳遞消息,所有接收和散發消息的節點身份平等,術語稱之為對等體。RPC始終有服務請求者 (AKA client) 和服務提供者 (AKA server)的概念。
          ●消息對于一個特定范疇是時間獨立的。沒有任何對等體希望實時接收消息--當對等體可用時MOM關注于傳遞一個消息到相應的對等體,然而,RPC在失去一方時立即失效。
          ●消息可被復制并且輕易的傳遞到眾多對待體。RPC本質上是一種一對一的交流方式,而消息更靈活,并且毫不費力地傳遞同一消息的拷貝到多種對等體。

          Web service消息

          ????Web service是在XML消息的基礎上定義的,下面3個因素描述了一個給定的Web service的消息交互。

          ●消息交換模式
          ●同步和異步客戶端API
          ●單向和雙向傳送行為

          ????從最抽象的角度來講,,web service消息傳遞建立在發送和接收消息基礎上,一個給定的消息被一方發出,并且被另一方接收。消息可能相互關聯,識別這些相互關聯的消息群中的最常見的應用場合是非常重要的,這些消息群被定義為消息交換模式(message exchange patterns),簡稱MEPs.

          ????過渡時期下在兩種相關消息間的一個服務請求者的行為在客戶端API定義了消息同步/異步行為。同步場合下,客戶端請求將會阻塞,在相關消息到達目的地后前一直等待,在非阻塞場合下,客戶端請求不會阻塞,當相關消息到達時,它與之前的消息相互聯系。

          ????傳送分類為單向或雙向,基于單方或雙方行為。類似SMTP和JMS傳送即是單向傳送的,不會阻塞,另一方面,類似HTTP和TCP即是雙向傳送,相關消息可能在回路中返回,實際上,在Web service消息中,雙向傳送可能被用作單向傳送,但是在這些場合下,它們可被有效的處理為單向方式。

          消息交換模式

          ????根據W3C建議,消息交互模式就是一個為交流雙方構建消息交換的一個模板,一個MEP將相關消息確定為一組。MEPs根據服務請求者和服務提供者來定義,需要注意,為了清晰,MEPs以服務提供者的消息特性來命名。為方便理解,所有的命名都可以用request代替in, 用response代替out。

          例如,我們看看兩個有名的MEPS

          1.In-only/"發后不理:" 服務請求者發送消息給服務提供者但是不關心任何后繼相關消息
          2.In-out/"應答式:" 服務請求者發送消息給服務提供者并希望返回結果

          ????MEPS概念仍在擴展中,模式的數目是沒有限制的,所以Web service中間件應用強制使用幾種選定的MEPs,"發后不理"和 "應答式" 是被明確使用的,其它大多數的模式可由這兩種組合使用。

          客戶端API同步/異步行為

          ????同步/異步(或阻塞/非阻塞)行為是基于在web service請求的線程,同步服務將會阻塞,等待相關消息到達。另一方面,異步請求僅僅返回,等待相關消息被后臺另一個不同線程執行。

          ????這兩種途徑有典型的用例。考慮一下銀行事務,其需要一定數量的消息來來回傳遞。銀行事務本質是連續的,當結果到達時后執行下一步驟,因此同步等待結果很有意義。另一方面,設想一個航班預約程序,其需要搜集多種數據來源,根據這些結果再匹配。這個案例中,異步行為發揮作用,因為程序可以提交所有結果并且當數據到達時工作。考慮到網絡響應,異步方式獲得較好的結果。

          ????同步請求很簡單:請求在相關消息到達前等待,并且可以像本地過程調用一樣被編碼。但是異步消息的相互關系就比較復雜,客戶端必須處理這種復雜性。盡管如此,通過一些額外工作來處理這種復雜情況仍是必要的。

          傳輸層的行為

          ????傳輸層的行為是個關鍵因素,決定了Web service消息的發生,傳輸根據依據其行為分類為單向和雙向。
          ????單向傳送減少web service消息的復雜性,因為相關消息必須來源于各自的通道。另一方面,如果傳送是雙向的,消息有機會選擇使用單向還是雙向,例如,傳輸是HTTP時,相關消息來自HTTP連接的返回路徑,或者這么講web service提供者可以寫HTTP 200來指出沒有來自同一連接的響應,在這種案例下回應經由獨立的HTTP連接發送。

          Web service尋址角色

          ????webs ervice尋址框架(也被稱為WS-addressing)是為了在同一web service交互活動中交換不同信息部分,下面的5個因素定義了交互活動:

          ????1.消息交換模式
          ????2.可被訪問的服務傳送
          ????3.相關消息傳送
          ????4.傳送的行為
          ????5.客戶端API的同步/異步行為

          ????服務提供者申明了頭兩個,客戶端定義了其余因素,客戶端級別的同步/異步行為對服務提供者是完全透明的,客戶端使用WS-addressing來解釋web service消息。

          ????在其它大多數結構中,WS-addressing定義了四種標題:To, ReplyTo, RelatesTo, FaultTo以及一個被稱為匿名地址的特定地址,當一個服務提供者接收到SOAP消息時,它會查找在to地址上的目標服務并且調用服務。如果有結果,即被發送到ReplyTo地址,錯誤被發送到FaultTo地址,如果以上任一個的標題沒有指定或具有匿名值,結果通過雙向傳送返回路徑返回(因為匿名決定雙向傳送返回路徑)。

          ????傳送和WS-addressing一起定義了查找相關消息的機制,消息是相關緣于它們共享了相同的傳送通道,也可能因為它們都共享把它們互相鏈接的公共信息。web service RelateTo標題正好提供了這種關系。

          下面的表顯示的明確定義的消息交互活動的尋址標題的不同值



          Axis2客戶端API概念

          ????Axis2客戶端API處理了In-Only和In-OutMEPs,所有的消息結合在下面的章節討論。MEPs的空間是無限的,因此,Axis2強制提供了支持任意消息交換模式的核心,并且提供了兩種常被使用的模式In-Only和In-Out的API,有兩種方法實現更多的復雜模式:組合In-Only和In-Out來完成希望的模式,或者對希望的模式寫新的擴展。因為Axis2為任意MEP提供核心級別的支持,實現是顯而易見的。In-Only和In-OutMEPS被InOnlyMEPClient和InOutMEPClient類支持,下兩節即做具體描述。

          In-Only MEP 支持: InOnlyMEPClient

          ????InOnlyMEPClient類對發送不理消息提供了支持,所有的傳送類型作為單向傳送對待,InOnlyMEPClient和InOutMEPClient真正的差別是尋址參數起先沒有鎖定,并且尋址參數隨后被Axis2控制。作為可被控制的尋址參數,InOnlyMEPClient可被用作消息API,并且在此基礎上構建更復雜的消息交互。

          In-Out MEP 支持: InOutMEPClient

          ????InOutMEPClient和繼承了InOutMEPClient的調用類為應答式消息提供了支持,Axis2關注完整的操作,除了To地址外的所有的尋址屬性都在Axis2的控制下

          ????用戶可以配置InOutMEPClient 來表現不同,利用以下的四個參數。

          1.發送者傳輸
          2.監聽者傳輸
          3.是用單獨監聽
          4.使用阻塞



          ????客戶端API當前提供了針對HTTP和SMTP傳輸的支持,下面的表格顯示了這些參數可能的組合以及它們怎樣結合來提供不同特效。

          舉例

          ????下面的代碼實例顯示了怎樣使用Apache Axis2做幾個定義明確的交互作用,用戶可以在客戶端API簡單的轉換屬性從而轉換不同的交互作用,客戶端Axis2 API僅僅支持XML級別的消息和代表大塊XML的OME元素。

          調用單向消息

          ????單向MEP簡單之處在于在僅有一個消息來回傳送時它能表現正確的單向,這些消息被異步對待并且傳送是單向的。

          應答式消息

          ????可以表現四種方式的應答式消息

          ????1.雙向In-Out 同步
          ????2.雙向In-Out 異步
          ????3.單向In-Out 同步
          ????4.單向In-Out 異步

          ????下面的代碼實例說明這些案例怎樣被Axis2尋址,注意客戶端API的四種屬性怎樣被使用。

          ????1.In-Out同步,HTTP作為雙向傳輸方式

          OMElement payload = .... 
          Call call = new Call();
          call.setTo(
          ????????new EndpointReference(AddressingConstants.WSA_TO,
          ????????????????"HTTP://...));
          call.setTransportInfo(Constants.TRANSPORT_HTTP,
          ?????? Constants.TRANSPORT_HTTP, false);
          OMElement result =
          ????????(OMElement) call.invokeBlocking(
          ????????operationName.getLocalPart(), payload);


          ????這里,SOAP消息經由同一個HTTP連接傳播,地址屬性沒有指定,所以它們在服務器方缺省為匿名,客戶端API將被鎖定直到回復消息到達。

          ????2.In-Out異步,HTTP使用HTTP作為雙向傳送

          //this is the payload goes on the body of SOAP message 
          OMElement payload = ....
          Call call = new Call();
          call.setTo(
          ????????new EndpointReference(AddressingConstants.WSA_TO,
          ????????????????"HTTP://...));
          call.setTransportInfo(Constants.TRANSPORT_HTTP,
          ??????????????Constants.TRANSPORT_HTTP, false);

          Callback callback = new Callback() {
          ????public void onComplete(AsyncResult result) {
          ????????//what user can do to result
          ????}
          ????public void reportError(Exception e) {
          ?????? //on error
          ????}
          };
          call.invokeNonBlocking(operationName.getLocalPart(),
          ?????? payload, callback);


          ????和前面相同,SOAP消息經由同一個HTTP連接傳輸并且不需要尋址,一旦回復消息到達客戶端API不會阻塞并且回調將被執行。

          ????3.In-Out, 異步HTTP 作為單向傳輸

          OMElement payload = .... 
          Call call = new Call();
          call.setTo(
          ????????new EndpointReference(AddressingConstants.WSA_TO,
          ????????????????"HTTP://...));
          call.setTransportInfo(Constants.TRANSPORT_HTTP,
          ????Constants.TRANSPORT_HTTP, true);
          Callback callback = new Callback() {
          ????????public void onComplete(AsyncResult result) {
          ????????....
          ????????}

          ????????public void reportError(Exception e) {
          ????????...
          ????????}
          };
          call.engageModule(new Qname("addressing"));
          call.invokeNonBlocking(operationName.getLocalPart(), method, callback);


          ????在這個案例中,SOAP消息通過兩個HTTP連接傳輸,尋址是強制的,ReplyTo標題出現指示服務器端經由單獨的通道發送回應。客戶端沒有阻塞,當回應消息到達時,喚起回調。

          4.In-Out, 同步 HTTP 作為單向傳送

          OMElement payload = .... 
          Call call = new Call();
          call.setTo(
          new EndpointReference(AddressingConstants.WSA_TO,
          ????????"HTTP://...));
          call.setTransportInfo(Constants.TRANSPORT_HTTP,
          ????Constants.TRANSPORT_HTTP, true);
          OMElement result =
          ????????(OMElement) call.invokeBlocking(
          ?????????? operationName.getLocalPart(), payload);


          ????在這種場合下使用"In-Out,異步HTTP作為單向傳送"類型,在結果到達第二種連接時喚起阻塞,執行并返回結果。

          總結

          ????總而言之,web wervice消息行為建立在三種因素上:消息交互模式,客戶端同步異步模式和傳送行為。Asis2建立核心在不一定要任何MEP類型,不過為MEPs的廣泛支持:單向和應答提供了客戶端API支持,這篇文章解釋Axis2消息支持概念和客戶端API的使用。

          資源
          ??
          Apache Axis2的官方站點
          ●W3C討論稿, "WSDL Version 2.0 Part 2: Message Exchange Patterns"
          "Why Messaging? The Mountain of Worthless Information," Ted Neward的博客, 星期三,2003年5月9日
          "Introduction to WS-Addressing,"??作者Beth Linker, dev2dev, 2005年1月31日
          "Async," Dave Orchard的博客, 2005年5月5日
          "Programming Patterns to Build Asynchronous Web Services,"??Holt Adams, IBM開發者文集, 2002年6月1日

          Srinath Perera是Apache Axis2的主要架構師
          Ajith Ranabahu是Apache Axis2項目的成員并且在基于Web service的項目里工作了3年


          posted @ 2006-05-07 16:05 hopeshared 閱讀(734) | 評論 (0)編輯 收藏

               摘要: 深入學習Web Service系列之 異步開發模式 ——《深入學習 Web Service 系列》之一 Terrylee , 2005 年 12 月 4 日 ...  閱讀全文
          posted @ 2006-05-07 15:28 hopeshared 閱讀(788) | 評論 (0)編輯 收藏

          在確定是否適合在您的應用程序中采用異步 Web 方法時,有幾個問題需要考慮。首先,調用的 BeginXXX 函數必須返回一個 IAsyncResult 接口。IAsyncResult 是從多個異步 I/O 操作返回的,這些操作包括訪問數據流、進行 Microsoft&reg; Windows&reg; 套接字調用、執行文件 I/O、與其他硬件設備交互、調用異步方法,當然也包括調用其他 Web 服務。您可以從這些異步操作中得到 IAsyncResult,以便從 BeginXXX 函數返回它。您也可以創建自己的類以實現 IAsyncResult 接口,但隨后可能需要以某種方式包裝前面提到的某個 I/O 操作。

          對于前面提到的大多數異步操作,使用異步 Web 方法包裝后端異步調用很有意義,可以使 Web 服務代碼更有效。但使用委托進行異步方法調用時除外。委托會導致異步方法調用占用進程線程池中的某個線程。不幸的是,ASMX 處理程序為進入的請求提供服務時同樣要使用這些線程。所以與對硬件或網絡資源執行真正 I/O 操作的調用不同,使用委托的異步方法調用在執行時仍將占用其中一個進程線程。您也可以占用原來的線程,同步運行您的 Web 方法。

          下面的示例顯示了一個調用后端 Web 服務的異步 Web 方法。它已經使用 WebMethod 屬性標識了 BeginGetAge 和 EndGetAge 方法,以便異步運行。此異步 Web 方法的代碼調用名為 UserInfoQuery 的后端 Web 方法,以獲得它需要返回的信息。對 UserInfoQuery 的調用被異步執行,并被傳遞到 AsyncCallback 函數,后者被傳遞到 BeginGetAge 方法。這將導致當后端請求完成時,調用內部回調函數。然后,回調函數將調用 EndGetAge 方法以完成請求。此示例中的代碼比前面示例中的代碼簡單得多,并且還具有另外一個優點,即沒有在與為中間層 Web 方法請求提供服務的相同線程池中啟動后端處理。

          [WebService]

          public class GetMyInfo : System.Web.Services.WebService
          {

          [WebMethod]
          public IAsyncResult BeginGetAge(AsyncCallback cb, Object state)
          {

          // 調用異步 Web 服務調用。
          localhost.UserInfoQuery proxy
          = new localhost.UserInfoQuery();
          return proxy.BeginGetUserInfo("用戶名",
          cb,
          proxy);
          }

          [WebMethod]
          public int EndGetAge(IAsyncResult res)
          {
          localhost.UserInfoQuery proxy
          = (localhost.UserInfoQuery)res.AsyncState;
          int age = proxy.EndGetUserInfo(res).age;
          // 在此對 Web 服務的結果進行其他
          // 處理。
          return age;
          }
          }

          發生在 Web 方法中的最常見的 I/O 操作類型之一是對 SQL 數據庫的調用。不幸的是,目前 Microsoft&reg; ADO.NET 尚未定義一個完好的異步調用機制;而只是將 SQL 調用包裝到異步委托調用中對提高效率沒有什么幫助。雖然有時可以選擇緩存結果,但是也應當考慮使用 Microsoft SQL Server 2000 Web Services Toolkit(英文)將您的數據庫發布為 Web 服務。這樣您就可以利用 .NET Framework 中的支持,異步調用 Web 服務以查詢或更新數據庫。

          通過 Web 服務調用訪問 SQL 時,需要注意眾多的后端資源。如果您使用了 TCP 套接字與 Unix 計算機通信,或者通過專用的數據庫驅動程序訪問其他一些可用的 SQL 平臺,甚至具有使用 DCOM 訪問的資源,您都可以考慮使用眾多的 Web 服務工具包將這些資源發布為 Web 服務。

          使用這種方法的優點之一是您可以利用客戶端 Web 服務結構的優勢,例如使用 .NET Framework 的異步 Web 服務調用。這樣您將免費獲得異步調用能力,而您的客戶端訪問機制會與異步 Web 方法高效率地配合工作。

          使用異步 Web 方法聚合數據

          現在,許多 Web 服務都訪問后端的多個資源并為前端的 Web 服務聚合信息。盡管調用多個后端資源會增加異步 Web 方法模型的復雜性,但最終還是能夠顯著提高效率。

          假設您的 Web 方法調用兩個后端 Web 服務:服務 A 和服務 B。從您的 BeginXXX 函數,您可以異步調用服務 A 和服務 B。您應該向每個異步調用傳遞自己的回調函數。在從服務 A 和服務 B 接收到結果后,為觸發 Web 方法的完成,您提供的回調函數將驗證所有的請求都已完成,在返回的數據上進行所有的處理,然后調用傳遞到 BeginXXX 函數的回調函數。這將觸發對 EndXXX 函數的調用,此函數的返回將導致異步 Web 方法的完成。

          小結

          異步 Web 方法在 ASP.NET Web 服務中提供了一個有效的機制,可以調用后端服務,而不會導致占用卻不利用進程線程池中的寶貴線程。通過結合對后端資源的異步請求,服務器可以使用自己的 Web 方法使同時處理的請求數目達到最大。您應該考慮使用此方法開發高性能的 Web 服務應用程序。


          原文地址:http://ewebapp.cnblogs.com/articles/237372.html
          posted @ 2006-05-07 15:22 hopeshared 閱讀(538) | 評論 (0)編輯 收藏

          為舉例說明異步 Web 方法,我從一個名為 LengthyProcedure 的簡單同步 Web 方法開始,其代碼如下所示。然后我們再看一看如何異步完成相同的任務。LengthyProcedure 只占用給定的毫秒數。

          [WebService]

          public class SyncWebService : System.Web.Services.WebService
          {

          [WebMethod]
          public string LengthyProcedure(int milliseconds)
          {
          System.Threading.Thread.Sleep(milliseconds);
          return "成功";
          }
          }

          現在我們將 LengthyProcedure 轉換為異步 Web 方法。我們必須創建如前所述的 BeginLengthyProcedure 函數和 EndLengthyProcedure 函數。請記住,我們的 BeginLengthyProcedure 調用需要返回一個 IAsyncResult 接口。這里,我打算使用一個委托以及該委托上的 BeginInvoke 方法,讓我們的 BeginLengthyProcedure 調用進行異步方法調用。傳遞到 BeginLengthyProcedure 的回調函數將被傳遞到委托上的 BeginInvoke 方法,從 BeginInvoke 返回的 IAsyncResult 將被 BeginLengthyProcedure 方法返回。

          當委托完成時,將調用 EndLengthyProcedure 方法。我們將調用委托上的 EndInvoke 方法,以傳入 IAsyncResult,并將其作為 EndLengthyProcedure 調用的輸入。返回的字符串將是從該 Web 方法返回的字符串。下面是其代碼:

          [WebService]

          public class AsyncWebService : System.Web.Services.WebService
          {
          public delegate string LengthyProcedureAsyncStub(
          int milliseconds);

          public string LengthyProcedure(int milliseconds)
          {
          System.Threading.Thread.Sleep(milliseconds);
          return "成功";
          }

          public class MyState
          {
          public object previousState;
          public LengthyProcedureAsyncStub asyncStub;
          }

          [ System.Web.Services.WebMethod ]

          public IAsyncResult BeginLengthyProcedure(int milliseconds,
          AsyncCallback cb, object s)
          {
          LengthyProcedureAsyncStub stub
          = new LengthyProcedureAsyncStub(LengthyProcedure);
          MyState ms = new MyState();
          ms.previousState = s;
          ms.asyncStub = stub;
          return stub.BeginInvoke(milliseconds, cb, ms);
          }

          [ System.Web.Services.WebMethod ]
          public string EndLengthyProcedure(IAsyncResult call)
          {
          MyState ms = (MyState)call.AsyncState;
          return ms.asyncStub.EndInvoke(call);
          }
          }


          原文地址:http://ewebapp.cnblogs.com/articles/237375.html
          posted @ 2006-05-07 15:18 hopeshared 閱讀(312) | 評論 (0)編輯 收藏

          僅列出標題
          共30頁: First 上一頁 8 9 10 11 12 13 14 15 16 下一頁 Last 
          主站蜘蛛池模板: 南岸区| 营口市| 新津县| 丹阳市| 大安市| 同心县| 诏安县| 安乡县| 三江| 沙河市| 南宫市| 宿迁市| 吕梁市| 曲沃县| 伊金霍洛旗| 凤城市| 安庆市| 钟祥市| 庐江县| 奎屯市| 石泉县| 新宁县| 繁昌县| 邯郸市| 当阳市| 武乡县| 梅州市| 邢台县| 甘孜县| 阜平县| 翁源县| 佛坪县| 乡宁县| 灵璧县| 宝兴县| 同仁县| 临漳县| 杭锦旗| 汽车| 兰州市| 区。|