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,只有在请求结束后才释放。因此,如果您想让请求异步工作,则需要使请求无会话。或者您可以使用不同的方法,例如后台作业或处理程序,但它们也不会使用会话。
我发现 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
:
<!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,只有在请求结束后才释放。因此,如果您想让请求异步工作,则需要使请求无会话。或者您可以使用不同的方法,例如后台作业或处理程序,但它们也不会使用会话。