【问题标题】:WCF REST Not Processing AsynchronouslyWCF REST 不异步处理
【发布时间】:2011-05-11 04:09:51
【问题描述】:

我们目前正在为我们的站点在 IIS 中实现新的 WCF REST 服务,并且在许多页面上,我们可能会使用 JQuery 异步进行少量 AJAX 调用。问题是 WCF(在服务器端)似乎是同步执行的。

在页面加载时,我们对 3 种不同的方法进行了 3 次单独调用。使用日志记录,我可以看到它们都在大约 5 毫秒内击中了 global.asax 文件。从那里,日志显示所有执行的顺序,它们退出 global.asax(不一定是我们通过 javascript 从页面进行调用的顺序)。我希望每个调用都能接收到自己的线程并单独返回。即使使用调试器附加,我也可以看到它不会执行下一个方法,直到我逐步执行它所在的当前方法。

以下是我“认为”为使用异步模型而实施的三种方法的操作协定。

    [OperationContract(AsyncPattern = true)]
    [WebInvoke(
        Method = "POST"
         , UriTemplate = "/ListUserPreferences"
        , BodyStyle = WebMessageBodyStyle.Wrapped
        , ResponseFormat = WebMessageFormat.Json
        , RequestFormat = WebMessageFormat.Json
    )]
    IAsyncResult BeginListUserPreferences(AsyncCallback callback, object state);
    Result<List<Data.EnumerationItem<UserPreferenceType>>> EndListUserPreferences(IAsyncResult asyncResult);

    [OperationContract(Name = "GetUserSecure", AsyncPattern = true)]
    [WebInvoke(
        Method = "POST"
         , UriTemplate = "/GetUser"
        , BodyStyle = WebMessageBodyStyle.Wrapped
        , ResponseFormat = WebMessageFormat.Json
        , RequestFormat = WebMessageFormat.Json
    )]
    IAsyncResult BeginGetUser(AsyncCallback callback, object state);
    Result<Data.User> EndGetUser(IAsyncResult asyncResult);

    [OperationContract(AsyncPattern = true)]
    [WebInvoke(
        Method = "POST"
         , UriTemplate = "/ListWithAttributes"
        , BodyStyle = WebMessageBodyStyle.Wrapped
        , ResponseFormat = WebMessageFormat.Json
        , RequestFormat = WebMessageFormat.Json
    )]
    IAsyncResult BeginListWithAttributes(int index, int pageSize, AsyncCallback callback, object state);
    Result<PagedCollection<Data.Attribute>> EndListWithAttributes(IAsyncResult asyncResult);

这是服务中的一种实现示例。

    public IAsyncResult BeginGetUser(AsyncCallback callback, object state)
    {
        var asyncResult = new CompletedAsyncResult<Result<Data.User>>(state);
        asyncResult.Result = new Result<Data.User>();

        asyncResult.Result.Value.UserId = Guid.Empty;
        asyncResult.Result.Value.DisplayName = "asdfasd";
        asyncResult.IsCompleted = true;           

        callback(asyncResult);

        return asyncResult;
    }

    public Result<Data.User> EndGetUser(IAsyncResult asyncResult)
    {
        return ((CompletedAsyncResult<Result<Data.User>>)asyncResult).Result;
    }

这是我们在服务实现类上的属性。

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]

谁能提供一些关于为什么这些同步执行以及我需要做什么的见解,或者至少指出我需要做什么的方向,以使这些异步执行?

更新

我接受了 Matt 的一些回答,并将我的逻辑转移到了 End 函数调用,并关注了这篇博文,了解他是如何更紧密地做到这一点的 Uploading Large Files To Self Hosted WCF Rest Service。但是,我无法使用他的技术调用 End 方法。我也开始认为我正在以错误的方式解决这个问题。因为查看日志,我的自定义身份验证服务在每次服务调用之前执行,这发生在任何异步方法操作甚至触发之前。经过进一步调查,我查看了进入 IIS 并执行操作的每个请求的 ThreadId。看来 WCF 只使用 1 个工作线程...期间。我一次向 IIS 发送多少请求并不重要。例如,如果我发送 3 个请求,我可以看到它们都在不同的时间(彼此相隔几毫秒)进入并且都有自己的线程。但是看起来 WCF 只是将它们全部排队并按顺序执行它们,因为它们都在同一个线程上执行,包括身份验证服务调用。

【问题讨论】:

  • 您似乎没有在该方法中执行任何异步工作。运行同步操作并将其结果放入 IAsyncResult 不会使方法异步(除非该代码不代表您的实现)。
  • 我确定我只是没有完全理解它是如何工作的。我给出的例子是我试图让它发挥作用。我正在/正在关注有关如何设置异步调用的博客文章。我同意运行同步操作并且做我所做的不会使它异步,我想我不知道为什么它们首先要同步运行。你能详细说明一下吗?如果需要,我可以提供更多信息。

标签: c# .net wcf rest asynchronous


【解决方案1】:

