| 這篇文章在去年就看了,但當(dāng)時不是很理解;現(xiàn)在回頭看,有些體會。 實現(xiàn)SOA的一個重要途徑是使用Xml WebServices,相對一些“高山流水、曲高和寡”的理論和概念來說,對于俺們平庸之輩,它門檻低,可操作性強(qiáng)。 為了便于理解后邊的內(nèi)容,再次老話重提……SOA的四個基本原則: 1)邊界明確:Boundaries Are Explicit 2)服務(wù)自治:Services Are Autonomous 3)服務(wù)共享架構(gòu)和契約:Services Share Schemas & Contacts 4)基于策略來使服務(wù)兼容:Service Compatibilility Is Based Upon Policy. 也許因為翻譯非常抽象,可以仔細(xì)閱讀 原英文文章(建議翻譯的中文頁面就不要看了,越看越迷糊) WebCast教程 
 進(jìn)入正題。文檔處理器(Documoent Processor)、冪等消息(Idempotent Message)和預(yù)訂(Reservation)是在進(jìn)行web 服務(wù)設(shè)計時可參考的三個模式。 
 核心:根據(jù)現(xiàn)有文檔和已知的業(yè)務(wù)事件來構(gòu)建業(yè)務(wù)處理模型。 注解: 1)在進(jìn)行web服務(wù)接口設(shè)計時,應(yīng)完全站在業(yè)務(wù)角度,而非設(shè)計角度; 2)所謂文檔,應(yīng)指的是反映在業(yè)務(wù)流程中不同異構(gòu)系統(tǒng)中用于交換和流通的數(shù)據(jù)資料; 3)所設(shè)計的服務(wù)接口應(yīng)該很好地封裝內(nèi)部實現(xiàn)的細(xì)節(jié); 4)以文檔為參照來定義或重用 XML 架構(gòu)以表示服務(wù)的請求和響應(yīng)消息;并直接從這些架構(gòu)生成對象,以加速開發(fā)。 5)所定義的服務(wù)必須為一個完整的過程,避免“CRUD”; 示例: 本示例設(shè)計:查詢某個國家的客戶。代碼下載 //不好的設(shè)計------返回DataSet類型,如果使用者不是.Net很難使用 [WebMethod(Description="Find customers in the Customer table based on a specific country code。 This method returns a dataset - consider this an ANTI-Pattern for interoperable web services.")] public NWCustomerDataSet FindCustomerByCountryReturnDataSet(string Country) { try { CustomerService svc = new CustomerService(); return svc.FindCustomerByCountryReturnDataSet(Country); } … } //好的設(shè)計-返回一個結(jié)構(gòu)化的文檔信息,簡單規(guī)范,任何使用者都很方便使用 [WebMethod(Description="Find customers in the Customer table based on a specific country code.\nThis method returns a structured \"document\" of customer information - a much cleaner approach than returning datasets.")] public FindCustomerResponse FindCustomersByCountry(string Country) { try { // Get the customer data CustomerService svc = new CustomerService(); NWCustomerDataSet nwCustomers; nwCustomers = svc.FindCustomerByCountryReturnDataSet(Country); // At the service boundary, translate the data into a format // described by the schema FindCustomerResponse Customers = new FindCustomerResponse(); 
 // Copy the data into a data transfer object foreach (NWCustomerDataSet.CustomersRow custRow in nwCustomers.Customers) { // Generated wrapper class Customers.Add(CopyRowToDTO(custRow)); } 
 return Customers; } catch (Exception ex) { System.Diagnostics.Trace.WriteLine(ex.Message); return null; } } 
 //以下是FindCustomerResponse結(jié)構(gòu)化文檔: <?xml version="1.0" encoding="utf-16"?> <xs:schema xmlns="http://www./ <xs:complexType name="NorthwindCustomer"> <xs:sequence> <xs:element name="CustomerID" type="xs:string" /> <xs:element name="CompanyName" type="xs:string" /> <xs:element name="ContactName" type="xs:string" /> <xs:element name="ContactTitle" type="xs:string" /> <xs:element name="Address" type="xs:string" /> <xs:element name="City" type="xs:string" /> <xs:element name="Region" type="xs:string" /> <xs:element name="PostalCode" type="xs:string" /> <xs:element name="Country" type="xs:string" /> <xs:element name="Phone" type="xs:string" /> <xs:element name="Fax" type="xs:string" /> </xs:sequence> </xs:complexType> <xs:complexType name="FindCustomerResponse"> <xs:sequence> <xs:element name="Customer" type="NorthwindCustomer" minOccurs="0" maxOccurs="unbounded" /> </xs:sequence> </xs:complexType> <xs:complexType name="FindCustomerByCountryRequest"> <xs:sequence> <xs:element name="Country" type="xs:string" /> </xs:sequence> </xs:complexType> </xs:schema> 
 問題: 模式固然是好的,但要根據(jù)具體情況進(jìn)行取舍。因為:服務(wù)使用者必須將該結(jié)構(gòu)化的xml文檔映射回其內(nèi)部本身的數(shù)據(jù)架構(gòu),與此同時,效率必然也會大大降低。 
 
 核心:使用帶有公共契約的消息請求進(jìn)行通訊。 
 注解: 1)什么是冪等消息? a) 一來一往,可靠的消息傳輸:可以理解為TCP,一來一往,保證消息是可靠的;不冪等呢,可以理解為UDP,發(fā)送過去,結(jié)果到底怎么樣就不知道了; b) 使用者很有可能多次發(fā)送同一請求,比如在網(wǎng)上購物,用戶反復(fù)點擊結(jié)賬(不知道為什么); 2)如上圖所示,服務(wù)使用方在發(fā)送請求的時候,攜帶一個UOW-工作單元ID;Web服務(wù)接口根據(jù)該UOW來判斷請求的命令是否已經(jīng)執(zhí)行過; 3)關(guān)于UOW:可以把UOW設(shè)計為一個定制的SOAP header或URI,在這里面可以(如果需要)加上直接調(diào)用者及相關(guān)信息,這樣做是為了解決使用者使用同一UOW進(jìn)行多次請求的處理問題: URI協(xié)助服務(wù)方分析調(diào)用者的信息,以決定具體采用什么方式進(jìn)行回應(yīng)? c) 如包含上次沒收到結(jié)果的信息,服務(wù)方就可以再次處理請求; d) 如果是寫操作,且上次已經(jīng)成功返回,則服務(wù)端就可以拋出異常; e) 如果是讀操作,則把對應(yīng)的緩存數(shù)據(jù)返回; 4)由上面幾條,我覺得結(jié)論是:這種模式比較復(fù)雜,并不好操作。因為本來使用復(fù)雜的消息頭或URI已經(jīng)額外增加了工作量;確定合理的緩存策略更是讓人頭疼,制定不好肯定會嚴(yán)重影響服務(wù)性能。 示例: 本示例設(shè)計: 代碼下載 /// <summary> /// This method updates the number of Insureds covered with a relative value. Since each /// call to update query modifies the database value to a new value, the call to update /// database should be made only if not done earlier. This is ensured by checking the RequestId /// passed as first parameter. If the RequestId passed is different from the previously /// passed RequestId value, only than update database call is made. /// Each RequestId is maintained in a list. /// </summary> /// <returns></returns> public UpdatePolicyResponse UpdateInsuredsCovered(int requestId, int policyId, int insuredsCovered) { UpdatePolicyResponse response = new UpdatePolicyResponse(); 
 response.RequestID = requestId.ToString(); response.PolicyId = policyId.ToString(); response.InsuredsCovered = insuredsCovered.ToString(); 
 // // The requestId should be a number greater than zero // if (requestId <= 0) { response.Result = ResultConts.INVALID_REQUEST_MSG; return response; } 
 // // Make the update of the price as required // UpdatePolicy updateData = new UpdatePolicy(); response.Result = updateData.UpdateInsuredsCovered(requestId, policyId, insuredsCovered); return response; } 
 核心:使用預(yù)訂ID、狀態(tài)保存和過期策略,處理需要多次請求才能完成的業(yè)務(wù)流程。 
 1)針對的問題:復(fù)雜的、長時間的業(yè)務(wù)流程。如充值業(yè)務(wù),網(wǎng)上購物等。 2)在完成業(yè)務(wù)過程最后一個步驟之前;所有前邊的請求都被認(rèn)為“試探性操作”,即隨時有可能取消;因此前面所有的試探性操作的結(jié)果都會被跟蹤保存; 3)當(dāng)開始第一步試探性請求時,服務(wù)方會創(chuàng)建一個預(yù)訂的ID ,以后每次校驗該ID,如果不符,則交易取消; 4)服務(wù)端對每次試探性操作蓋上時間戳,超過時間沒有下一個操作,交易也會取消; 5)每一步試探性操作都是一來一回:使用者請求,服務(wù)端處理后發(fā)送確認(rèn)消息,當(dāng)使用者收到確認(rèn)消息以后才算該操作完成; 5)對于重復(fù)預(yù)訂請求,可以結(jié)合Idempotemt模式進(jìn)行處理; 示例: 本示例設(shè)計:代碼下載 // For a given Flight number, check travel date and number of seats. // The service checks the availability of seats and returns the availability as ‘Y‘ or ‘N‘. // If the the seats are available the service also returns a RequestID that can be used // to confirm the reservation. The RequestID is set to 0 if the seats are not available // for the requested flight. If the request is successfully processed the value of Result // element will be "Success" In case of invalid input or exceptional conditions an error // message is returned in the Result element of the response. [WebMethod(BufferResponse=false, CacheDuration=60, Description="Check the availability of reservation in the specified flight. The Travel Date must be in mm/dd/yyyy format and Number of Seats must be greater than Zero. For testing purposes you can use Flight Numbers AI111 or AI112." )] public ReservationStatusResponse CheckSeatAvailability( string FlightNumber,string TravelDate, string NumberofSeats ) { FBReservationService svc = new FBReservationService (); return svc.CheckReservationStatus(FlightNumber,TravelDate,NumberofSeats); } 
 // This service checks the validity of the incoming RequstID. Valid RequestIDs will be // confirmed. Return statuses are as follows: // Confirm - for Valid RequestIDs // Duplicate - for valid but not unexpired RequestIDs // Invalid Requst - for all other RequestIDs // // If the request is successfully processed the value of Result element is "Success" // For invalid input or exceptional conditions an error message is returned in the Result // element. [WebMethod(BufferResponse=false, CacheDuration=60,          Description="Confirm the reservation for the provided  )] public ConfirmReservationResponse ConfirmReservationRequest( string RequestID ) { FBReservationService svc = new FBReservationService (); return svc.ConfirmReservationStatus(RequestID); } 最后,引用一個名言:pattern is “an abstraction from a concrete form which keeps recurring in specific, non-arbitrary contexts." 所謂模式,只是在特定環(huán)境中過程的最佳實踐,并不是萬能的。況且,模式往往是抽象成了最簡單的,現(xiàn)實情況更復(fù)雜,所以,把它當(dāng)成戰(zhàn)略就行了。 
 在設(shè)計的時候,沒有最完美,只有最合適。不能被這些華麗的東西蒙住雙眼,Just Keep It Simple。 
 | 
|  |