2016-06-29 10 views
2

Seit einigen Wochen verwenden wir Storm in unserem Projekt. Heute haben wir ein sehr merkwürdiges Verhalten entdeckt. Nehmen wir an, wir die folgende Topologie:Apache Storm: Veränderliches Objekt, das an verschiedene Schrauben ausgegeben wird


SpoutA ---> BoltB 
     ---> BoltC 

So haben wir eine SpoutA haben, die eine benutzerdefinierte Java-Objekt aussendet (nennen wir es Message) an zwei verschiedene Schrauben. BoltB und BoltC. Grundsätzlich führen wir einen Split durch.

Bis heute hatten wir die Annahme, dass wenn SpoutA das Message Object ausgibt, es auf SpoutA serialisiert und sowohl auf BoltB als auch auf BoltC deserialisiert wird. Diese Annahme scheint jedoch falsch zu sein. Heute haben wir festgestellt, dass das deserialisierte Objekt in BoltB identisch mit dem Objekt in BoltC ist (Same System.identitfyHashCode). Mit anderen Worten, wenn ich das Objekt in BoltB manipuliere, manipuliere ich auch das Objekt in BoltC, was zu vielen unvorhergesehenen Nebenwirkungen führt.

Darüber hinaus scheint mir dieses Verhalten sehr seltsam, da es nur gilt, wenn die SpoutA und die entsprechenden Bolts B und C in demselben Arbeiter ausgeführt werden. Wenn ich explizit die Verwendung von drei Werken erzwinge, dann ist das Objekt (wie erwartet) ein anderes Objekt für BoltB und für BoltC, da es in verschiedenen JVMs verwendet wird. Wenn wir also davon ausgehen, dass wir eine größere Topologie (50 verschiedene Schrauben) haben, die auf drei Arbeitern läuft, dann könnten wir nie sicher sein, ob Objekte momentan zwischen Schrauben geteilt sind oder nicht.

Also im Grunde wollen wir wirklich nicht, dass ein Objekt zwischen Schrauben geteilt wird. Wir würden normalerweise erwarten, dass während der Deserialisierung für jede Schraube neue Objekte erstellt werden.

Also hier ist meine Frage: Was sind unsere größten Fehler hier? Ist unser Hauptfehler, dass wir "veränderbare" Objekte aussenden? Verwenden wir Serialisierung/Deserialisierung falsch? Oder kann es sogar ein Konstruktionsfehler des Sturms sein?

Offensichtlich könnten wir die Schaffung neuer Objekte erzwingen, indem wir nur Byte-Arrays aussenden, aber das ist meiner Meinung nach im Gegensatz zu Storm.

Mit freundlichen Grüßen, André

+1

Derzeit laufen wir 40 Bolzen Sturm Prozess auf unserer Produktion und erste hatten wir dünner Problem so wenn wir Objekt von einer Schraube zu anderen analysieren wir verwenden XML-Serialisierung und es wird als XML-Zeichenfolge übergeben. es löst unser Problem –

+1

Ja, wir haben auch darüber nachgedacht, unsere eigene Serialisierung/Deserialisierung zu implementieren (aber dann auf Byte-Ebene). Die andere Lösung würde explizit übertragene Objekte als unveränderlich per Konvention definieren. Ich hoffte jedoch, dass es eine bessere Lösung geben könnte (z. B. nur eine Einstellung, um Storm zu zwingen, die Objekte getrennt zu deserialisieren ...) – Vion

+0

ja aggregiert Sie können mit denen zu Optionen gehen –

Antwort

1

Storm verwendet zwei verschiedene Warteschlangenansätze, wenn Tupel von einer Komponente in eine andere verschoben werden, eine, in der sich die beiden Komponenten in derselben JVM befinden und eine, in der das Tupel über JVMs hinweg laufen muss.Ich denke, Sie werden in dem JVM-Fall gefangen, in dem Objekte in Tupeln nicht serialisiert werden, da die Serialisierung nur für JVM-Queues erforderlich ist.