在我看来,从您的示例来看,您好像在“开始”调用完成之前完成了所有工作;这是我在学习异步模式时发现的一个常见错误。

虽然我不熟悉它在 WCF 中的应用程序,但 Async 模型更典型的是一个 Begin 方法,它启动一些带有 IAsyncResult 对象的新线程,该对象将由该新线程更新。为了“完成”该动作,当IsCompleted 设置为true 时,原始调用者应将原始IAsyncResult 对象传递回相应的End 方法,该方法返回结果。一个简单的实现如下所示:

    static Func<string> getUser;
    public static IAsyncResult BeginGetUser(AsyncCallback callback, object state)
    {
        getUser = () =>
            {
                Thread.Sleep(2000);
                return "finished";
            };
        return getUser.BeginInvoke(callback, state);
    }

    public static string EndGetUser(IAsyncResult asyncResult)
    {
        return getUser.EndInvoke(asyncResult);
    }

对它的调用可能如下所示:

var result = BeginGetUser(null, null);
string value = EndGetUser(result);

当然,这是一个小例子:引用 http://kennyw.com/work/indigo/258,“如果你没有做一些‘原生异步’的事情,那么你不应该使用 AsyncPattern=true”。

幸运的是,随着 C# 5.0 或微软发布的 Async CTP,.Net 异步模式可能成为过去。

【讨论】:

  • 抱歉,已经有一段时间了,我只是回到一些尚未修复的代码。然而,这对我来说很有意义,因为我们是通过浏览器中的 ajax 调用它们,所以我们不能单独调用 Begin/End 方法。
  • @phreak3eb 因为你只需要用OperationContract(Attribute)-attribute 修饰begin 方法,只有GetUser-method 会被发布。这里的主要内容是AsyncPattern = true 主要是为了对托管过程感兴趣。它将帮助 IIS(或任何其他托管进程)释放处理请求的线程以服务任何其他请求。一旦动作完成,宿主进程将获取另一个线程来结束整个循环。
【解决方案2】:

为了使事物异步,您通常将其与其他异步事物组合在一起。如果你只是用你的异步方法做同步的事情,那么使用异步模式没有任何意义。例如,如果您的所有代码都将工作放在线程池上,那么您还没有完成任何事情,因为您已经通过异步移动代码将线程交还给线程池,但通过启动您的在那里工作。

如果您正在调用数据库来获取用户,并且您的数据库支持异步操作,您可能会构建如下内容:

public IAsyncResult BeginGetUser(AsyncCallback callback, object state)
{
  var taskFunc = Task<DbReader>.Factory.FromAsync(db.BeginGetUser, db.EndGetUser);
  return taskFunc.ContinueWith(task => {
    var reader = task.Result;
    reader.Read();
    return new Data.User {
      DisplayName = reader["displayName"] as string,
      UserId = Guid.Parse(reader["userId"] as string),
    }
   }
  );
}

public Result<Data.User> EndGetUser(IAsyncResult asyncResult)
{
  return (Task<User>)(asyncResult).Result;
}

让我强调一点:异步编程很棘手。使用 Task 会变得更容易一些,但您仍然需要考虑延续。调试是一件苦差事,也是一个错误的举动,您的代码就消失了,您不知道为什么。如果你错过了一个异常,它将出现在终结器线程上,你不一定知道为什么。此外,为了进行异步编程,您必须小心翼翼地进行多线程编程,这充满了危险。

在尝试异步之前确保您真正了解正在发生的事情。如果您的代码本质上是同步的,那么将其设为异步并不会为您带来太多好处除非 这是一个长期运行的过程,您可以启动另一个线程来处理它。人们使用异步处理程序,以便 IIS(或任何底层 Web 服务器)可以将其线程返回以服务其他请求;正如我之前提到的,如果您只是从线程池中取出一个线程,那么您就是从 IIS 中窃取该线程,并且您不会看到可伸缩性方面的任何收益。但是,如果您只是疯狂地创建新线程,您就会面临拒绝服务攻击。

【讨论】:

  • 问题来自 IIS,排队并同步运行。如果我通过 javascript 进行 10 次调用,IIS 会在它们自己的线程上单独获取每个请求。但是,即使每个服务/方法调用不同,WCF 也会同步处理它们。包括在任何 Begin/End 异步模式方法调用之前访问授权管理器。我希望传入的每个请求都会获得自己的线程并并行处理,从而使总响应时间约为 200 毫秒,但是,事实并非如此,响应时间是累加的。
  • 您的代码示例不起作用。返回值(taskFunc.ContinueWith as IAsyncResult)不保留“AsyncCallback 回调”和“对象状态”。因此,WCF 框架永远不会调用 EndGetUser,客户端将收到超时异常。请根据此线程修复您的答案:social.msdn.microsoft.com/Forums/en-CA/parallelextensions/…
猜你喜欢
  • 1970-01-01
  • 2014-06-01
  • 1970-01-01
  • 2011-12-20
  • 1970-01-01
  • 2018-07-27
  • 2013-03-10
  • 2011-09-24
  • 2011-07-20
相关资源
最近更新 更多