Blocking DNS in HttpUtils_NonblockingGet

Begonnen von Markus M., 12 Mai 2016, 17:07:28

Vorheriges Thema - Nächstes Thema

Markus M.

Mir ist leider beim letzten Netzausfall wieder aufgefallen, dass die blockierenden DNS Abfragen in HttpUtils_NonblockingGet ein riesengrosses Problem sind.
Bei mir ist FHEM dank genügend Calls dann komplett blockiert.
Ungünstig ist, dass es in der aktuellen Implementierung nicht nur eine, sondern 2 Stellen sind die jeweils 10 Sekunden Timeout haben, was damit zu je 20 Sekunden Blockade je Aufruf führt.

Zitatif($hash->{callback}) { # Nonblocking staff
    $hash->{conn} = IO::Socket::INET->new(Proto=>'tcp', Blocking=>0);
    if($hash->{conn}) {
      my $iaddr = inet_aton($host);
      if(!defined($iaddr)) {
        my @addr = gethostbyname($host); # This is still blocking
        return "gethostbyname $host failed" if(!$addr[0]);
        $iaddr = $addr[4];
      }

Ich finde hier braucht es eine Idee um dieses Problem zu verhindern oder zumindest abzuschwächen.
Hat jemand vielleicht schon eine?

Spontane Einfälle:
  Check im Modul vor jedem Call (verbraucht aber auch 10 Sekunden) und dann hochsetzen der jeweiligen Timer
  Globaler DNS Status der aus den Modulen abgefragt wird
  Check in HttpUtils, evtl. mit interner Statusliste

Fällt jemandem etwas Sinnvolles ein?


Viele Grüsse,
Markus
FHEM dev + HomeBridge + Lenovo Flex15 + HM-CFG-USB + RFXtrx433 + Fritz!Box 7590/7580/546E

HM Aktor/Sensor/Winmatic/Keymatic/Thermostat, HUE, Netatmo Weather/Security/Heating, Xiaomi AirPurifier/Vacuum, Withings Aura/BPM/Cardio/Go/Pulse/Thermo, VSX828, Harmony, Siro ERB15LE
https://paypal.me/mm0

rudolfkoenig

Bist du sicher, dass inet_aton sich blocken kann? Die soll nur eine IP-Adresse erkennen.

Moeglichkeiten, die fuer gethostbyname mir einfallen:
- gethostbyname in BlockingCall aufrufen, evtl. nach dem ersten Blockieren (reicht das?)
- es gibt ein ADNS Bibliothek (weiss jemand mehr darueber?) und ein Perl Wrapper: http://search.cpan.org/~salva/Net-ADNS-0.03/lib/Net/ADNS.pm
- DNS query in perl selbst implementieren, kann nicht so schwer sein :)

Die Nonblocking-Alternative in FHEM muss zunaechst optional sein.

Markus M.

Zitat von: rudolfkoenig am 12 Mai 2016, 17:44:11
Bist du sicher, dass inet_aton sich blocken kann? Die soll nur eine IP-Adresse erkennen.

Moeglichkeiten, die fuer gethostbyname mir einfallen:
- gethostbyname in BlockingCall aufrufen, evtl. nach dem ersten Blockieren (reicht das?)
- es gibt ein ADNS Bibliothek (weiss jemand mehr darueber?) und ein Perl Wrapper: http://search.cpan.org/~salva/Net-ADNS-0.03/lib/Net/ADNS.pm
- DNS query in perl selbst implementieren, kann nicht so schwer sein :)

Die Nonblocking-Alternative in FHEM muss zunaechst optional sein.

inet_aton blockiert definitiv für 10 Sekunden, das passiert jetzt in meiner Fallback Lösung (leider je Device), siehe unten.
Im Prinzip müsste der Check global geschehen und weitere Versuche unterbinden bzw. den Modulen passendes Feedback geben - auch die Reduktion auf 10 Sekunden bringt nichts wenn die Module weiter blind Anfragen senden oder sie wie bei Twilight mehrfach wiederholen.
Meine nächste Idee wäre ein inet_aton mit variablem Timeout für Checks im Modul gewesen, ich will aber keine zusätzlichen Perl Libs.

Betroffen sind aktuell Twilight, Weather, Calendar, withings, netatmo, livetracking und allergy und wahrscheinlich noch weitere Module die ich bei mir nicht verwende.

Ich hab bei allergy (und allen anderen betroffenen Modulen) mal testweise folgendes eingebaut und rufe es vor jedem Call auf:
sub allergy_GetNetwork($) {
  my ($hash) = @_;
  my $name = $hash->{NAME};

  my $resolve = inet_aton("www.allergiedaten.url");
  if(!defined($resolve))
  {
    InternalTimer( gettimeofday() + 10800, "allergy_GetNetwork", $hash, 0);
    return undef;
  }

  allergy_GetUpdate($hash);
  return undef;
}


