测试发送Web请求的不同方法的吞吐量,因此我创建了一个WebRequester类,它使用BeginGetResponse异步发送请求,并实现BeginGetResponse和BeginRead的回调。从我的调用代码循环发送多个请求时,如果我重用相同的HttpWebRequest对象,我会收到以下异常:
"请求已中止:连接意外关闭。"
如果我在调用代码For循环的每次迭代期间创建一个新的HttpWebRequest对象,则异常永远不会发生。
问题是,在每次迭代时创建一个新的HttpWebRequest会有相当显着的性能下降。这里发生了什么?
编辑:忘记提及一些事情。使用Visual Studio 2017,.NET 4.5.2,在端口8888上运行的Fiddler和在端口1337上启动的NodeJS Express测试服务器。Imports System.Net
Imports System.Threading
Public Class Form1
Private Sub Test() Handles Button1.Click
'Test using HttpWebRequest.BeginGetResponse
ServicePointManager.DefaultConnectionLimit = 2
Dim iterations as integer = 1000
Dim completedCount As Integer = 0
Dim lock As New Object
Dim mre As New ManualResetEvent(False)
Dim wr As New WebRequester
AddHandler wr.ResponseReceived, Sub(reqState As RequestState)
SyncLock lock
completedCount += 1
If completedCount = iterations Then
mre.Set()
End If
End SyncLock
End Sub
'If I reuse the HttpWebRequest object below in my loop, I receive "The request was aborted: The connection was closed unexpectedly." :(
'Dim req As HttpWebRequest = HttpWebRequest.Create("http://localhost.:1337/")
'req.Proxy = New WebProxy("127.0.0.1", 8888)
For i = 0 To iterations - 1
Dim req As HttpWebRequest = HttpWebRequest.Create("http://localhost.:1337/")
req.Proxy = New WebProxy("127.0.0.1", 8888)
wr.SendRequest(req, 30000)
Next
mre.WaitOne()
End Sub
End Class
Imports System.Net
Public Class RequestState
Private bufferSize As Integer = 1024
Public Property Request As HttpWebRequest
Public Property Response As HttpWebResponse
Public Property ResponseBody As String
Public Property ResponseStream As IO.Stream
Public Property ResponseData As Text.StringBuilder
Public Property ReadBuffer As Byte()
Public Sub New()
ReadBuffer = New Byte(bufferSize) {}
ResponseData = New System.Text.StringBuilder()
End Sub
End Class
Imports System.Net
Public Class WebRequester
'Adapted from https://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.begingetresponse(v=vs.110).aspx?cs-save-lang=1&cs-lang=vb#code-snippet-2
Private mre As New Threading.ManualResetEvent(False)
Public Event ResponseReceived(reqState As RequestState)
Public Event ExceptionRaised(ex As Exception)
Public Sub SendRequest(req As HttpWebRequest, timeOut As Integer)
Try
Dim reqState As New RequestState
reqState.Request = req
Dim result As IAsyncResult = reqState.Request.BeginGetResponse(New AsyncCallback(AddressOf RespCallback), reqState)
Threading.ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, New Threading.WaitOrTimerCallback(AddressOf TimeoutCallback), reqState.Request, timeOut, True)
mre.WaitOne()
reqState.Response.Close()
Catch ex As Exception
RaiseEvent ExceptionRaised(ex)
End Try
End Sub
Private Sub RespCallback(asyncResult As IAsyncResult)
Try
Dim reqState As RequestState = asyncResult.AsyncState
reqState.Response = reqState.Request.EndGetResponse(asyncResult)
reqState.ResponseStream = reqState.Response.GetResponseStream
reqState.ResponseStream.BeginRead(reqState.ReadBuffer, 0, 1024, New AsyncCallback(AddressOf ReadCallback), reqState)
Catch ex As Exception
RaiseEvent ExceptionRaised(ex)
End Try
mre.Set()
End Sub
Private Sub ReadCallback(asyncResult As IAsyncResult)
Try
Dim reqState As RequestState = asyncResult.AsyncState
'Read the buffer, if there is more data, recursively call responseStream.BeginRead
Dim read As Integer = reqState.ResponseStream.EndRead(asyncResult)
If read > 0 Then
reqState.ResponseData.Append(Text.Encoding.ASCII.GetString(reqState.ReadBuffer, 0, read))
Dim ar As IAsyncResult = reqState.ResponseStream.BeginRead(reqState.ReadBuffer, 0, 1024, New AsyncCallback(AddressOf ReadCallback), reqState)
Return
Else
'Request complete!
reqState.ResponseBody = reqState.ResponseData.ToString
reqState.ResponseStream.Close()
reqState.ResponseStream.Dispose()
RaiseEvent ResponseReceived(reqState)
End If
Catch ex As Exception
RaiseEvent ExceptionRaised(ex)
End Try
mre.Set()
End Sub
Private Shared Sub TimeoutCallback(state As Object, timedOut As Boolean)
If timedOut Then
Dim request As HttpWebRequest = state
If Not (request Is Nothing) Then
request.Abort()
End If
End If
End Sub 'TimeoutCallback
End Class
为了回应可能存在的问题,除了我们都在重用HttpWebRequest对象之外,我们的情况似乎有所不同,足以保证一个新问题。从技术上讲,我甚至不知道是否可以重用HttpWebRequest,我真的在问我为什么这样做,我收到了我发布的例外。