Schnellerer WebRequest

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von BiedermannS.

    Schnellerer WebRequest

    Hallo Leute,
    ich Versuche gerade so viele WebRequests einen meinen Server wie möglich zu machen, der Code sieht so aus:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Dim postData = "..."
    2. Dim request As WebRequest = WebRequest.Create("...")
    3. request.Method = "POST"
    4. request.Proxy = Nothing
    5. Dim byteArray As Byte() = Encoding.UTF8.GetBytes(postData)
    6. request.ContentType = "application/x-www-form-urlencoded"
    7. request.ContentLength = byteArray.Length
    8. Dim dataStream As Stream = request.GetRequestStream()
    9. dataStream.Write(byteArray, 0, byteArray.Length)
    10. dataStream.Close()
    11. Dim response As WebResponse = request.GetResponse()
    12. dataStream = response.GetResponseStream()
    13. Dim reader As New StreamReader(dataStream)
    14. Dim responseFromServer As String = reader.ReadToEnd()
    15. reader.Close()
    16. dataStream.Close()
    17. response.Close()
    18. Return responseFromServer


    Aber so bekomme ich maximal 5 Abfragen pro Sekunde hin, auch bei mehreren Threads (liegt wahrscheinlich an meiner Bandbreite). Kann man das irgendwie beschleunigen? Eventuell könnte man es so machen, dass das Skript nicht jedes mal auf eine Antwort wartet und dann weiter macht, sondern "pipelinet". So:

    Aber wenn ich zwei mal das Programm öffne ändert sich auch nichts, also denke ich nicht, dass das was ausmacht. Habt ihr noch Ideen?

    MfG Frank

    *Topic verschoben*

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    stackoverflow.com/questions/86…ection-limit-in-webclient

    Damit kannst evtl. schon was rausholen. Ansonsten kannst du den Teil vor der Abfrage evtl. schon vor bearbeiten, aber ich denke, dass dies dir kaum nen merkbaren Vorteil bringen sollte...
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    xd-franky-5 schrieb:

    maximal 5 Abfragen pro Sekunde
    Scheint mir für einen HTTP-POST-Request schon relativ gut zu sein.
    Beachte, dass auf Serverseite 'nur' ein Webserver läuft.

    Welchen Durchsatz hättest du gerne?
    Wie viele Requests willst du insgesamt absetzen?

    Hast du die Serverseite selbst im Griff?
    Kannst du dort anstelle eines Webservers einen Webservice bereitstellen?

    Ist der Server im lokalen Netz?
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    Bei deinem Code Beispiel geht es das Ganze asynchron zu starten:

    VB.NET-Quellcode

    1. webRequest.BeginGetResponse


    dazu den Beitrag Schnellerer WebRequest
    beachten und es sollte gehen.
    Dein Vorhaben hört sich aber schon nach DOS Attacke an :o
    Das ist meine Signatur und sie wird wunderbar sein!
    @petaod Wie gesagt, in mehreren Threads teilt sich das dann irgendwie und ich habe insgesamt trotzdem nur 5 Abfragen die Sekunde. Hm okay, also 10 wäre schon sehr praktisch, oder noch mehr. Insgesamt ca. 1.000.000. Und nein habe ich nicht, er ist auch nicht im lokal Netz.

    @MrTrebron Ein ganz normaler Webserver ist das.

    @Bluespide Http, geht das dort?

    @Mono Würde es was bringen, das Asynchron zu starten? Ich weiß nicht wie genau ich das mir vorstellen und zusammenbringen soll. Und nein ist keine DOS Attacke, für sowas bringen 10 Abfragen/Sekunde denke ich nichts, außer es ist eine "Slow Loris Attacke"

    petaod schrieb:

    Ich glaube, das Asynchrone Starten bringt nur was, wenn du danach in dem Thread noch irgendetwas sinnvolles tun kannst.
    Die HTTP-Session ist blockiert bis der Request abgearbeitet ist.
    Vorher kannst du keinen neuen Post schicken.


    Hm? wie meinst du das?
    Du läufst im Loop und erzeugst lauter neuer Webrequest Instanzen und startest den Request mit BeginGetResponse.
    Die Antworten holst dir im übergebenen AsyncCallback. Du kannst ja mehrere Http Sessions verwenden und nicht nur eine.
    Musst halt lediglich das Connection Limit hochsetzen.

    LG
    Das ist meine Signatur und sie wird wunderbar sein!

    Mono schrieb:

    Du kannst ja mehrere Http Sessions verwenden und nicht nur eine
    Klar.
    Dann funktioniert's natürlich.

    Wenn aber mehrere Sessions in parallelen Threads keine Performance bringen, verspreche ich mir von mehreren Sessions in Asynchron-Verarbeitung auch nicht so furchtbar viel.

    Ich wollte halt darauf hinweisen, dass eine Session nicht in der Lage ist, einen Request entgegenzunehmen, bevor der alte nicht abgearbeitet ist.
    Wenn, dann wird entweder der alte weggeworfen oder der Serverstate kommt durcheinander und es gibt einen 500-Error.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    Wenn man so viel wie möglich von etwas braucht, ist eine der einfachsten Architekturen eine fixe Anzahl an Threads zu erstellen welche sich aus einer Queue Arbeit abholen.

    Um die perfekte Anzahl der Threads herauszufinden, muss man eventuell etwas herumspielen. Das hängt nämlich von mehreren Sachen ab (z.B.: Was ausgeführt werden soll). In irgendeinem Whitepaper hab ich mal gelesen dass Anzahl der CPUs * 2 + 1 ein guter Wert für die maximale Anzahl an Threads sein soll, da sonst die Thread Congestion zu hoch wird. Aber ich denke dass die Anzahl der CPUs ein guter Ausgangswert für die maximale Anzahl an Threads sein sollte.

    Hier mal ne generische Beispiel Umsetzung:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class ManagerThread(Of TData, TResult)
    2. Private _DataQueue As New Queue(Of TData)
    3. Private _ResultList As New List(Of ThreadResult)
    4. Private _ThreadList As New List(Of WorkerThread)
    5. Public Shared MaxThreadCount As Integer = Environment.ProcessorCount
    6. Public Sub New(ByVal data As TData, ByRef transform As Func(Of TData, TResult), ByVal count As Integer)
    7. Me.New(
    8. Enumerable.Range(0, count).Select(Function(i) data).ToList(), ' use the same data, [count] times for a list
    9. transform
    10. )
    11. End Sub
    12. Public Sub New(ByVal dataList As List(Of TData), ByRef transform As Func(Of TData, TResult))
    13. For Each data As TData In dataList
    14. _DataQueue.Enqueue(data)
    15. Next
    16. For i As Integer = 0 To MaxThreadCount
    17. _ThreadList.Add(New WorkerThread(transform, AddressOf GetData, AddressOf SetData))
    18. Next
    19. End Sub
    20. Public Function GetResult() As List(Of ThreadResult)
    21. Return _ResultList
    22. End Function
    23. Public Sub WaitAll()
    24. For Each t In _ThreadList
    25. t.Join()
    26. Next
    27. End Sub
    28. Private Function GetData() As TData
    29. SyncLock (_DataQueue)
    30. Return _DataQueue.Dequeue()
    31. End SyncLock
    32. End Function
    33. Private Sub SetData(ByVal data As TData, ByVal result As TResult)
    34. SyncLock (_ResultList)
    35. _ResultList.Add(New ThreadResult(data, result))
    36. End SyncLock
    37. End Sub
    38. Public Class ThreadResult
    39. Public ReadOnly Data As TData
    40. Public ReadOnly Result As TResult
    41. Public Sub New(ByVal data As TData, ByVal result As TResult)
    42. Me.Data = data
    43. Me.Result = result
    44. End Sub
    45. End Class
    46. Private Class WorkerThread
    47. Private ReadOnly _GetNextWorkItem As Func(Of TData)
    48. Private ReadOnly _SetResult As Action(Of TData, TResult)
    49. Private ReadOnly _Transform As Func(Of TData, TResult)
    50. Private ReadOnly _Thread As System.Threading.Thread
    51. Private Sub Execute()
    52. Try
    53. While (True)
    54. Dim workItem As TData = _GetNextWorkItem()
    55. _SetResult(workItem, _Transform(workItem))
    56. End While
    57. Catch ex As Exception
    58. End Try
    59. End Sub
    60. Public Sub New(ByRef transform As Func(Of TData, TResult), ByRef getNextWorkItem As Func(Of TData), ByRef setResult As Action(Of TData, TResult))
    61. _Transform = transform
    62. _GetNextWorkItem = getNextWorkItem
    63. _SetResult = setResult
    64. _Thread = New System.Threading.Thread(AddressOf Execute)
    65. _Thread.Start()
    66. End Sub
    67. Public Sub Join()
    68. _Thread.Join()
    69. End Sub
    70. End Class
    71. End Class


    Und benutzen kannst du das Ganze so:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Const REQUEST_COUNT = 1000
    2. Class TestData
    3. Public ReadOnly Url As Uri
    4. Public ReadOnly Data As String
    5. Public Sub New(url As Uri, data As String)
    6. Me.Data = data
    7. Me.Url = url
    8. End Sub
    9. End Class
    10. Function MakeRequest(ByVal data As TestData) As String
    11. Dim request As Net.WebRequest = Net.WebRequest.Create(data.Url)
    12. request.Method = "POST"
    13. request.Proxy = Nothing
    14. Dim byteArray As Byte() = Text.Encoding.UTF8.GetBytes(data.Data)
    15. request.ContentType = "application/x-www-form-urlencoded"
    16. request.ContentLength = byteArray.Length
    17. Dim dataStream As IO.Stream = request.GetRequestStream()
    18. dataStream.Write(byteArray, 0, byteArray.Length)
    19. dataStream.Close()
    20. Dim response As Net.WebResponse = request.GetResponse()
    21. dataStream = response.GetResponseStream()
    22. Dim reader As New IO.StreamReader(dataStream)
    23. Dim responseFromServer As String = reader.ReadToEnd()
    24. reader.Close()
    25. dataStream.Close()
    26. response.Close()
    27. Return responseFromServer
    28. End Function
    29. Sub Main()
    30. Dim url As Uri = New Uri("http://deine-url")
    31. Dim sw = Stopwatch.StartNew()
    32. Dim t As New ManagerThread(Of TestData, String)(New TestData(url, "somedata"), AddressOf MakeRequest, REQUEST_COUNT)
    33. t.WaitAll()
    34. sw.Stop()
    35. For Each result In t.GetResult
    36. Console.WriteLine(result.Result)
    37. Next
    38. Dim seconds = sw.ElapsedMilliseconds / 1000
    39. Console.WriteLine("{0} seconds per request", seconds / REQUEST_COUNT)
    40. Console.WriteLine("{0} requests per second", REQUEST_COUNT / seconds)
    41. Console.ReadLine()
    42. End Sub


    Der Code ist zwar nicht besonders optimiert, aber ich komm bei meinem Server damit auf bis zu 86 Requests pro Sekunde. Wenn dein Server also nicht die Anzahl der Sessions limitiert, solltest du damit auch mehr Requests als 5 hinbekommen ;)

    Wenn du noch mehr brauchst solltest du dir LockFree Data Structures und IO Completion Ports ansehen.
    Die LockFree Data Structures werden zwar nicht viel an Performance bringen, aber wenn man wirklich ans Maximum will, dann sollte man versuchen alle Locks loszuwerden.
    Die IO Completion Ports könnten nochmal einiges an Performance herausholen, da diese genau für solche Dinge ausgelegt sind.

    Wenn du einfach nur die Performance des Webservers Testen willst, dann kannst du auch einfach eines der Performance Testing Frameworks (z.B.: eines der Tools die auf serverfault.com/questions/2107…load-testing-http-servers erwähnt werden) verwenden, diese sind nämlich dahingehend optimiert, so viele Requests wie möglich zu machen.
    SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

    Weil einfach, einfach zu einfach ist! :D