Das blockiert immer noch 10 Sekunden, allerdings dann nur alle paar Stunden. So ganz glücklich bin ich damit aber nicht.
Wenn ich bei zig Netatmo Geräten von 15 Minuten auf eine Stunde gehe, kommt eben immer noch einige Zeit zusammen in der FHEM blockiert ist.
Ohne diese Checks tut sich bei mir allerdings gar nichts mehr.

Gruss, Markus
FHEM dev + HomeBridge + Lenovo Flex15 + HM-CFG-USB + RFXtrx433 + Fritz!Box 7590/7580/546E

HM Aktor/Sensor/Winmatic/Keymatic/Thermostat, HUE, Netatmo Weather/Security/Heating, Xiaomi AirPurifier/Vacuum, Withings Aura/BPM/Cardio/Go/Pulse/Thermo, VSX828, Harmony, Siro ERB15LE
https://paypal.me/mm0

justme1968

#3
inet_aton konvertiert einen string in die interne binäre adresse. in der meisten dokumentation steht zwar das es die string repräsentation einer ip4 adresse (also xxx.yyy.zzz.abc) erwartet aber tatsächlich akzeptiert es auch ganz normale host namen wie localhost aber auch www.google.de. die namensauflösung geht dann über den konfigurierten resolver. kann also blockieren.

gethostbyname macht zum teil das gleiche, einer der haupt unterschiede ist das mehr als eine adresse zurück liefern kann. ansonsten kann der aufruf auch blockieren.

ich bin mir nicht ganz sicher warum überhaupt beides aufgerufen wird bzw. in welchem fall inet_aton fehl schlägt und dann gethostbyname funktioniert.


ich weiss nicht wie effizient es ist für jeden das call zu forken wie es mit BlockigCall der fall wäre.

ADNS schaut gut aus. ich habe es noch nicht verwendet. man müsste mal schauen was intern genau passiert. es ist eventuell effizienter als BlockingCall. der aufwand es einzubauen und zu verteile ist aber vermutlich höher.


vielleicht kann man auch umgekehrt vorgehen: die namensauflösung in einen wrapper zu stecken der erst mal alles macht wie bisher, aber nach dem ersten blockieren ein flag setzt und jede weiter das anfrage verwirft. gleichzeitig per BlockigCall im hintergrund prüfen bis es wieder geht und dann das flag zurück setzen.

damit wäre der overhead im normalfall recht klein und man müsste auch nicht alles intern umbauen damit es mit einer non-blocking routine zurecht kommt. dafür nimmt man aber ein ein maliges blockieren für 10 sekunden in kauf.


unabhängig von der non-blocking frage: wäre es nicht sinnvoll auf die neuen inet_pton und/oder getaddrinfo umzusteigen wenn hier etwas angefasst? gethostbyname und inet_aton sind beide glaube ich schon als veraltet eingestuft.
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

Markus M.

Zitat von: justme1968 am 12 Mai 2016, 18:43:45ich bin mir nicht ganz sicher warum überhaupt beides aufgerufen wird bzw. in welchem fall inet_aton fehl schlägt und dann gethostbyname funktioniert.
Mir fällt spontan keiner ein.

Zitatvielleicht kann man auch umgekehrt vorgehen: die namensauflösung in einen wrapper zu stecken der erst mal alles macht wie bisher, aber nach dem ersten blockieren ein flag setzt und jede weiter das anfrage verwirft. gleichzeitig per BlockigCall im hintergrund prüfen bis es wieder geht und dann das flag zurück setzen.
damit wäre der overhead im normalfall recht klein und man müsste auch nicht alles intern umbauen damit es mit einer non-blocking routine zurecht kommt. dafür nimmt man aber ein ein maliges blockieren für 10 sekunden in kauf.
Klingt gut und die 10 Sekunden wären ok.
Soll das dann je Hostname passieren? Es kann ja theoretisch auch mal vorkommen dass der Hostname aus einem Aufruf nicht aufgelöst werden kann, alle anderen aber funktionieren.
FHEM dev + HomeBridge + Lenovo Flex15 + HM-CFG-USB + RFXtrx433 + Fritz!Box 7590/7580/546E

HM Aktor/Sensor/Winmatic/Keymatic/Thermostat, HUE, Netatmo Weather/Security/Heating, Xiaomi AirPurifier/Vacuum, Withings Aura/BPM/Cardio/Go/Pulse/Thermo, VSX828, Harmony, Siro ERB15LE
https://paypal.me/mm0

rudolfkoenig

Zitatich bin mir nicht ganz sicher warum überhaupt beides aufgerufen wird
Ich schon: ich war bisher der Ansicht, dass inet_aton nur die String-Representation der 4-stelligen IP-Adresse zu der binaeren Variante konvertiert. Ich schau die Geschichte am Wochenende mal naeher an.

herrmannj

gerade über den thread gestolpert, war duch Zufall vergangene Woche mit genau dem Thema beschäftigt.

Das aton blocken kann überrascht mich auch gerade, da war ich definitiv auch der Meinung das der nicht und nur gethosbyname blockt. Wenn Rudi das prüft ist das top.

Für DNS habe ich auch nur ADNS gefunden (auch bei intensiver Suche), war aber  nicht so glücklich weil man wieder neue cpan module beim user vorraussetzt.

Ich habe kurz über eine eigene Implementierung nachgedacht. Das cpan Modul ist recht mächtig, macht hallt auch Zonentransfer, mmx und so weiter. Das brächte man ja für einen gethostbyname nicht. Das habe aber dann verworfen weil ich keinen guten Weg gefunden habe den defaullt System DNS einfach zu bekommen.

Schlßendlich habe ich das dann in einen fork gesteckt. Das ist aber auch nicht optimal.. Wenn halt mehrere Abfragen parallel benötigt werden, zum Beispiel bei Start, dann wird für jeden ein fork aufgemacht und wenn der DNS nicht da ist (darum gehts ja hauptsächlich) hat man plötzlich einige dutzend forks offen.

Bin auch noch nicht recht glücklich so, klinke mich sehr gern hier mit ein.

vg
joerg

justme1968

das aton nicht nur numerische ip adressen akzeptiert findet man sogar in der doku wenn man lange genug sucht. es fängt mit beispielen an in denen localhost verwendet wird und dann steht sogar was von fully qualified hostnamen drin.

lässt sich mit inet_aton('www.google.de') auch ganz einfach überprüfen.

genau wegen den vielen forks der vorschlag die logik umzudrehen. und nur im fehlerfall ein mal zu forken. damit ist zwar alles nicht wirklich non-blocking sondern man riskiert genau ein mal zu blockieren. aber eben nur für x sekunden und auch nur ein mal. und man muss nicht den ganzen code auch auf non-blockeng und callbacks umstellen.

gruss
  andre
hue, tradfri, alexa-fhem, homebridge-fhem, LightScene, readingsGroup, ...

https://github.com/sponsors/justme-1968

herrmannj

Na klar, wirst recht haben. In den docs wird es so stehen.

Den block der bei aton rauskommt müsste man doch für ip auch händisch bauen können. Schau ich mir heute abend mal an.

Vg
Joerg

rudolfkoenig

Ich habe HttpUtils.pm erweitert:
- Namensaufloesung fuer nonblocking nach HttpUtils_gethostbyname ausgelagert
- falls eine IPV4 Adresse spezifiziert ist, dann wird keine DNS-Routine aufgerufen, sondern pack()
- falls "attr global dnsServer" _nicht_ gesetzt ist dann wird gethostbyname aufgerufen, inet_aton ist entfernt.
- falls "attr global dnsServer" gesetzt ist (auf dem DNS-Server, am besten IP-Adresse), dann wird nicht blockierend via UDP nachgefragt, und das Ergebnis bis TTL gecached.

Ist natuerlich alles IPV4, der Rest der Routinen ist aber auch noch nicht IPV6 faehig.
Habs mit meinem fritz.box und mit 8.8.8.8 getestet, scheint zu tun.
dnsServer dokumentiere ich erst dann, wenn ich mehr Feedback kriege.

dev0

Augefallen ist mir bisher, dass CNAME Records bei gesetzten dnsServer Atrribut nicht berücksichtigt werden:
Error: DNS: Wrong answer for ...

Markus Bloch

Hallo zusammen,

ich würde dann mit eurem OK den Hinweis im Wiki zum Thema DNS-Auflösung bei HttpUtils_NonblockingGet() entfernen.

http://www.fhemwiki.de/wiki/HttpUtils#HttpUtils_NonblockingGet

Gruß
Markus
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

rudolfkoenig

@dev0: kannst du bitte in HttpUtils.pm die Zeile mit "DNS ANSWER" aktivieren, und die Ausgabe im Fehlerfall hier anhaengen?

@Markus: im Prinzip ja, ich moechte aber mehr Feedback haben (s.o.), bevor ich das als "verwendbar" deklariere.

Markus Bloch

Kein Thema. Ich beobachte das ganze eh mit ;-)

Gruß
Markus
Developer für Module: YAMAHA_AVR, YAMAHA_BD, FB_CALLMONITOR, FB_CALLLIST, PRESENCE, Pushsafer, LGTV_IP12, version

aktives Mitglied des FHEM e.V. (Technik)

herrmannj