Set socket-timeout in Ruby über SO_RCVTIMEO socket-option
Ich versuche, mich sockets timeout in Ruby über die SO_RCVTIMEO socket-option, aber es scheint keinen Einfluss auf die aktuelle *nix Betriebssystem.
Mithilfe von Ruby ' s Timeout-Modul ist keine option, da es erfordert die Laich-und Fügeverfahren und-threads für jedes timeout, das kann teuer werden. In Anwendungen, die eine geringe socket-timeouts und die eine hohe Anzahl von threads, die es im wesentlichen tötet Leistung. Dies wurde festgestellt in vielen Orten, einschließlich - Stack Overflow.
Ich habe gelesen, Mike Perham ' s ausgezeichneten Beitrag zum Thema hier und zu einer Reduzierung des Problems auf eine Datei ausführbarer code erstellt ein einfaches Beispiel für einen TCP-server, der eine Anfrage erhalten, warten Sie die Zeit geschickt, in der Wunsch und dann die Verbindung schließen.
Erstellt der client einen socket, legt der receive-timeout auf 1 Sekunde, und dann eine Verbindung zum server herstellt. Der client teilt dem server mit, um die Sitzung schließen, nach 5 Sekunden wartet dann auf Daten.
Sollte der client timeout nach einer Sekunde sondern erfolgreich schließt die Verbindung nach 5.
#!/usr/bin/env ruby
require 'socket'
def timeout
sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
# Timeout set to 1 second
timeval = [1, 0].pack("l_2")
sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, timeval
# Connect and tell the server to wait 5 seconds
sock.connect(Socket.pack_sockaddr_in(1234, '127.0.0.1'))
sock.write("5\n")
# Wait for data to be sent back
begin
result = sock.recvfrom(1024)
puts "session closed"
rescue Errno::EAGAIN
puts "timed out!"
end
end
Thread.new do
server = TCPServer.new(nil, 1234)
while (session = server.accept)
request = session.gets
sleep request.to_i
session.close
end
end
timeout
Habe ich versucht, das gleiche zu tun, mit ein TCPSocket (die automatisch eine Verbindung her) und habe ähnliche code in redis und andere Projekte.
Außerdem kann ich überprüfen, ob die option gesetzt wurde, durch den Aufruf getsockopt
wie diese:
sock.getsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO).inspect
Nicht festlegen, an die dieser socket-option tatsächlich für jedermann?
- Diese Art von Frage schon gepostet hat, bevor, und wie es scheint, die beste Antwort war der Einsatz von Ruby ' s
timeout
Bibliothek rund um dierecv
nennen. - Nein, mit der timeout-Bibliothek threads erstellt, so dass jedes timeout benötigt die Schaffung und die Zerstörung eines thread, das kann sehr teuer werden. Ich update meine Antwort zu reflektieren, dass es nicht effizient ist.
- Sieben Jahre später, haben wir auch gelernt, dass es nicht nur ineffizient, sondern auch gefährlich.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Können Sie dies tun, effizient mit
select
von Ruby ' s IO-Klasse.IO::select
nimmt 4 Parameter. Die ersten drei Reihen von sockets zu überwachen und der Letzte ist ein timeout (in Sekunden angegeben).Den Weg wählen funktioniert, ist, dass es macht die Listen der IO-Objekte bereit, die für einen bestimmten Vorgang blockiert, bis mindestens einer von Ihnen ist bereit, die entweder gelesen werden, geschrieben, oder will, um einen Fehler auszulösen.
Die ersten drei Argumente daher entsprechen die verschiedenen Arten der Staaten zu überwachen.
Vierten ist das timeout, das Sie einstellen wollen (wenn überhaupt). Wir nutzen dieses Parameters.
Wählen Sie gibt ein array mit arrays von IO-objects (sockets in diesem Fall), die als bereit, die vom Betriebssystem für die jeweilige Aktion überwacht.
Also der Rückgabewert von select wird wie folgt Aussehen:
Jedoch, wählen Sie zurück
nil
wenn die optionalen timeout-Wert angegeben ist und kein IO-Objekt bereit ist, innerhalb von timeout Sekunden.Also, wenn Sie wollen, um performant IO-timeouts in Ruby und vermeiden, dass das Timeout-Modul, können Sie Folgendes tun:
Lass uns ein Beispiel erstellen, wo wir warten
timeout
Sekunden für einen read aufsocket
:Dies hat den Vorteil, dass es nicht dreht einen neuen thread für jede Zeitüberschreitung (wie in der Timeout-Modul) und multi-Thread-Anwendungen mit vielen timeouts viel schneller in Ruby.
Ich glaube, du bist im Grunde Pech gehabt. Wenn ich dein Beispiel mit
strace
(nur mit einem externen server zu halten die Ausgabe sauber), es ist leicht zu überprüfen, dasssetsockopt
ist in der Tat immer genannt:strace
zeigt auch, was blockiert das Programm. Dies ist die Linie, die ich auf dem Bildschirm sehen, bevor der server mal aus:Das bedeutet, dass das Programm blockiert, auf diesen Anruf zu
ppoll
, nicht auf einen Anruf zurecvfrom
. Die man-page, die Listen-socket-Optionen (socket(7)) besagt, dass:Also die timeout-Einstellung hat aber keine Wirkung. Ich hoffe, ich bin hier falsch, aber es scheint, gibt es keine Möglichkeit, dieses Verhalten zu ändern Ruby. Ich warf einen kurzen Blick auf die Implementierung und nicht finden, eine offensichtliche Art und Weise aus. Wieder, ich hoffe, ich bin falsch -- dies scheint etwas grundlegendes, wie kommt es, das es nicht gibt?
Einem (sehr hässlichen) workaround ist die Verwendung
dl
zu nennenread
oderrecvfrom
direkt. Diese Aufrufe sind betroffen durch den timeout festlegen. Zum Beispiel:Dieser code funktioniert hier. Natürlich: es ist eine hässliche Lösung, die nicht funktioniert auf vielen Plattformen, etc. Es kann ein Weg heraus, obwohl.
Beachten Sie bitte auch, dass dies das erste mal, dass ich etwas tun, ähnlich wie in Ruby, also ich bin mir nicht bewusst, alle Fallstricke, die ich möglicherweise mit Blick auf-in allem, bin ich vermute, die Typen, die ich angegeben
'long read(int, void *, long)'
und der Art und Weise, die ich bin übergeben einen Puffer zu Lesen.Basierend auf meinen Tests, und Jesse Storimer ausgezeichnete ebook zu "Arbeiten mit TCP-Sockets" (in Ruby), den timeout für socket-Optionen nicht arbeiten in Ruby 1.9 (und ich vermute, 2.0 und 2.1). Jesse sagt:
Wow. Ich denke, die moral von der Geschichte ist, zu vergessen, über diese Möglichkeiten und verwenden
IO.select
oder Tony Arcieri ist NIO-Bibliothek.