簡體   English   中英

WCF Windows服務 - 長時間操作/回調到調用模塊

[英]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雙工服務和客戶端,我發現這兩個有用:

msdn雙工服務

Codeproject文章WCF雙工服務

然后按照以下步驟添加您自己的代碼:

  1. 使用事件管理器方法定義回調接口,以從服務器發送結果並在客戶端中接收它們。

     public interface ILongOperationCallBack { [OperationContract(IsOneWay = true)] void OnResultsSend(....); } 
  2. 使用方法定義服務接口以傳遞long操作所需的參數(請參閱CallBackContractAttribute中的前一個ILongOperationCallBack接口)

     [ServiceContract(CallbackContract=typeof(ILongOperationCallBack))] public interface ILongOperationService { [OperationContract] bool StartLongOperation(...); } 
  3. 在實現服務接口的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 } .... } 
  4. 在客戶端創建一個實現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 } } 

筆記:

  1. 我在StartLongOperation方法中使用bool返回來指示服務器是Busy而不是down,但只有當long操作不能像我的實際應用程序那樣並發時才需要,並且可能在WCF中有最好的方法實現非並發(發現服務器是否已關閉,像往常一樣添加Try / Catch塊)。

  2. 我沒有看到記錄的重要引用是需要在StartLongOperation方法中緩存回調客戶端代理。 我的問題是我試圖在工作方法中獲取代理(是的,所有示例都在服務方法中使用回調客戶端代理,但是在文檔中沒有明確說明,並且在長時間操作我們必須延遲回調直到操作結束)。

  3. 在服務方法返回之后和下一個服務方法之前,不要獲取並緩存兩次回調代理。

免責聲明:我沒有添加代碼來控制錯誤等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM