【问题标题】:C# .NET Web Service AsynchronouslyC# .NET Web 服务异步
【发布时间】:2011-06-02 16:42:41
【问题描述】:

我正在尝试实现一个 API,它使用 Web 服务执行一些非常冗长的任务。我基本上希望 Web 服务启动一个线程来执行冗长的任务并让它一直运行直到它完成。问题是因为它是一个 API,我希望它是跨平台的。所以我的问题如下:

  • 是否可以进行不需要客户端在 .NET Framework 上的异步调用? (似乎 Begin/End 框架需要我返回 .NET IASyncResult 对象)如果是这样,怎么办?完全明确的示例代码将非常有用。
  • 因为web服务中没有状态保存,这个线程以后能恢复吗?如果客户想要取消流程,这将非常重要。

【问题讨论】:

    标签: c# asp.net multithreading web-services asynchronous


    【解决方案1】:

    好的,请稍等。

    如果您希望人们能够调用 asynch,那么他们可以控制并且您只需响应即可。如果您希望自己完成异步工作,那么您需要制定不同的模式。一种模式是设置一个调用工作的方法,然后设置另一个方法来查看工作的状态。第一种方法返回某种类型的令牌/ID,可用于检查另一个调用的状态。然后,您有第三种方法来获取结果。这都是客户驱动的。

    理论上,您可以设置回调机制,但客户端需要有一种获取答案的方法,这是他们身边的服务。这在本质上更复杂,“公共 API”更少,但它可以与客户端一起使用。

    系统的异构性不应成为一个因素。而且,您可以制定这两种模式,因此拥有可以接受答案的 Web 服务的客户端可以触发并忘记,而其他人则轮询。

    投票的一个缺点是你增加了你的体重,所以要设定适当的期望,并准备好扼杀做这种事情的人:

    while(thread.NoComplete())
    {
       pollTheCrapOutofService();
    }
    

    如果您可以绕过异构环境(开放标准等),并且可以强制使用 .NET,那么您还有其他选择,正如 Craig 所提到的。

    【讨论】:

    • 您描述的模式正是我想要做的。那么它是否需要 Begin/End 模式,还是我可以在 web 方法中启动一个线程?我预见到的问题是如果客户端调用这样的方法,如何找到线程来挂起/恢复/中止它。
    • 好吧,假设每个用户调用一次,您总是可以将线程 ID 存储在数据库中。然后当用户调用 stop 时,您可以发送带有 id 的 kill
    • 如果您只是尝试启动工作,则启动线程并等待返回。至少在大多数情况下,上下文应该在请求和响应中,因此您可以将响应与请求相匹配。如果这不容易与键值匹配,则必须通过将请求存储在数据库中来创建键值。然后,带有响应的请求将等于已完成的任务(可以这么说)。希望这会有所帮助。
    【解决方案2】:

    这是一个很好的起点:

    http://msdn.microsoft.com/en-us/library/aa480516.aspx

    如果您实施此模式,从客户端的角度来看,没有任何变化。您可以实现第二个 Web 方法来检查您在异步方法中排队的任何正在运行的作业的状态。

    从示例代码中进行了一些修改,让您了解您需要做什么(我不希望这会编译):

    [WebService]
    public class AsyncWebService : System.Web.Services.WebService
    {
    public delegate string LengthyProcedureAsyncStub(
        int milliseconds, MyState state);
    
    public string LengthyProcedure(int milliseconds, MyState state) 
    { 
        while(state.Abort == false)
        {
              //Do your work.  Check periodically for an abort
        }
        return state.Abort ? "Aborted" : "Success"; 
    }
    
    //This state object is what you can use to track invocations of your method
    //You'll need to store it in a thread safe container.  Add it to the container in the Begin method and remove it in the end method.  While it's in the container other web methods can find it and use it to monitor or stop the executing job.
    public class MyState 
    { 
        public Guid JobID = Guid.NewGuid();
        public object previousState; 
        public LengthyProcedureAsyncStub asyncStub; 
        public bool Abort = false;
    }
    
    [ 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;
        //Add to service wide container
        return stub.BeginInvoke(milliseconds, cb, ms);
    }
    
    [ System.Web.Services.WebMethod ]
    public string EndLengthyProcedure(IAsyncResult call)
    {
        //Remove from service wide container
        MyState ms = (MyState)call.AsyncState;
        return ms.asyncStub.EndInvoke(call);
    }
    
    [WebMethod]
    public void StopJob(Guid jobID)
    {
         //Look for the job in the service wide container
         MyState state = GetStateFromServiceWideContainer(jobID);
         state.Abort = true;
    }
    }
    

    就客户端而言,他们正在调用一个名为 LenghtyProcedure 的网络方法,该方法在作业完成之前不会返回。

    【讨论】:

    • 这是很好的信息,但有点过时了。我认为现在人们喜欢他们的网络服务比 SOAPy 更 RESTy。
    • 实际上,我的任务是创建一个 SOAP API ......所以这可能是我想要进入的方向。我正在快速阅读这篇文章,但它似乎给了我需要返回 .NET 对象的 Begin/End 模式。我觉得我忽略了一些东西。
    • @Redian - 如果您认为在示例中将 IAsyncResult 传递回客户端,那您就错了。该框架将 BeginXXX/EndXXX 对作为简单的 XXX 公开给客户端。您在 BeginXXX 实现中返回的 IAsyncResult 由框架内部使用。
    • 所以它只是返回结果,而不等待异步方法调用完成? (此时我的大脑几乎要爆炸了——就像盯着视错觉看了 5 个小时)。但是如果我需要取消异步调用,我该如何跟踪它呢?
    • 见上面的编辑。有很多方法可以做到这一点,这只是其中之一。祝你的任务好运 - 这是一项具有挑战性的任务!
    【解决方案3】:

    我们为此使用 AppFabric 工作流服务。

    它们作为 WCF 服务公开,因此任何可以进行 SOAP 调用(或任何其他受支持的 WCF 传输)的东西都可以调用它们。

    持久性是免费的(如果您设置了 SQL Server),监控和管理也是免费的,并且 .NET 客户端是自动生成的。

    一个潜在的缺点:您需要 IIS 7+ 和 .NET 4。

    【讨论】:

    • 我不熟悉 AppFabric。理想情况下,客户将在本地托管 API,而 IIS 出现在他们可能运行的 Windows 版本上,要求他们托管另一项服务不是很多吗?
    • 如果你想托管一个网络服务,你将需要一个网络服务器。 AppFabric 在 IIS 之上运行。
    • IIS 随 Windows Business 版本一起提供,作为添加/删除 Windows 功能。那么 AppFabric 是否也必须与 Web 服务的安装程序一起打包?
    • 连同您的服务本身,是的。这只是一个MSI。或者,您可以从 Web 平台安装程序中获取它。就像我说的,更有意义的限制是 IIS 7。这意味着它在 Server 2003 上不起作用。你需要 Vista+ 或 Server 2008+。
    【解决方案4】:

    是否可以异步 不需要客户端的调用 在.NET Framework 上? (看来 到位的开始/结束框架需要 我返回 .NET IASyncResult 对象) 如果是这样怎么做?

    向您的客户公开一个 REST API。然后,您只需要在 .NET 异步对象和 REST 服务之间转换的代码层。

    因为没有状态保存 在网络服务中,这个线程可以是 后来恢复了吗?

    您可以公开一个返回操作状态的 REST 方法,以及另一个取消异步操作的 REST 方法。

    对于长时间运行的操作,我通常使用ThreadPool 设置 Windows 服务。您可以在那里公开您的 IAsync API。

    在 IIS 服务器上公开 REST API 的最简单方法是使用简单的 ASP.NET MVC 应用程序。您可以直接从控制器方法执行长时间运行的异步进程。

    【讨论】:

    • 能否麻烦您提供一个可能会将我引向这个方向的链接?如果我选择它,我将需要通过我的主管进行课程更改(SOAP 到 REST)。
    • @Redian:啊,我明白了;您已经在使用 SOAP。嗯,关于使用 ASP.NET MVC 创建 REST API 的文档有点参差不齐,所以由于您不熟悉 ASP.NET MVC,我将向您推荐codeproject.com/KB/WCF/RestServiceAPI.aspx,它描述了如何使用WCF。但无论您使用 SOAP 还是 REST,原理都是一样的:创建一层代码,在您的 Web 服务 API 和 .NET 长时间运行的异步 API 之间进行转换。
    • 那么这应该不是问题。我正在为已经编写的基于线程的解决方案实现 API。本质上,那应该作为接口层。这就是我一直在寻找的。如果我使用 ThreadPool 设置服务,我应该能够追踪线程?
    • 是的,如果基于线程的解决方案在其 API 中有一些东西可以实现这一点。有很多方法可以做到这一点。一种方法是将某种唯一的任务令牌交还给启动长期运行服务的原始 SOAP 调用者(它可以像“任务号”一样简单);然后,他们可以在另一个 SOAP 调用中使用该令牌来询问进程的状态,或取消进程。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-12
    • 1970-01-01
    • 2016-09-14
    • 2013-11-14
    相关资源
    最近更新 更多