[英]WCF Windows Service - Long operations/Callback to calling module
我有一個Windows服務,它取一堆文件的名稱並對它們進行操作(zip / unzip,更新db等)。 根據發送到服務的文件的大小和數量,操作可能需要一些時間。
(1)向該服務發送請求的模塊將等待處理文件。 我想知道是否有一種方法可以在服務中提供一個回調,它將在完成處理文件時通知調用模塊。 請注意,多個模塊可以一次調用服務來處理文件,因此服務需要提供某種TaskId我猜。
(2)如果調用了一個服務方法並且正在運行另一個對同一服務的調用,那么該調用將如何處理(我認為只有一個與該服務相關聯的線程)。 我已經看到,當服務在處理方法時花費時間時,與服務相關聯的線程開始增加。
WCF確實提供了雙工綁定,允許您指定回調契約,以便服務可以回調給調用客戶端進行通知。
但是,在我看來,這種機制相當薄弱,並不值得推薦。
在這種情況下,當調用導致相當長的運行操作發生時,我會做這樣的事情:
如果你想堅持使用HTTP / NetTcp綁定,我會:
因此,在您的情況下,您可以刪除壓縮某些文件的請求。 該服務將啟動並完成其工作並將生成的ZIP存儲在臨時位置。 然后,客戶端可以檢查ZIP是否准備就緒,如果是,則檢索它。
這在每個Windows服務器計算機中都存在的消息隊列(MSMQ)上工作得更好(但很多人似乎並不了解它或使用它):
通過閱讀優秀的MSDN文章Foudnations:構建隊列WCF響應服務 ,了解如何有效地完成所有這些 - 強烈推薦!
在我看來,基於消息隊列的系統往往比基於雙工/回調合約的系統更穩定,更不容易出錯。
(1)實現這一點的最簡單方法是使用taskId,然后使用另一個名為IsTaskComplete的方法,客戶端可以使用該方法檢查任務是否已完成。
(2)對服務的附加調用將啟動新線程。
編輯:默認服務行為是每次調用啟動新線程。 可配置屬性是Instance Context Mode ,可以設置為PerCall,PerSession或Shareable。
這個問題有一個解決方案,但是我正在使用WCF雙工服務來獲得長時間操作的結果,即使我發現一個問題花了我幾個小時來解決(這也是我之前搜索過這個問題的原因),現在它完美運行,我相信它是WCF雙工服務框架內的一個簡單解決方案。
長時間操作有什么問題? 主要問題是在服務器執行操作時阻塞客戶端接口,並且通過WCF雙工服務,我們可以使用回調客戶端來避免阻塞(這是一種避免阻塞的舊方法,但它可以很容易地轉換為使用TaskCompletionSource的async / await框架)。
簡而言之,該解決方案使用一種方法在服務器上異步啟動操作並立即返回。 結果准備就緒后,服務器通過客戶端回調返回它們。
首先,您必須遵循任何標准指南來創建WCF雙工服務和客戶端,我發現這兩個有用:
然后按照以下步驟添加您自己的代碼:
使用事件管理器方法定義回調接口,以從服務器發送結果並在客戶端中接收它們。
public interface ILongOperationCallBack { [OperationContract(IsOneWay = true)] void OnResultsSend(....); }
使用方法定義服務接口以傳遞long操作所需的參數(請參閱CallBackContractAttribute中的前一個ILongOperationCallBack接口)
[ServiceContract(CallbackContract=typeof(ILongOperationCallBack))] public interface ILongOperationService { [OperationContract] bool StartLongOperation(...); }
在實現服務接口的Service類中, 首先獲取客戶端回調的代理並將其保存在類字段中 ,然后異步啟動長操作並立即返回bool值。 完成長操作工作后,使用客戶端回調代理字段將結果發送到客戶端。
public class LongOperationService:ILongOperationService { ILongOperationCallBack clientCallBackProxy; public ILongOperationCallBack ClientCallBackProxy { get { return OperationContext.Current.GetCallbackChannel<ITrialServiceCallBack>()); } } public bool StartLongOperation(....) { if(!server.IsBusy) { //set server busy state //**Important get the client call back proxy here and save it in a class field.** this.clientCallBackProxy=ClientCallBackProxy; //start long operation in any asynchronous way ......LongOperationWorkAsync(....) return true; //return inmediately } else return false; } private void LongOperationWorkAsync(.....) { .... do work... //send results when finished using the cached client call back proxy this.clientCallBackProxy.SendResults(....); //clear server busy state } .... }
在客戶端創建一個實現ILongOperationCallBack的類來接收結果並添加一個方法來啟動服務器中的長操作(start方法和事件管理器不需要在同一個類中)
public class LongOperationManager: ILongOperationCallBack { public busy StartLongOperation(ILongOperationService server, ....) { //here you can make the method async using a TaskCompletionSource if(server.StartLongOperation(...)) Console.WriteLine("long oper started"); else Console.Writeline("Long Operation Server is busy") } public void OnResultsSend(.....) { ... use long operation results.. //Complete the TaskCompletionSource if you used one } }
筆記:
我在StartLongOperation方法中使用bool返回來指示服務器是Busy而不是down,但只有當long操作不能像我的實際應用程序那樣並發時才需要,並且可能在WCF中有最好的方法實現非並發(發現服務器是否已關閉,像往常一樣添加Try / Catch塊)。
我沒有看到記錄的重要引用是需要在StartLongOperation方法中緩存回調客戶端代理。 我的問題是我試圖在工作方法中獲取代理(是的,所有示例都在服務方法中使用回調客戶端代理,但是在文檔中沒有明確說明,並且在長時間操作我們必須延遲回調直到操作結束)。
在服務方法返回之后和下一個服務方法之前,不要獲取並緩存兩次回調代理。
免責聲明:我沒有添加代碼來控制錯誤等。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.