当一个ASP.Net网站使用AJAX名为REST WCF服务锁螺纹 [英] REST WCF service locks thread when called using AJAX in an ASP.Net site

查看:113
本文介绍了当一个ASP.Net网站使用AJAX名为REST WCF服务锁螺纹的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有消耗在ASP.Net网站WCF REST服务,从页面,使用AJAX。

我希望能够调用从我的服务异步,这意味着我将有回调的句柄,我的JavaScript code,当方法完成,输出将被更新的方法。该方法应该在不同的线程中运行,因为每个方法将采取不同的时间来完成他们的任务。

我有code半的工作,但一些奇怪的事情正在发生,因为我第一次执行code 编译后,它的工作原理运行各种通话一个不同的线程的但随后调用集团的服务,以这样的方式,每个方法调用必须等待,直到最后一次调用,以便执行下一个结束。他们都在同一个线程中运行。我有当我使用的页方法的前同样的问题,我通过禁用会话页面解决了,但我还没有想通了如何消费WCF REST服务时<做同样的/ P>

注:方法完整的时间(运行它们异步应仅需 7秒,其结果应该是: Execute1 - Execute3 - Execute2

  • Execute1 - > 2秒
  • Execute2 - > 7秒
  • Execute3 - > 4秒

输出编译之后

输出后续调用(这是问题)

我会后的code ...我会尽量简化它,就像我可以

服务合同

  [的ServiceContract(
    SessionMode = SessionMode.NotAllowed
)]
公共接口IMyService
{
    //我还有其他3种方法这样的:Execute2和Execute3
    [OperationContract的]
    [WebInvoke(
        RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json,
        UriTemplate =/ Execute1
        方法=邮报)
    字符串Execute1(字符串参数);
}
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)
[ServiceBehavior(
    InstanceContextMode = InstanceContextMode.PerCall
)]
公共类为MyService:IMyService
{
    //我还有其他3种方法这样的:Execute2(7秒)和Execute3(4秒)
    公共字符串Execute1(字符串参数)
    {
        VAR T = Observable.Start(()=&GT; Thread.sleep代码(2000),Scheduler.NewThread);
        t.First();

        返回的String.Format(Execute1上:{0}计数:{1}在:{2}主题:{3},参数,0,DateTime.Now.ToString(),Thread.CurrentThread.ManagedThreadId.ToString ());
    }
 }
 

ASPX页面

 &LT;%@页面的EnableSessionState =FALSE标题=主页LANGUAGE =C#的MasterPageFile =〜/ Site.master母AutoEventWireup =真
    codeBehind =Default.aspx.cs继承=RestService._Default%&GT;
&LT; ASP:内容ID =HeaderContent=服务器ContentPlaceHolderID =HeadContent&GT;
    &LT;脚本类型=文/ JavaScript的&GT;
        功能callMethodAsync(URL数据){
            $(#信息)追加(&LT; BR /&gt;中+新的日期());
            $阿贾克斯({
                缓存:假的,
                键入:POST,
                异步:真正的,
                网址:网址,
                数据:德,
                的contentType:应用/ JSON
                数据类型:JSON,
                成功:函数(MSG){
                    $(#信息)追加(&LT; BR /&GT;&安培; NBSP;&安培; NBSP;&安培; NBSP;+味精)。
                },
                错误:函数(XHR){
                    警报(xhr.responseText);
                }
            });
        }
        $(函数(){
            $(#callMany)。点击(函数(){
                $(#信息)HTML()。
                callMethodAsync(/ Execute1,你好);
                callMethodAsync(/ Execute2,疯狂);
                callMethodAsync(/ Execute3,世界);
            });
        });
    &LT; / SCRIPT&GT;
&LT; / ASP:内容&GT;
&LT; ASP:内容ID =的BodyContent=服务器ContentPlaceHolderID =日程地址搜索Maincontent&GT;
    &LT;输入类型=按钮ID =callMany值=后,许多/&GT;
    &LT; D​​IV ID =消息&GT;
    &LT; / DIV&GT;
&LT; / ASP:内容&GT;
 

的Web.config(相关)

 &LT; system.webServer&GT;
  &LT;模块runAllManagedModulesForAllRequests =真/&GT;
&LT; /system.webServer>
  &LT; system.serviceModel&GT;
    &LT; serviceHostingEnvironment aspNetCompatibilityEnabled =真multipleSiteBindingsEnabled =真/&GT;
    &LT; standardEndpoints&GT;
      &LT; webHttpEndpoint&GT;
        &LT; standardEndpoint NAME =helpEnabled =真正的automaticFormatSelectionEnabled =真/&GT;
      &LT; / webHttpEndpoint&GT;
    &LT; / standardEndpoints&GT;
  &LT; /system.serviceModel>
 

的Global.asax

 无效的Application_Start(对象发件人,EventArgs的)
    {
        RouteTable.Routes.Ignore({}资源个.axd / {* PATHINFO});
        RouteTable.Routes.Add(新ServiceRoute(,
          新WebServiceHostFactory(),
          typeof运算(为MyService)));
    }
 

编辑1

我尝试了好几种组合,但结果是一样的,我在测试使用Visual Studio,但现在我正在测试的IIS 7,这是同样的结果。

我曾尝试下列属性的组合:

  [ServiceBehavior(
    InstanceContextMode = InstanceContextMode.PerCall,
    ConcurrencyMode = ConcurrencyMode.Multiple
)]
 

此外,我删除了使用接收的,现在我只是模拟像这种长期的过程操作:

 的Thread.Sleep(2000);
 

但结果是相同的...的编译之后,和部署,(第一个呼叫)的服务工作正常,则在不同的线程得到期望结果来执行,但后续的呼叫在相同的运行线程....我不明白这一点

我刚刚发现了一些,第一次编制工作后,和最后一个线程中使用总是在后续调用中使用的线程,该线程被阻塞,这就像如果其他线程间没有'牛逼处置或某事,或者最后一个线程被封锁由于某种原因,

编辑2

这是这个项目(RestWCF.zip)的全code

http://sdrv.ms/P9wW6D

解决方案

我下载源$ C ​​$ C做我自己的一些测试。我设法得到它通过添加这web.config中工作:

 &LT;的sessionState模式=关&GT;&LT; /的sessionState&GT;
 

这个问题似乎涉及到Asp.Net会话处理(似乎是从WCF会话不同)。

如果没有这个,提琴手表明WCF服务响应发送 ASP.NET_SessionId 的cookie。您水平的EnableSessionState =FALSE不影响服务。

修改:我做了一些更多的测试,看看我是否能得到它,而无需关闭会话状态为整个应用程序的工作。我现在的工作。我所做的是:

  • 在应用程序的根目录下创建一个新文件夹ServiceFolder
  • 移动服务接口和实现该文件夹

然后在的Global.asax 我改变了 ServiceRoute 注册:

  RouteTable.Routes.Add(新ServiceRoute(ServiceFolder /,
    新WebServiceHostFactory(),
    typeof运算(为MyService)));
 

在Default.aspx的我变了:

  $(函数(){
    $(#callMany)。点击(函数(){
        $(#信息)HTML()。
        callMethodAsync('&LT;%= this.ResolveUrl(〜/ ServiceFolder / Execute1)%&GT;,你好);
        callMethodAsync('&LT;%= this.ResolveUrl(〜/ ServiceFolder / Execute2)%&GT;,疯狂​​);
        callMethodAsync('&LT;%= this.ResolveUrl(〜/ ServiceFolder / Execute3)%&GT;,世界);
    });
});
 

然后,为了控制cookie处理,我创建了一个 HTTP模块

 使用系统;
使用的System.Web;

命名空间RestService
{
    公共类测试preventCookie:IHttpModule的
    {
        公共无效的Dispose()
        {
        }
        公共无效的Ini​​t(HttpApplication的应用程序)
        {
            application.BeginRequest + =
                (新的EventHandler(this.Application_BeginRequest));
            application.PostAcquireRequestState + =
                (新的EventHandler(this.Application_PostAcquireRequestState));

        }
        私人无效的Application_BeginRequest(对象源,EventArgs五)
        {
            //到达服务prevent会话cookie
            HttpApplication的应用=(HttpApplication的)来源;
            HttpContext的背景下= application.Context;
            如果(context.Request.Path.StartsWith(/ ServiceFolder))
            {
                context.Request.Cookies.Remove(ASP.NET_SessionId);
            }
        }
        私人无效Application_PostAcquireRequestState(对象源,EventArgs五)
        {
            HttpApplication的应用=(HttpApplication的)来源;
            HttpContext的背景下= application.Context;
            如果(context.Request.Path.StartsWith(/ ServiceFolder))
            {
                VAR S = context.Session;
                如果(S!= NULL)
                    s.Abandon();
            }
        }
    }
}
 

然后我注册在web.config中的模块:

 &LT;的HttpModules&GT;
    &LT;添加名称=测试preventCookieTYPE =RestService.Test preventCookie/&GT;
&LT; / HttpModules的&GT;
 

最后,我改变Default.aspx的,以允许会话:

 的EnableSessionState =真
 

这些更改后,服务调用的parallell执行工作。强制性免责声明:

  

它工作在我的机器上

我们的想法是,当调用服务的进来,该HTTP模块检查URL,必要时,将删除 ASP.NET_SessionId 饼干所以它不牛逼到达服务。然后在 Application_PostAcquireRequestState ,我们就立刻放弃创建的会话,所以我们不要不必要地消耗服务器的内存进行了大量的空会话。

通过该解决方案,您可以:

  • 服务电话Parallell执行
  • 您仍然可以使用会话在应用程序的其他部分

目前的成本:

  • 但是,你的服务必须被要求可识别的网址在不需要会话cookie
  • 服务器将创建并放弃了很多会议

I have a WCF REST service consumed in an ASP.Net site, from a page, using AJAX.

I want to be able to call methods from my service async, which means I will have callback handlers in my javascript code and when the methods finish, the output will be updated. The methods should run in different threads, because each method will take different time to complete their task

I have the code semi-working, but something strange is happening because the first time I execute the code after compiling, it works, running each call in a different threads but subsequent calls blocs the service, in such a way that each method call has to wait until the last call ends in order to execute the next one. And they are running on the same thread. I have had the same problem before when I was using Page Methods, and I solved it by disabling the session in the page but I have not figured it out how to do the same when consuming WCF REST services

Note: Methods complete time (running them async should take only 7 sec and the result should be: Execute1 - Execute3 - Execute2)

  • Execute1 --> 2 sec
  • Execute2 --> 7 sec
  • Execute3 --> 4 sec

Output After compiling

Output subsequent calls (this is the problem)

I will post the code...I'll try to simplify it as much as I can

Service Contract

[ServiceContract(
    SessionMode = SessionMode.NotAllowed
)]
public interface IMyService
{
    // I have other 3 methods like these: Execute2 and Execute3
    [OperationContract]
    [WebInvoke(
        RequestFormat = WebMessageFormat.Json,
        ResponseFormat = WebMessageFormat.Json,
        UriTemplate = "/Execute1",
        Method = "POST")]
    string Execute1(string param);
}
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(
    InstanceContextMode = InstanceContextMode.PerCall
)]
public class MyService : IMyService
{
    // I have other 3 methods like these: Execute2 (7 sec) and Execute3(4 sec)
    public string Execute1(string param)
    {
        var t = Observable.Start(() => Thread.Sleep(2000), Scheduler.NewThread);
        t.First();

        return string.Format("Execute1 on: {0} count: {1} at: {2} thread: {3}", param, "0", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId.ToString());
    }
 }

ASPX page

<%@ Page EnableSessionState="False" Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
    CodeBehind="Default.aspx.cs" Inherits="RestService._Default" %>
<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
    <script type="text/javascript">
        function callMethodAsync(url, data) {
            $("#message").append("<br/>" + new Date());
            $.ajax({
                cache: false,
                type: "POST",
                async: true,
                url: url,
                data: '"de"',
                contentType: "application/json",
                dataType: "json",
                success: function (msg) {
                    $("#message").append("<br/>&nbsp;&nbsp;&nbsp;" + msg);
                },
                error: function (xhr) {
                    alert(xhr.responseText);
                }
            });
        }
        $(function () {
            $("#callMany").click(function () {
                $("#message").html("");
                callMethodAsync("/Execute1", "hello");
                callMethodAsync("/Execute2", "crazy");
                callMethodAsync("/Execute3", "world");
            });
        });
    </script>
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <input type="button" id="callMany" value="Post Many" />
    <div id="message">
    </div>
</asp:Content>

Web.config (relevant)

<system.webServer>
  <modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
  <system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
    <standardEndpoints>
      <webHttpEndpoint>
        <standardEndpoint name="" helpEnabled="true" automaticFormatSelectionEnabled="true" />
      </webHttpEndpoint>
    </standardEndpoints>
  </system.serviceModel>

Global.asax

    void Application_Start(object sender, EventArgs e)
    {
        RouteTable.Routes.Ignore("{resource}.axd/{*pathInfo}");
        RouteTable.Routes.Add(new ServiceRoute("", 
          new WebServiceHostFactory(), 
          typeof(MyService)));
    }

Edit 1

I have tried several combinations but the result is the same, I was testing using Visual Studio but now I am testing on IIS 7, and it's the same result

I have tried combinations of the following properties:

[ServiceBehavior(
    InstanceContextMode = InstanceContextMode.PerCall,
    ConcurrencyMode = ConcurrencyMode.Multiple
)]

Also I removed the use of Rx, now I am just simulating long-process operations like this:

Thread.Sleep(2000);

But the result is the same.... After compiling, and deploying, (the first call) the service works correctly, it is executed on different threads giving the desired result, but subsequent calls run on the same thread.... I do not get it

I just noticed something, the first time after compiling works, and the last thread used is always the thread used on subsequent calls, and this thread is blocked, it's like if the other threads weren't disposed or something or if the last thread were blocked for some reason

Edit 2

This is the full code of this project (RestWCF.zip)

http://sdrv.ms/P9wW6D

解决方案

I downloaded your source code to do some testing on my own. I managed to get it to work by adding this to the web.config:

<sessionState mode="Off"></sessionState>

The problem seem to be related to the Asp.Net session handling (seems to be different from WCF session).

Without this, Fiddler shows that the WCF service response is sending an ASP.NET_SessionId cookie. You Page level EnableSessionState="False" does not affect the service.

EDIT: I did some more testing to see if I could get it to work without having to turn off session state for the whole application. I got it working now. What I did was:

  • Create a new folder "ServiceFolder" under the root of the application
  • Moved the service interface and implementation to that folder

Then in Global.asax I changed the ServiceRoute registration:

RouteTable.Routes.Add(new ServiceRoute("ServiceFolder/",
    new WebServiceHostFactory(),
    typeof(MyService)));

In Default.aspx I changed:

$(function () {
    $("#callMany").click(function () {
        $("#message").html("");
        callMethodAsync('<%=this.ResolveUrl("~/ServiceFolder/Execute1") %>', "hello");
        callMethodAsync('<%=this.ResolveUrl("~/ServiceFolder/Execute2") %>', "crazy");
        callMethodAsync('<%=this.ResolveUrl("~/ServiceFolder/Execute3") %>', "world");
    });
});

Then, in order to control the cookie handling, I created an HttpModule:

using System;
using System.Web;

namespace RestService
{
    public class TestPreventCookie : IHttpModule
    {
        public void Dispose()
        {
        }
        public void Init(HttpApplication application)
        {
            application.BeginRequest +=
                (new EventHandler(this.Application_BeginRequest));
            application.PostAcquireRequestState +=
                (new EventHandler(this.Application_PostAcquireRequestState));

        }
        private void Application_BeginRequest(Object source, EventArgs e)
        {
            //prevent session cookie from reaching the service
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;
            if (context.Request.Path.StartsWith("/ServiceFolder"))
            {
                context.Request.Cookies.Remove("ASP.NET_SessionId");
            }
        }
        private void Application_PostAcquireRequestState(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;
            if (context.Request.Path.StartsWith("/ServiceFolder"))
            {
                var s = context.Session;
                if (s != null)
                    s.Abandon();
            }
        }
    }
}

And then I registered the module in web.config:

<httpModules>
    <add name="TestPreventCookie" type="RestService.TestPreventCookie" />
</httpModules>

Finally, I change Default.aspx to allow session:

EnableSessionState="True"

After these changes, the parallell execution of the service call works. Obligatory disclaimer:

It works on my machine

The idea is that when the calls to the service are coming in, the HttpModule inspects the URL and, when necessary, removes the ASP.NET_SessionId cookie so it doesn't reach the service. And then in the Application_PostAcquireRequestState we immediately abandon the created session so we don't unnecessarily consume server memory for a lot of empty sessions.

With this solution, you get:

  • Parallell execution of service calls
  • You can still use Session in other parts of your application

At the cost of:

  • But you service must be called on a recognizable URL where session cookies are not needed
  • The server will create and abandon a lot of sessions

这篇关于当一个ASP.Net网站使用AJAX名为REST WCF服务锁螺纹的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