IIS在单线程中同步执行自定义错误处理器

IIS executes custom error handler synchronously in a single thread

我发现 IIS 在单个线程中同步执行定义为自定义错误处理程序的经典 ASP 脚本。 这很慢

这是我的设置:

我有 index.asp IIS Express 10.0 服务。 index.asp 有 3 个图像元素 (<img src="..">),它们带有断开的链接,使 IIS 为每个标记调用自定义 400 错误处理程序。

处理程序在 applicationhost.config 中定义为

<httpErrors lockAttributes="allowAbsolutePathsWhenDelegated,defaultPath" errorMode="Custom" >
   <error statusCode="404" path="/handler.asp" responseMode="ExecuteURL" />

handler.asp 将其开始和结束时间记录为

Log2File "start"
call main
Log2File "end"

三个电话的时间如下:

3/10/2017 3:16:03 AM|start|http://localhost:8081/xENOjODFFUSZWDD
10-3-2017 3:16:12|end|http://localhost:8081/xENOjODFFUSZWDD

3/10/2017 3:16:12 AM|start|http://localhost:8081/pWEzIB25374ynkwv
10-3-2017 3:16:20|end|http://localhost:8081/pWEzIB25374ynkwv

3/10/2017 3:16:20 AM|start|http://localhost:8081/pMeODA30827gurud
10-3-2017 3:16:29|end|http://localhost:8081/pMeODA30827gurud

我们可以看到处理程序的执行是顺序的:第一个停止在 3:16:12 第二个以 3:16:12 开头,依此类推

我去TraceLogFiles文件夹看看什么时候请求不存在的文件,可能一个接一个?

请求次数如下:

http://localhost:8081/pWEzIB25374ynkwv
<TimeCreated SystemTime="2017-03-10T01:16:03.586Z"/>

http://localhost:8081/pMeODA30827gurud
<TimeCreated SystemTime="2017-03-10T01:16:03.580Z"/>

http://localhost:8081/xENOjODFFUSZWDD
<TimeCreated SystemTime="2017-03-10T01:16:03.586Z"/>

我们可以看到所有请求几乎同时出现。

所以,我得出一个结论,请求被放入一个队列中,一个接一个地处理。没有创建 3 个线程来处理 3 个并发请求。 这个单线程顺序执行它们。

所以,我的问题是:

如何让 IIS 并行执行自定义错误处理程序?

更新

我做了一个小测试。我在 index.asp:

上放置了 5 个 src 损坏的 iframe
<!DOCTYPE html>
<html>
<body>
<iframe style="border: 2px solid red" width="1080" height="100" src="http://localhost:8081/none1"></iframe>
<iframe style="border: 2px solid red" width="1080" height="100" src="http://localhost:8081/none2"></iframe>
<iframe style="border: 2px solid red" width="1080" height="100" src="http://localhost:8081/node3"></iframe>
<iframe style="border: 2px solid red" width="1080" height="100" src="http://localhost:8081/none4"></iframe>
<iframe style="border: 2px solid red" width="1080" height="100" src="http://localhost:8081/none5"></iframe>
</body>
</html>

然后我用经典的asp来处理400错误:

    <%
    Dim http, url
    Set http =  Server.CreateObject("MSXML2.ServerXMLHTTP")
    url = "http://localhost:10000/"
    http.open "GET", url, False
    http.send()
    Response.Write( http.ResponseText )
    %>

在 localhost:10000 上运行一个响应延迟 10 秒的服务器:

const logger = require('log4js').getLogger();
const uid = require('uid');
let app = require('express')()
app.get('/', (req,res)=>{
  let id = uid();
  logger.debug(`${id}: got request`);
  setTimeout( function() {
    logger.debug(`${id}: response sent`);
    res.send(`${id}: All ok`);
  }, 10000);
});

console.log('Listening on 1000');
app.listen(10000);

Chrome 的网络控制台显示如下:

现在,我使用 asp.net 处理程序(我通过 Web.config 定义):

Imports System.Web
Imports System.Net
Imports System.IO


Public Class MySyncHandler
    Implements IHttpHandler

    Private Function GetText() As String
        Dim request As WebRequest = WebRequest.Create("http://localhost:10000/")
        Dim response As HttpWebResponse = CType( request.GetResponse(), HttpWebResponse)
        Console.WriteLine(Now() & "|Got status: " & response.StatusDescription)
        Dim dataStream As Stream = response.GetResponseStream()
        Dim reader As New StreamReader(dataStream)
        Dim responseFromServer As String = reader.ReadToEnd()
        reader.Close()
        dataStream.Close()
        response.Close()
        Return responseFromServer
    End Function

    Public Sub ProcessRequest(ByVal context As _
            System.Web.HttpContext) Implements _
            System.Web.IHttpHandler.ProcessRequest
        Dim request As HttpRequest = context.Request
        Dim response As HttpResponse = context.Response
        Dim data As String = GetText()
        response.Write("<!DOCTYPE html><html><head><meta charset='UTF-8'/></head>")
        response.Write("<body>")
        response.Write(data)
        response.Write("</body>")
        response.Write("</html>")
    End Sub

    Public ReadOnly Property IsReusable() As Boolean _
            Implements System.Web.IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property
End Class

网络控制台的图片看起来更好:

因此,问题出在经典 ASP 处理程序上。

有什么办法可以改善吗?

我会使用 URL 重写模块来完成这种任务。你能以某种方式将那些断开的链接描述为模式吗?如果没有,您可以简单地将 .* 作为模式并添加 2 个条件:

{REQUEST_FILENAME} 不是文件 {REQUEST_FILENAME} 不是目录

然后将操作更改为重写并将其用作重写 URL:

/handler.asp?路径={R:0}

其中 handler.asp 是您将处理那些丢失文件的文件。

在您的 handler.asp 中获取 path=request("path") 并按您喜欢的方式处理它。

自定义错误模块已经很多年没有更新了,我相信 URL 重写模块要新得多。它也是多线程的。

我认为您遇到问题是因为会话。 Session无论是asp还是asp.net都是同步的,在开始请求事件上是initialized/locked,只有在请求结束后才释放。因此,如果您想让请求异步工作,则需要使请求无会话。或者您可以使用不同的方法,例如后台作业或处理程序,但它们也不会使用会话。