Beste Sprache für sehr schnelle HTTP requests (jedes ms zählt)?

shoa66

Lt. Junior Grade
Registriert
Mai 2005
Beiträge
314
Guten Morgen zusammen,

ich möchte eine CLI App entwickeln, die via HTTP GET request eine Website scraped und dann, sollten bestimmte Dinge gefunden werden, via POST request Daten an eine REST API schickt.

Zu bemerken sei, dass ich an dieser Stelle nicht der einzige bin, der das so macht, sondern hundert wenn nicht sogar tausend Konkurrenten habe - also zählt wirklich jedes ms Latenz/Rechenzeit. Einen VPS mit <= 1 ms zum entsprechenden Edge Server habe ich schon.

Dazu habe ich zunächst eine NodeJS App geschrieben. Allerdings fiel mir nach einigen Rückschlagen und näherer Betrachtung auf, dass trotz HTTPS keepalive ("forever") HTTP requests recht lange brauchen.

Also habe ich als nächstes GO Lang probiert und festgestellt, dass damit die Zeiten auch nicht wirklich besser sind (auf eine Samplesize von 100 requests statistisch genormt ist es fast identisch, leicht zugunsten von Go Lang). Hier mal ein paar Werte zum Vergleich:

bild1.jpg

Nun das Merkwürdige: Ebenfalls auf 100 requests genormt ist die Chrome Konsole, wie man auch anhand einiger weniger Versuche schon fast sehen kann, reproduzierbar 150-250 ms schneller:

bild2.jpg

Nun frage ich mich: Warum? Leider ist die Chrome Konsole allein schon wegen den CORS policies bei requests zu anderen Websites und der fehlenden/umständlichen Möglichkeit zur Einbindung von Libraries natürlich eine suboptimale Runtime Umgebung. Natürlich könnte ich die Daten via Websocket aus der Konsole an NodeJS exportieren, aber das wäre auch ein wenig "Ghetto".

Viel mehr würde mich interessieren warum die Chrome Konsole hier schneller als NodeJS und Go Lang sein kann und welche Sprache da evtl. noch mithalten könnte? C++?

Bin auf Ideen und Anregungen gespannt!

Besten Dank vorab!
 
Zuletzt bearbeitet:
Also entweder machst du da etwas falsch, die Seite ist sehr groß oder der Server antwortet sehr lahm. Keine Programmiersprache oder Framework sind so langsam das einfache GET requests bis zu einer Sekunden dauern.

Wie groß ist die Response von dem Server? Was sagt das Chrome/Firefox network tab in den Developer Tools wenn Du diesen request im Browser machsts? Das Problem ist nicht die Programmiersprache zu diesem Zeitpunkt, sondern etwas anderes.
 
  • Gefällt mir
Reaktionen: Hayda Ministral
shoa66 schrieb:
Viel mehr würde mich interessieren warum die Chrome Konsole hier schneller als NodeJS und Go Lang sein kann und welche Sprache da evtl. noch mithalten könnte? C++?
Also, wen es wirklich auf jede MS ankommt, dann ist NodeJS oder Go vermutlich nicht die beste weg dafür (u.a. wegen GC).

Am schnellsten wäre es wohl, wenn du direkt über die OS-Api (in Windows in C++ z.B. via C++/WinRT, oder Rust/C) gehst oder direkt das Betriebssystem weglässt.

Aber dem kann man trotzdem erstmal zustimmen:
Dalek schrieb:
Also entweder machst du da etwas falsch, die Seite ist sehr groß oder der Server antwortet sehr lahm. Keine Programmiersprache oder Framework sind so langsam das einfache GET requests bis zu einer Sekunden dauern.
 
Kann es sein, dass der Chrome einfach die Seite im Cache hat und dein Scraper jedes Mal die Seite vollständig lädt?

Vielleicht siehst du dir mal Scrapy für Python an, das ist ein Framework für scraping und mit requests kannst du auch deine POSTs absetzen.

Aber das ist nur eine Idee, aus einem Kopf ohne Kaffee :D
 
Dalek schrieb:
Also entweder machst du da etwas falsch, die Seite ist sehr groß oder der Server antwortet sehr lahm. Keine Programmiersprache oder Framework sind so langsam das einfache GET requests bis zu einer Sekunden dauern.