Ich marshall immer und demarshall die Daten zwischen Tuple und Java-Bean, um eine stark typisierte Schnittstelle für meine Geschäftslogik in jedem Bolzen/Auslauf zur Verfügung zu stellen. Vermutlich vermeide ich damit das Problem, auf das Sie stoßen. Das könnte ein Weg sein, um Ihr Problem zu umgehen.

+0

Ja, danke für die ausführliche Erklärung. Ich habe darüber auch vor einer halben Stunde in der folgenden Präsentation gelesen (http://de.slideshare.net/miguno/apache-storm-09-basic-training-verisign). Ich verstehe jedoch nicht, warum dies auf der Storm-Website nicht dokumentiert ist. Marshalling und Demarshalling lösen das Problem definitiv, können aber auch die Leistung verringern. – Vion

0

Warum erwarten Sie die Hash-Code, anders zu sein? Genauso wie es keine Anforderung gibt, dass vom Benutzer bereitgestellte Hashcode-Werte für jede neue Objektinstanz (mit denselben Feldern und Feldwerten!) Unterschiedlich sein sollten, gibt es nichts, was die native Hashcode-Implementierung erfordert, unterschiedliche Werte zurückzugeben, während das gleiche Objekt zweimal erzeugt wird.

So, um Ihre Frage zu beantworten: Was sind unsere größten Fehler hier?

Der Hauptfehler ist Ihr Verständnis, wie Hashcode funktioniert. Wie Mahesh Madushanka im Kommentar ausgeführt hat, können Sie dieses Feature umgehen.

Auch wenn Sie ein Objekt serialisieren, serialisiert es alles, einschließlich der privaten Felder. Und viele Java-Objekte cachen ihren Hash-Wert in einem privaten Feld. Z.B. Zeichenfolge. Es ist also völlig normal, dass ihr Hashcode den gleichen Wert zurückgibt (ich weiß, dass Sie System.identitfyHashCode verwenden, und String gibt einen überschriebenen Wert zurück, aber das ist trotzdem wichtig zu merken).

+0

Es geht nicht um den Hashcode. Der HashCode interessiert mich nicht wirklich. Ich sorge dafür, dass die Objekte, die durch zwei verschiedene Schrauben deserialisiert werden, immer noch identisch sind (nicht nur in Bezug auf den Hashcode). Wenn ich das Objekt in BoltB modifiziere, wird es auch in BoltC geändert. Dies führt zu undefinierbarem Verhalten, da man nicht sicher sein kann, ob ein Bolt in derselben oder in einer anderen VM ausgeführt wird. Daher kann man nicht sicher sein, ob man zwei verschiedene Objekte erhält, die identisch oder einfach gleich sind. Daher ist es meiner Meinung nach kein Feature, sondern ein Bug von Apache Storm. – Vion

+0

@Vion Also verstehe ich nicht, Sie haben angenommen, dass der Hashcode identisch ist, weil die Objekte identisch sind. Aber diese Annahme ist falsch. Wie haben Sie überprüft, ob diese Objekte identisch sind? Haben Sie einen '==' Vergleich auf ihrer Referenz gemacht? Denn * das * macht zwei Objekte identisch. –

+0

Wir haben überprüft, dass die Objekte identisch sind, indem wir eine Eigenschaft (z. B. einen Zähler) des in BoltB empfangenen Objekts manipulieren und die Eigenschaft (den Zähler) in BoltC auslesen. Der Wert, auf den wir die Eigenschaft in BoltB geändert haben, war dann in BoltC verfügbar => Das von BoltB empfangene Objekt ist genau dasselbe Objekt, das BoltC erhalten hat ... jede Änderung, die ich in BoltB auf das Objekt anwende, gilt auch für BoltC ... (wie in einem einfachen Threading-Szenario ...) – Vion

Verwandte Themen