piwo2 Posted August 29, 2019 at 04:14 PM Share Posted August 29, 2019 at 04:14 PM hallo ! a) soweit ich der doku und den sourcen von "tinkerforge_mqtt" entnehme, ist es bereits jetzt möglich -> register/ip_connection/disconnected und -> register/ip_connection/connected vorzunehmen ... -> callback/ip_connection/connected wird am anfang aber explizit nie aufgerufen : stattdessen ist mit dem initialisierungs-file implizit die einzige möglichkeit gegeben, callbacks zu registrieren bzw. ein anfängliches enumerate anzustossen ... -> callback/ip_connection/disconnected ist aber z.zt. ebenfalls "seltsam" : soweit ich sehe, wird dieser callback nur getriggert, falls ein "regulärer" ausstieg via KeyboardInterrupt erfolgt. alle anderen umstände haben "seltsame" dinge zur folge : (PROBLEM ? BUG? falsch verstanden ?) wenn ich eine zwischenzeitliche ip-netzwerk-unterbrechung zum brickd simuliere, scheint der disconnect probe thread dies nicht zu erkennen ! selbst wenn ich abseits der zu erwartenden callbacks auch funktionen via -> request/.../get_identity o.ä. absetze, erhalte ich -> response/.../get_identity { '_ERROR' : "Did not receive response for function BLAH in time ..." } ... aber es erfolgt keinerlei triggern eines -> callback/ip_connection/disconnected !!! b) dieses momentane design macht es ausserordentlich mühsam, ein scheitern eines kompletten "stacks" aus erwarteten uid's konsistent zu managen : jeden topic auf { ..., "_ERROR" : ... } zu prüfen IST mühsam ! es könnte auch einen weiterer "höher" aggregierter topic z.b. -> callback/bindings/issue { "type" : ... } eingebaut werden, wo dann von aussen entschieden werden kann, ob es sich um ein potentiell temporäres problem der ip_connection handelt und das wartens auf ein reconnect sinnvoll ist oder eine - permanent fatale - exception auftrat ... aufgrund dieser info kann man noch immer entscheiden, ob man mittels z.b. -> request/bindings/terminate ein normales mqtt-disconnect durchführt oder über -> request/bindings/abort und den proxy ohne disconnect mit exit(1) beendet ... wenn -> callback/ip_connection/connected & -> callback/ip_connection/disconnected arbeiten wie es sein sollte (d.h. von anfang an und der disconnect prober funkt !!!), dann kann eine restartlogik über -> request/bindings/reset_callbacks & -> request/ip_connection/enumerate auch mühelos eingebaut werden ... wie gesagt : es müsste aber auch ein -> request/bindings/terminate beinhalten, wo durch einen sofortigen "normalen" disconnect vom mqtt-proxy ein benutzerdefinierter "last will" verhindert wird oder über -> request/bindings/abort mit exit(1) so wie bei fatalen fehlern den proxy einfach beendet und den "last will" triggert ... vorteile : sämtliche exceptions die NICHT AUSSCHLIESSLICH connection-bezogen sind und wo man (MIT TIMEOUT ;-) !!!) auf einen reconnect warten kann & sollte, aber auch fatale exceptions werden durch - eventuell von aussen angesteuerte - abnormale termination & benutzerdefinierten last will "von aussen" erkennbar ... --- ersuche um gedanken & ev. andere lösungsvorschläge ziel ist : sinnvolle behandlung von connection-issues auf höher aggregierter topic-ebene, die eine sinnvolle restartlogik erlauben oder eben ein GIVEUP und : der proxy soll ja auch für eine generelle integration tauglich sein, und nicht nur für fire & forget & warten auf godot ... Quote Link to comment Share on other sites More sharing options...
rtrbt Posted September 4, 2019 at 02:15 PM Share Posted September 4, 2019 at 02:15 PM Moin, Ich habe für die nächste Version der MQTT-Bindings mal einiges gefixt: -> callback/ip_connection/connected wird am anfang aber explizit nie aufgerufen : Das stimmt. Das Problem war, dass zuerst die IP-Connection aufgebaut wurde, danach die Verbindung zum Broker. Das passiert jetzt andersrum. stattdessen ist mit dem initialisierungs-file implizit die einzige möglichkeit gegeben, callbacks zu registrieren bzw. ein anfängliches enumerate anzustossen ... Das Init-File unterstützt jetzt eine zweigeteilte Syntax in dieser Form: { "pre_connect": { "tinkerforge/register/ip_connection/connected": {"register": true}, "tinkerforge/register/ip_connection/enumerate": {"register": true} }, "post_connect": { "tinkerforge/request/ip_connection/enumerate": "" } } Mit diesem Beispiel-Init-File werden vor dem Verbinden der IP-Connection die Callbacks registriert und direkt nach dem Connect wird ein Enumerate ausgelöst. -> callback/ip_connection/disconnected ist aber z.zt. ebenfalls "seltsam" : soweit ich sehe, wird dieser callback nur getriggert, falls ein "regulärer" ausstieg via KeyboardInterrupt erfolgt. alle anderen umstände haben "seltsame" dinge zur folge : (PROBLEM ? BUG? falsch verstanden ?) wenn ich eine zwischenzeitliche ip-netzwerk-unterbrechung zum brickd simuliere, scheint der disconnect probe thread dies nicht zu erkennen ! selbst wenn ich abseits der zu erwartenden callbacks auch funktionen via -> request/.../get_identity o.ä. absetze, erhalte ich -> response/.../get_identity { '_ERROR' : "Did not receive response for function BLAH in time ..." } ... aber es erfolgt keinerlei triggern eines -> callback/ip_connection/disconnected !!! Der Disconnected-Callback meldet den Verlust der TCP-Connection zum Brick Daemon. Wenn du das Netzwerkkabel rausziehst, wird die aber nicht geschlossen. Wenn du den Brick Daemon beendest, sollte der Callback auslösen. Detektieren, ob die Netzwerkverbindung an sich unterbrochen ist, können aktuell keine Bindings. Langfristig ist dafür aber eine Lösung geplant. jeden topic auf { ..., "_ERROR" : ... } zu prüfen IST mühsam ! es könnte auch einen weiterer "höher" aggregierter topic z.b. -> callback/bindings/issue { "type" : ... } eingebaut werden, wo dann von aussen entschieden werden kann, ob es sich um ein potentiell temporäres problem der ip_connection handelt und das wartens auf ein reconnect sinnvoll ist oder eine - permanent fatale - exception auftrat ... Das kannst du aktuell auch haben, indem du tinkerforge/# subscribst und nachsiehst ob "_ERROR" im Payload ist. aufgrund dieser info kann man noch immer entscheiden, ob man mittels z.b. -> request/bindings/terminate ein normales mqtt-disconnect durchführt oder über -> request/bindings/abort und den proxy ohne disconnect mit exit(1) beendet ... Eigentlich sollte es nicht nötig sein, die Bindings neuzustarten. Hast du das Verhalten bei Verbindungsproblemen mit den aktuellen Bindings getestet? Ich hatte für Version 2.0.7 einiges am Verhalten geändert, damit es saubere Reconnects zum Broker und Brick Daemon gibt. Falls du mit der angehangenen Version in der Richtung noch Probleme hast, gib nochmal Bescheid. so wie bei fatalen fehlern den proxy einfach beendet und den "last will" triggert ... Die Bindings schicken wie gehabt "null" nach tinkerforge/callback/bindings/restart und mit der neuen Version auch nach tinkerforge/callback/bindings/shutdown, wenn sie normal beendet werden oder nach tinkerforge/callback/bindings/last_will, falls sie abgeschossen wurden oder die Verbindung weg ist. Ich habe die aktuelle Version mal angehangen.tinkerforge_mqtt_bindings_2_0_8_beta.zip Quote Link to comment Share on other sites More sharing options...
piwo2 Posted September 5, 2019 at 07:40 AM Author Share Posted September 5, 2019 at 07:40 AM danke vielmals ! werde das testen mal ... mqtt ist für mich das werkzeug derwahl, somit ziemlich zentral ;-) ============= noch ein paar gedanken ......... ich nutze folgedes pattern für - cronjob oder sonstige endloslogik in einem bash-skript : sub(topic=PREFIX/status) # falls "terminated" (mqtt-proxy abnormales ende) : # retry counter bzw. timeout ## mqtt-proxy start pub(qos=2,retained=true,topic="PREFIX/status",data="starting") - im gestarteten programm : connect(LWT=(qos=2,retained=true,topic="PREFIX/status",data="died")) ... pub(qos=2,retained=true,topic="PREFIX/status",data="started") sub(topic=PREFIX/status) pub(qos=2,retained=true,topic="PREFIX/status",data="functional") ... ... # exceptions triggern LWT und # sinnvolle statusinformationen werden gepublished : pub(qos=2,retained=true,topic="PREFIX/status",data="blah-whatever") ... ... if transient_error: pub(qos=2,retained=true,topic="PREFIX/status",data="restartable") disconnect() # LWT discard exit(1) if fatal_error: pub(qos=2,retained=true,topic="PREFIX/status",data="fatal") disconnect() # LWT discard exit(1) ... ... pub(qos=2,retained=true,topic="PREFIX/status",data="done") disconnect() # LWT discard exit(0) # reguläres programmende --- je nach programmstruktur sollte sich mit einem solchen pattern 1) nutzbare informationen für die restartlogik ergeben 2) eine verlässliche (quos=2 !!!) "interne" state-machine implementieren, die über on_message() nicht nur auf mehrfachen programmstart checkt, sondern auch sonst z.b. den *** "robusten ansatz" der tinkerforge-programme *** umsetzt ;-) ============= bottom line : bitte nochmals ein review, das die (externe) restartlogik in die überlegungen miteinbezieht und : wie wird der MQTT-proxy eigentlich von aussen "regulär beendet" ? SIGTERM ? ... er läuft ja idealerweise endlos ... und KeyboardInterrupt von extern triggern - da bin ich kein spezialist ;-) lg wp Quote Link to comment Share on other sites More sharing options...
rtrbt Posted September 5, 2019 at 09:50 AM Share Posted September 5, 2019 at 09:50 AM bitte nochmals ein review, das die (externe) restartlogik in die überlegungen miteinbezieht Brauchst du die externe Restartlogik für die Bindings oder dein Programm, dass mit dem Bindings MQTT spricht? und : wie wird der MQTT-proxy eigentlich von aussen "regulär beendet" ? SIGTERM ? ... er läuft ja idealerweise endlos ... und KeyboardInterrupt von extern triggern - da bin ich kein spezialist ;-) Den KeyboardInterrupt kannst du mit Strg-C auslösen, oder indem du SIGINT schickst. Ich habe gerade mal noch eingebaut, dass SIGTERM und SIGQUIT auch funktionieren. Edit: Durch das sinnvollere Signal-Handling kann man die Bindungs auch als systemd-Service verwenden, ein Beispiel-Service-File habe ich mal angehangen. tinkerforge_mqtt_bindings_2_0_8_beta2.ziptinkerforge_mqtt.service Quote Link to comment Share on other sites More sharing options...
piwo2 Posted September 5, 2019 at 11:32 AM Author Share Posted September 5, 2019 at 11:32 AM hallo ! in meiner sichtwiese sind die bindings wie ein service für höherliegende applikationen ! das hat 2 konsequenzen : a) das service (bindings) muss theoretisch immer laufen. dazu müssen sie (bindings) : 1) gestartet und im a) nicht fatalen fehlerfall restartet werden bis ein retry-count oder timeout erreicht wird : diese aufgabe übernimmt im idealfall der systemd oder ein cron-job b) fatalen fehlerfall nicht mehr restartet werden c) muss ich diesen "systemzustand" entweder aus den mqtt-topics der bindings erkennen können oder separate topics im restart-script einbauen, die den zustand dieses "services" für übergeordnete applikationen widerspiegeln d) dazu muss idealerweise ein (re-)startscript ausreichend informationen von den topics bindings haben e) sollte eine (re-)startlogik aber in den bindings über "ihre" topics selbst verfügbar sein, bzw. der "systemzustand" auch dort ablesbar sein, so fällt eine externe überwachung über ein (re-)start-script natürlich weg ... die state-machine wäre idealerweise so ähnlich wie state=starting, ts_started=<now>, ts_now=<now> state=running, ts_started=<time of state == starting>, ts_now=<now> state=retrying, ts_started=..., ts_now=..., retry_count_now=..., time_between_retries=<cmd_line_parameter>, max_retry_count=<cmd_line_parameter> state=giveup, ts_started=..., ts_now=..., retry_count_now=<same as max_retry_count>, time_between_retries=<cmd_line_parameter>, max_retry_count=<cmd_line_parameter> ---- beantwortet das die frage ? nebenbei : warum wird für jeden internen zustand der bindings ein eigener topic mit inhalt "null" genutzt ? es wäre doch EIN topic mit entsprechdem inhalt ev. besser bzw. EIN topic für (re-)start und je EINER für jeden "internen zustand" einer dezidierten komponente : z.b. mqtt-status, brickd-status, _ERROR-status .... lg wp Quote Link to comment Share on other sites More sharing options...
rtrbt Posted September 5, 2019 at 01:47 PM Share Posted September 5, 2019 at 01:47 PM Hm, ich glaube, dass du dir das zu kompliziert machst: Falls die Bindings einen nicht fatalen Fehler finden, schreiben sie ins Log und laufen weiter. Beispiele dafür sind nicht-parsebare Topics oder Timeouts usw.. Falls ein fataler Fehler auftritt, d.h. einer bei dem die Bindings nicht weiterlaufen können, dann ist das entweder ein Problem des Rechners auf dem sie laufen, z.B. RAM voll, Abhängigkeiten nicht installiert, defekte Hardware oder sowas. Dann wird der Fehler ausgegeben, es ist aber immer Nutzerinteraktion nötig, um das Problem zu lösen. Oder es ist ein Bug in den Bindings, dann fixen wir den. Automatische Neustarts führen dann nur dazu, dass Probleme versteckt werden. nebenbei : warum wird für jeden internen zustand der bindings ein eigener topic mit inhalt "null" genutzt ? es wäre doch EIN topic mit entsprechdem inhalt ev. besser bzw. EIN topic für (re-)start und je EINER für jeden "internen zustand" einer dezidierten komponente : z.b. mqtt-status, brickd-status, _ERROR-status .... Die Null-Topics gibt es nur für den Zustand der Bindings, Brickd-Status bekommst du wie bei allen anderen API-Bindings über die Callbacks der IP-Connection und get_connection_state. Der MQTT-Status ist ein interessantes Problem: Wenn der nicht "okay" ist, dann können die Bindings das auch nicht mitteilen. Sie sind aber robust dagegen, dass die Verbindung zum Broker mal wegbricht. Für den Binding-Status habe ich drei Topics gebaut, anstelle von einem mit Payload, weil 1. das restart-Topic vor den anderen existierte, d.h. das rückwärtskompatibel zu ändern ist nicht drin. 2. sind das ja eher Trigger, auf die andere Software reagieren soll. Wenn das einzelne Topics sind, kann man sich dann auf das registrieren, was einen interessiert. (So wie es die Bindings selber mit restart machen um Kollisionen mit anderen Bindings-Instanzen zu erkennen) Du kannst dich natürlich auf tinkerforge/callback/bindings/+ registrieren, dann hast zu ein Verhalten, das sehr ähnlich zu einem Topic mit Payload ist. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.