Wie groß ist die Response von dem Server? Was sagt das Chrome/Firefox network tab in den Developer Tools wenn Du diesen request im Browser machsts? Das Problem ist nicht die Programmiersprache zu diesem Zeitpunkt, sondern etwas anderes.

Denke nicht, dass ich da was falsch mache, der Server ist nicht der schnellste. Normal greife ich die Daten eh via JSON direkt ab, aber in manchen Fällen muss man leider die ganze HTML Seite holen. Normaler Request der HTML Seite im Browser:

bild3.png


Telmur schrieb:
Kann es sein, dass der Chrome einfach die Seite im Cache hat und dein Scraper jedes Mal die Seite vollständig lädt?

Vielleicht siehst du dir mal Scrapy für Python an, das ist ein Framework für scraping und mit requests kannst du auch deine POSTs absetzen.

Aber das ist nur eine Idee, aus einem Kopf ohne Kaffee :D

Gute Idee, ist aber tatsächlich auch so, wenn man die verschiedene Seiten lädt, statt laufende die selbe Seite. Ist also kein Cache Issue I guess.

Danke, Scrapy werd ich mir mal angucken.

new Account() schrieb:
Am schnellsten wäre es wohl, wenn du direkt über die OS-Api (in Windows in C++ z.B. via C++/WinRT, oder Rust/C) gehst oder direkt das Betriebssystem weglässt

Interessant, man lernt nie aus, Danke.

Denke aber auch, dass sich auf anderem Wege noch deutlich mehr einsparen lassen muss, bevor man dann solche Wege zum Optimieren geht.
 
Ein rein auf Geschwindigkeit getrimmtes Programm schreibst du auf jeden Fall in C/C++. Dort hast du einfach die beste Kontrolle über deine Ressourcen und kannst gezielt dann z.B. Speicherbereiche freigeben, wenn gerade kein anderer Task ansteht.
Allerdings muss ich meinen Vorrednern hier absolut beipflichten, das Problem scheint ein anderes zu sein. Ohne die Menge der gelieferten Daten zu kennen, kann man hier aber auch nur mutmaßen.
Ergänzung ()

OK, in deinem letzten Screenshot sehen wir ja die Größe der Antwort: 13,8KB. Meiner Meinung nach dauern alle Anfragen, egal ob von deinem VPS oder deiner Chrome-Konsole aus viel zu lang. Ggfs. gibt es hier auch ein Problem auf der Gegenseite.
 
Vielleicht solltest du, um allen zu helfen, mal die Seite posten von der die Daten geladen werden sollen.
 
  • Gefällt mir
Reaktionen: burglar225
Was ist genau das Ziel? Wieso scrapst du, und welche Seiten?
 
https://stackoverflow.com/a/47944496
Code:
$ curl-time "https://bitskins.com/view_item?item_id=17760016000BSL1831126655&app_id=730"

===  TIME BREAKDOWN:  ==========
             lookup:  0.055990
            connect:  0.086715
         appconnect:  0.163990
        pretransfer:  0.164057
           redirect:  0.000000
      starttransfer:  1.160244
                      ----------
              total:  1.161830
# https://netbeez.net/blog/http-transaction-timing-breakdown-with-curl/
        lookup: The time, in seconds, it took from the start until the name
                resolving was completed.
       connect: The time, in seconds, it took from the start until the TCP
                connect to the remote host (or proxy) was completed.
    appconnect: The time, in seconds, it took from the start until the
                SSL/SSH/etc connect/handshake to the remote host was completed.
                (Added in 7.19.0)
   pretransfer: The time, in seconds, it took from the start until the file
                transfer was just about to begin. This includes all pre-transfer
                commands and negotiations that are specific to the particular
                protocol(s) involved.
      redirect: The time, in seconds, it took for all redirection steps include
                name lookup, connect, pretransfer and transfer before the final
                transaction was started. time_redirect shows the complete
                execution time for multiple redirections. (Added in 7.12.3)
 starttransfer: The time, in seconds, it took from the start until the first
                byte was just about to be transferred. This includes
                time_pretransfer and also the time the server needed to
                calculate the result.
         total: The total time, in seconds, that the full operation lasted. The
                time will be displayed with millisecond resolution.

Chrome ist nicht schneller

1583572467829.png


Jedenfalls ist deine Jagd nach den letzten MS sinnlos. Die Zeit wird nämlich immer durch den Server limitiert.

