Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/273.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# WCF Windows服务-对调用模块的长时间操作/回调_C#_.net_Wcf_Windows Services_Callback - Fatal编程技术网

C# WCF Windows服务-对调用模块的长时间操作/回调

C# WCF Windows服务-对调用模块的长时间操作/回调,c#,.net,wcf,windows-services,callback,C#,.net,Wcf,Windows Services,Callback,我有一个Windows服务,它使用一堆文件的名称并对它们进行操作(zip/unzip、更新数据库等)。操作可能需要时间,具体取决于发送到服务的文件的大小和数量 (1) 向该服务发送请求的模块将等待文件处理完毕。我想知道是否有一种方法可以在服务中提供回调,当调用模块处理完文件时,回调将通知调用模块。请注意,多个模块可以一次调用该服务来处理文件,因此该服务需要提供某种类型的TaskId (2) 如果一个服务方法被调用并且正在运行,并且对同一个服务进行了另一个调用,那么将如何处理该调用(我认为只有一个

我有一个Windows服务,它使用一堆文件的名称并对它们进行操作(zip/unzip、更新数据库等)。操作可能需要时间,具体取决于发送到服务的文件的大小和数量

(1) 向该服务发送请求的模块将等待文件处理完毕。我想知道是否有一种方法可以在服务中提供回调,当调用模块处理完文件时,回调将通知调用模块。请注意,多个模块可以一次调用该服务来处理文件,因此该服务需要提供某种类型的TaskId


(2) 如果一个服务方法被调用并且正在运行,并且对同一个服务进行了另一个调用,那么将如何处理该调用(我认为只有一个线程与该服务相关)。我已经看到,当服务处理一个方法时,与服务相关联的线程开始增加。

WCF确实提供了双工绑定,允许您指定回调约定,以便服务可以回调调用客户端进行通知

然而,在我看来,这种机制相当脆弱,不值得推荐

在这种情况下,当调用导致相当长的运行操作发生时,我会这样做:

如果您想坚持HTTP/NetTcp绑定,我会:

  • 通过服务放弃请求,然后“放手”——这将是一个单向呼叫,你只需放弃你想做的事情,然后你的客户就完成了
  • 拥有一个状态调用,客户端可以在给定时间后调用该状态调用,以确定请求的结果现在是否准备就绪
  • 如果是,则应该有第三个服务调用来检索结果
因此,在您的情况下,您可以放弃压缩一些文件的请求。该服务将启动并执行其工作,并将生成的ZIP存储在临时位置。然后,客户机可以检查ZIP是否已准备就绪,如果已准备就绪,则检索它

这在消息队列(MSMQ)上工作得更好,每个Windows server计算机上都有消息队列(但似乎不是很多人知道或使用它):

  • 您的客户机将请求放到请求队列中
  • 该服务侦听该请求队列,一个接一个地获取请求,并运行正常
  • 然后,该服务可以将结果发布到结果队列中,您的调用者依次在该队列中侦听
通过阅读优秀的MSDN文章,了解如何有效地完成所有这些工作-强烈推荐

在我看来,与基于双工/回调契约的系统相比,基于消息队列的系统往往更稳定,更不容易出错。

(1)实现这一点的最简单方法是使用您注意到的taskId,然后使用另一个名为IsTaskComplete的方法,客户端可以使用该方法检查任务是否已完成

(2) 对服务进行的其他调用将启动新线程


编辑:默认的服务行为是每次调用启动新线程。可配置属性是,并且可以设置为PerCall、PerSession或Shareable。

这个问题有一个解决方案,但我使用WCF双工服务来获得长时间操作的结果,尽管我发现了一个花了我几个小时才解决的问题(这就是我之前搜索这个问题的原因),但现在它工作得很好,我相信这是WCF双工服务框架内的一个简单解决方案

长期手术有什么问题?主要问题是在服务器执行操作时阻塞客户机接口,通过WCF双工服务,我们可以使用对客户机的回调来避免阻塞(这是一种避免阻塞的旧方法,但可以使用TaskCompletionSource轻松地将其转换为异步/等待框架)

简而言之,该解决方案使用一种方法在服务器上异步启动操作并立即返回。当结果准备就绪时,服务器通过客户端回调返回结果

首先,您必须遵循任何标准指南来创建WCF双工服务和客户机,我发现以下两个很有用:

然后按照以下步骤添加您自己的代码:

  • 使用事件管理器方法定义回调接口,以从服务器发送结果并在客户端接收结果

    public interface ILongOperationCallBack
    { 
        [OperationContract(IsOneWay = true)]
        void OnResultsSend(....);        
    }
    
  • 使用传递长操作所需参数的方法定义服务接口(请参阅CallBackContractAttribute中前面的ILongOperationCallBack接口)

  • 在实现服务接口的服务类中,首先获取客户端回调的代理并将其保存在类字段中,然后异步启动长操作工作并立即返回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
        }
        ....
    }
    
  • 注:

  • 我在操作方法中使用bool return来表示服务器繁忙而不是停机,但只有当长操作不能像在实际应用程序中那样并发时才有必要使用bool return,而且在WCF中可能有实现非并发的最佳方法(要发现服务器是否停机,请像往常一样添加Try/Catch块)

  • 我没有看到文档中的重要引用是需要在操作方法中缓存回调客户机代理。我的问题是,我试图在工作方法中获取代理(是的,所有示例都在服务方法中使用回调客户机代理,但文档中没有明确说明,并且在长时间操作的情况下,我们必须将回调延迟到操作结束)

  • 在服务方法返回之后和下一次打开之前,不要获取和缓存两次回调代理
    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
        }
        ....
    }
    
    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
        }
    }