Auch ein Apache Bench zeigt nix Anderes an.
Code:
$ ab -n 100 -c 1 "https://bitskins.com/view_item?item_id=17760016000BSL1831126655&app_id=730"
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking bitskins.com (be patient).....done


Server Software:        cloudflare
Server Hostname:        bitskins.com
Server Port:            443
SSL/TLS Protocol:       TLSv1.2,ECDHE-ECDSA-CHACHA20-POLY1305,256,256
TLS Server Name:        bitskins.com

Document Path:          /view_item?item_id=17760016000BSL1831126655&app_id=730
Document Length:        46595 bytes

Concurrency Level:      1
Time taken for tests:   111.827 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      5009200 bytes
HTML transferred:       4659500 bytes
Requests per second:    0.89 [#/sec] (mean)
Time per request:       1118.267 [ms] (mean)
Time per request:       1118.267 [ms] (mean, across all concurrent requests)
Transfer rate:          43.74 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       78   95  11.1     97     151
Processing:   677 1023 197.0    970    1628
Waiting:      674 1010 195.5    964    1603
Total:        762 1118 196.7   1066    1713

Percentage of the requests served within a certain time (ms)
  50%   1066
  66%   1166
  75%   1216
  80%   1261
  90%   1455
  95%   1525
  98%   1632
  99%   1713
 100%   1713 (longest request)
Und explizit mit Keep Alive:
Code:
$ ab -k -n 100 -c 1 "https://bitskins.com/view_item?item_id=17760016000BSL1831126655&app_id=730"
This is ApacheBench, Version 2.3 <$Revision: 1807734 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking bitskins.com (be patient).....done


Server Software:        cloudflare
Server Hostname:        bitskins.com
Server Port:            443
SSL/TLS Protocol:       TLSv1.2,ECDHE-ECDSA-CHACHA20-POLY1305,256,256
TLS Server Name:        bitskins.com

Document Path:          /view_item?item_id=17760016000BSL1831126655&app_id=730
Document Length:        46595 bytes

Concurrency Level:      1
Time taken for tests:   102.600 seconds
Complete requests:      100
Failed requests:        0
Keep-Alive requests:    0
Total transferred:      5009200 bytes
HTML transferred:       4659500 bytes
Requests per second:    0.97 [#/sec] (mean)
Time per request:       1026.004 [ms] (mean)
Time per request:       1026.004 [ms] (mean, across all concurrent requests)
Transfer rate:          47.68 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       78   94   9.5     96     117
Processing:   589  932 154.3    928    1443
Waiting:      557  917 153.2    919    1413
Total:        703 1026 155.0   1017    1554

Percentage of the requests served within a certain time (ms)
  50%   1017
  66%   1065
  75%   1109
  80%   1132
  90%   1220
  95%   1306
  98%   1472
  99%   1554
 100%   1554 (longest request)
 
  • Gefällt mir
Reaktionen: shoa66
Yuuri schrieb:
Jedenfalls ist deine Jagd nach den letzten MS sinnlos. Die Zeit wird nämlich immer durch den Server limitiert.
?
Die Zeit wird immer von allen beteiligten Geräten und von jeglicher beteiligten Software limitiert (Stichwort Critical Path).
Wenn der Server 1S benötigt zur Antwort, und seine Request 1 ms benötigt bis zur Ankunft während die Requests von anderen 20 ms benötigen (höhere Netzwerklatenz + OS-Layer + Interpreter + GC-Delay), dann ist er trotzdem 19 ms schneller beim Server (und entsprechend schneller hat er auch seine Antwort zurück).
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: shoa66
Benutz doch einfach curl oder wget. Viel leichtgewichtiger wird's beim selbst schreiben auch nicht. Die Ausgabe kannst du dann ja irgendwohin pipen und weiterverarbeiten.
 
  • Gefällt mir
Reaktionen: nerdalicious
@new Account() Wenn du davon ausgehst, dass die Antwort immer exakt eine Sekunde benötigt, ja.

Es ist aber vollkommen egal 2 ms nachzuhaschen, während der Server für eine Antwort eben jene eine, schwankende Sekunde braucht. Es ist auch vollkommen egal welche Sprache verwendet wird, denn alle greifen auf die API vom OS zurück. Ein Curl verbringt keine anderen Wunder wie ein fsockopen() in PHP, ein net.socket.connect() in Node, ein connect() in C oder oder oder... Du kannst auch deinen Server direkt neben jenen Angefragten stellen und trotzdem benötigt der Request eine Sekunde. Du kannst ihn auch mit 10 GBit verbinden und trotzdem benötigt er eine Sekunde.

Insofern die Berechnung auf dem Server nicht manipuliert werden kann (anderer Content-Type, bestimmte Flags, API, ...), muss diese eine Sekunde gefressen werden. Natürlich kann man hier und da ggf. ne Millisekunde rausholen, aber auf den gesamten Request betrachtet optimierst du eben nur jene maximal einstelligen Prozentwerte. Weiterhin benötigt der Server für die eine Seite eben nicht immer eine Sekunde, sondern es schwankt um 200 ms. Was bringt da eine Optimierung um ggf. 10 ms von deiner Seite aus? Ein Request mehr auf der Seite in jenem Moment deines Requests und deine Optimierung ist bereits dahin, weil der andere Request ggf. höher priorisiert wird.

Hier nochmal von nem Server bei Hetzner in Falkenstein:
Code:
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       19   23   2.8     22      38
Processing:   625  969 222.5    937    1727
Waiting:      624  967 222.4    933    1721
Total:        646  991 222.1    962    1747

Percentage of the requests served within a certain time (ms)
  50%    962
  66%   1033
  75%   1105
  80%   1137
  90%   1327
  95%   1476
  98%   1604
  99%   1747
 100%   1747 (longest request)

Und gern nochmal von nem 1&1 Server vServer:
Code:
Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       19   24   4.4     22      47
Processing:   595  935 205.1    903    1602
Waiting:      594  934 205.0    900    1601
Total:        616  959 205.4    933    1623

Percentage of the requests served within a certain time (ms)
  50%    933
  66%    998
  75%   1071
  80%   1114
  90%   1219
  95%   1424
  98%   1494
  99%   1623
 100%   1623 (longest request)

Die 0,7 s pro Request von Chrome lasse ich nicht gelten, insofern hier nicht die reinen Requests offen gelegt werden. Diese eine Seite, die ich jetzt bestimmt fast 1000x angefragt habe, hat nun immer im Schnitt eine Sekunde benötigt und schwankt um bis zu 200 ms pro Request.

Jeder hier kann selbst rechnen, was optimistische 10 ms Optimierung bei 200 ms Schwankung bedeuten oder gar 1000 ms pro Request... Und da der TE auch mit Informationen geizt... Wo will man hier auch nur ansatzweise helfen?

Weiterhin steht noch die Frage nach seinem Algorithmus offen. Was bringen ihm 2 ms im Request, wenn seine folgenden Schritte 100 ms aufgrund von einer langsamen Programmiersprache benötigen oder gar sein Algorithmus einfach Mist ist? Führt er eben nen Regex aus, der ggf. 10 ms benötigt, während ein strpos() nur eine Millisekunde benötigen würde.

Profiling ist eben nicht trivial...
 
  • Gefällt mir
Reaktionen: shoa66
Yuuri schrieb:
@new Account() Wenn du davon ausgehst, dass die Antwort immer exakt eine Sekunde benötigt, ja.
Nein auch, wenn Varianz drin ist.
Zeit, die man einspart, indem man z.B. den Delay durch das OS weg lässt, spart man immer ein egal, ob Requests 1ms Varianz oder 1s Varianz haben.

Yuuri schrieb:
Du kannst auch deinen Server direkt neben jenen Angefragten stellen und trotzdem benötigt der Request eine Sekunde. Du kannst ihn auch mit 10 GBit verbinden und trotzdem benötigt er eine Sekunde.
Ja, die Berechnungszeit durch den Server stellt die absolute untere Grenze dar. Heißt aber nicht, dass man nicht versuchen kann sich möglichst dieser Berechnungszeit anzunähern.

Yuuri schrieb:
Jeder hier kann selbst rechnen, was optimistische 10 ms Optimierung bei 200 ms Schwankung bedeuten oder gar 1000 ms pro Request...
Im Mittel einen 10 ms Vorteil gegenüber der unoptimierten Variante. Eventuell DEN Vorteil gegenüber dem Konkurrenten. In gewissen Geschäftsfeldern wird so auch nur auf Bruchteile von Millisekunden optimiert: https://www.ip-insider.de/ultra-low-latency-routen-im-hochfrequenzhandel-a-369853/

Yuuri schrieb:
Weiterhin steht noch die Frage nach seinem Algorithmus offen. Was bringen ihm 2 ms im Request, wenn seine folgenden Schritte 100 ms aufgrund von einer langsamen Programmiersprache benötigen oder gar sein Algorithmus einfach Mist ist? Führt er eben nen Regex aus, der ggf. 10 ms benötigt, während ein strpos() nur eine Millisekunde benötigen würde.
Deswegen habe ich C++ und Rust vorgeschlagen - anstatt einer Interpreter-basierten Sprache oder einer Sprache mit GC.
 
  • Gefällt mir
Reaktionen: shoa66
Mal ein rechtlicher Einwand, dass massenweise Abfragen/Scrapen kann als Angriff und/oder Urheberrechtsverletzung gewertet werden.

Wenn die Latenz eine so große Rolle spielt, sollte man als erstes den Anbieter nach einer Schnittstelle fragen, die die gewünschten Daten direkt anbietet oder vielleicht gar per Push ausliefert. Ebenso sollte man schauen, ob man sinnvoll HTTP/2 einsetzen kann. Nicht zuletzt dann der alte Trick einfach einen Sever zu mieten, der möglichst im selben Rechenzentrum steht.
Edit: Hatten wir eigentlich schon das Thema, dass man Netzwerk und Logik auf Threads verteilen sollte?
 
  • Gefällt mir
Reaktionen: nerdalicious
@shoa66 Was ist genau dein Ziel? Du wirst genau 0 Konkurrenz schlagen, wenn du nicht eine massiv parallele Anwendung schreibst. Du wirst nur enttäuscht, nutze ein Tool. Ich verstehe nicht was dein Businessmodell sein soll. Du redest ja von Konkurrenz...
 
HTTPS-Zertifikatprüfungen abschalten, DNS abschalten (fest konfigurieren ... /etc/hosts)

HTTPS ist bei dem Ganzen die größte Bremse. Ohne wird es jedoch nicht funktionieren, da zumindest der Server https://bitskins.com/ immer ein 301-Forward auf TLS macht anstatt den Content auszugeben, wenn man nur via HTTP anfragt.

Performante Sprachen für das Vorhaben wären in der Reihenfolge: C, C++, Python. Würde damit direkt über Sockets arbeiten, falls möglich.

Parallelisierung ist das Stichwort des Tages. 10, 20, 50 Threads gleichzeitig laufen lassen.
Da das Ganze über Cloudflare läuft - und das ja wohl nicht zum Spaß - ist dann auch schnell Schluss mit Lustig und deine IP wird einfach weggeblockt.

Du wirst dann also nach der ganzen Optimierung künstliche Verzögerungen einbauen müssen oder ein Distributed Computing Service über die halbe Welt aufbauen und die Parallelisierung über viele verschiedene Exit Nodes erreichen.
 
  • Gefällt mir
Reaktionen: shoa66
Testa2014 schrieb:
Vielleicht solltest du, um allen zu helfen, mal die Seite posten von der die Daten geladen werden sollen.
nerdalicious schrieb:
Was ist genau das Ziel? Wieso scrapst du, und welche Seiten?
Es geht offensichtlich darum, CS:GO Skins auf https://bitskins.com/ zu kaufen. Vermutlich will man die "Deals" abgreifen und danach teurer verkaufen. Wenn man sich die älteren Beiträge des Threaderstellers anschaut, ist er auch schon seit Oktober damit beschäftigt..

nerdalicious schrieb:
@shoa66 Was ist genau dein Ziel? Du wirst genau 0 Konkurrenz schlagen, wenn du nicht eine massiv parallele Anwendung schreibst. Du wirst nur enttäuscht, nutze ein Tool. Ich verstehe nicht was dein Businessmodell sein soll. Du redest ja von Konkurrenz...
Parallelisierung wird nicht funktionieren, weil es ja immer nur um einzelne Skins geht, die man kaufen will.
 
stwe schrieb:
Es geht offensichtlich darum, CS:GO Skins auf https://bitskins.com/ zu kaufen.
Oh man, son Müll kaufen die Menschen? Naja, zum Glück sind Spiele fast Spurenlos an mir vorbeigezogen 😅
stwe schrieb:
danach teurer verkaufen
Meinst du wie das Desinfektionsmittel aktuell? Sinnhaftigkeit dahin gestellt. Das ein Mensch so etwas moralisch vertreten kann ...
 
Zurück
Oben