Architekturproblem mit Tomcat Cluster-Umgebung

8

Ich arbeite an einem Projekt, in dem wir einen Authentifizierungsmechanismus haben. Wir befolgen die folgenden Schritte im Authentifizierungsmechanismus.

  1. Der Benutzer öffnet einen Browser und gibt seine E-Mail-Adresse in ein Textfeld ein und klickt auf den Login-Button.
  2. Die Anfrage geht an einen Server. Wir generieren eine zufällige Zeichenfolge (z. B. 123456) und senden eine Benachrichtigung an das Android / iPhone und lässt den aktuellen Thread mit Hilfe der wait() -Methode warten.
  3. Der Benutzer gibt ein Passwort auf seinem Telefon ein und klickt auf die Senden-Taste seines Telefons.
  4. Sobald der Benutzer auf den Submit-Button klickt, machen wir einen Webservice, der den Server anspricht und den zuvor generierten String (zum Beispiel 123456) und das Passwort.
  5. übergibt
  6. Wenn das Passwort mit der zuvor eingegebenen E-Mail übereinstimmt, rufen wir die Methode notify() für den zuvor wartenden Thread auf und senden Erfolg als Antwort, und der Benutzer wird in unser System eingegeben.
  7. Wenn das Passwort für die zuvor eingegebene E-Mail falsch ist, rufen wir die notify() -Methode für den zuvor wartenden Thread auf und senden failed als Antwort und zeigen dem Benutzer eine ungültige Anmeldeinformation an.

Alles funktioniert gut, aber kürzlich sind wir in eine Clusterumgebung gewechselt. Wir haben festgestellt, dass einige Threads auch nach einer Antwort des Benutzers und einer unbegrenzten Wartezeit nicht benachrichtigt werden.

Für den Server verwenden wir Tomcat 5.5, und wir folgen The Apache Tomcat 5.5 Servlet / JSP Container für die Tomcat Cluster-Umgebung.

Antwort :: Mögliches Problem und Lösung

Das mögliche Problem sind die mehreren JVMs in einer Clusterumgebung. Jetzt senden wir auch die gruppierte Tomcat-URL zusammen mit der generierten Zeichenfolge an die Android-Anwendung des Benutzers.

Und wenn der Benutzer auf die Antwortschaltfläche klickt, senden wir die generierte Zeichenfolge zusammen mit der gruppierten Tomcat-URL. In diesem Fall werden beide Anfragen an dieselbe JVM gesendet, und es funktioniert einwandfrei.

Aber ich frage mich, ob es eine einzige Lösung für das oben genannte Problem gibt.

Es gibt ein Problem in dieser Lösung. Was passiert, wenn der Clustered-Tomcat abstürzt? Der Load-Balancer sendet eine Anfrage an den zweiten Cluster-Tomcat, und wieder tritt das gleiche Problem auf.

    
Rais Alam 23.12.2012, 11:57
quelle

10 Antworten

9

Der Grund für Ihre Probleme liegt darin, dass Java EE so konzipiert wurde, dass es auf eine andere Art und Weise funktioniert. Der Versuch, einen Dienst-Thread zu blockieren / zu warten, ist einer der wichtigen No-No-Punkte. Ich werde den Grund dafür zuerst nennen und danach das Problem lösen.

Java EE (sowohl die Web- als auch die EJB-Ebene) wurde so entworfen, dass es auf sehr große Größen skaliert werden kann (Hunderte von Computern in einem Cluster). Um dies zu tun, mussten die Designer jedoch die folgenden Annahmen treffen, die spezifische Einschränkungen bei der Codierung darstellen:

  • Transaktionen sind:

    1. Kurzlebig (z. B. nicht blockieren oder auf Zeiträume von mehr als einer Sekunde warten)
    2. Unabhängig voneinander (zB keine Kommunikation zwischen Threads)
    3. Für EJBs, die vom Container verwaltet werden
  • Der gesamte Benutzerstatus wird in bestimmten Datenspeichercontainern beibehalten, einschließlich:

    1. Ein Datenspeicher, auf den z. B. über JDBC zugegriffen wird. Sie können eine traditionelle SQL-Datenbank oder ein NoSQL-Backend verwenden
    2. Stateful Session-Beans, wenn Sie EJBs verwenden. Stellen Sie sich diese als Java Bean vor, die ihre Felder in einer Datenbank speichert. Stateful Session Beans werden vom Container
    3. verwaltet
    4. Websitzung Dies ist ein Schlüssel-Wert-Speicher (etwa wie eine NoSQL-Datenbank, aber ohne die Skalierungs- oder Suchfunktionen), die Daten für einen bestimmten Benutzer über ihre Sitzung hinweg festhält. Es wird vom Java EE-Container verwaltet und hat die folgenden Eigenschaften:

      1. Es wird automatisch verschoben, wenn der Knoten in einem Cluster abstürzt
      2. Benutzer können mehr als eine aktuelle Websitzung haben (d. h. auf zwei verschiedenen Browsern)
      3. Websitzungen enden, wenn der Benutzer seine Sitzung durch Abmelden beendet oder wenn die Sitzung länger als das konfigurierbare Zeitlimit inaktiv ist.
      4. Alle Werte, die gespeichert werden müssen serialisierbar sein, damit sie zwischen Knoten in einem Cluster persistiert oder übertragen werden können.

Wenn wir diesen Regeln folgen, kann der Java EE-Container erfolgreich einen Cluster verwalten, einschließlich dem Herunterfahren von Knoten, dem Starten neuer Knoten und dem Migrieren von Benutzersitzungen, ohne einen bestimmten Entwicklercode . Entwickler schreiben die grafische Benutzeroberfläche und die Geschäftslogik - alle "Installationen" werden durch konfigurierbare Containerfunktionen verwaltet.

Außerdem kann der Java EE-Container zur Laufzeit von einer ziemlich ausgeklügelten Software überwacht und verwaltet werden, die Anwendungsleistung und Verhaltensprobleme in einem Live-System nachverfolgen kann.

& lt; snark & ​​gt; Nun, das war die Theorie. Die Praxis legt nahe, dass es ziemlich wichtige Einschränkungen gibt, die übersehen wurden, die zu AOSP- und Code-Injektionstechniken führen, aber das ist eine andere Geschichte & lt; / snark & ​​gt;

[Es gibt viele Diskussionen rund um das Internet dazu. Eine, die sich auf EJBs konzentriert, ist hier: Warum Laich-Threads in Java EE-Container wird abgeraten? Genau das gleiche gilt für Webcontainer wie Tomcat]

Entschuldigung für den Aufsatz - aber das ist wichtig für Ihr Problem. Aufgrund der Beschränkungen für Threads sollten Sie die Webanforderung, die auf eine spätere Anforderung wartet, nicht blockieren.

Ein weiteres Problem mit dem aktuellen Design ist, was passiert, wenn der Benutzer vom Netzwerk getrennt wird, keine Stromversorgung mehr hat oder sich einfach entscheidet aufzugeben? Vermutlich wirst du aussteigen, aber nach wie lange? Gerade zu früh für einige Kunden, vielleicht, was Zufriedenheitsprobleme verursachen wird. Wenn das Zeitlimit zu lang ist, können Sie in Tomcat die alle Worker-Threads blockieren und der Server wird einfrieren. Dies öffnet Ihre Organisation für einen Denial-of-Service-Angriff.

BEARBEITEN: Verbesserte Vorschläge, nachdem eine detailliertere Beschreibung des Algorithmus veröffentlicht wurde.

Ungeachtet der obigen Diskussion über die schlechte Praxis des Blockierens eines Web-Worker-Threads und der möglichen Dienstverweigerung ist es klar, dass dem Benutzer ein kleines Zeitfenster angezeigt wird, in dem er auf die Benachrichtigung auf dem Android-Telefon reagieren kann. und das kann relativ klein gehalten werden, um die Sicherheit zu erhöhen. Dieses Zeitfenster kann auch unterhalb von Tomcat's Timeout für Antworten gehalten werden. So könnte der Thread-Blocking-Ansatz verwendet werden.

Es gibt zwei Möglichkeiten, wie dieses Problem gelöst werden kann:

  1. Ändern Sie den Fokus der Lösung auf das Clientende. - Abfragen des Servers mithilfe von JavaScript im Browser
  2. Die Kommunikation zwischen Knoten im Cluster ermöglicht es dem Knoten, die Autorisierungsantwort von der Android App zu erhalten, den Knoten freizugeben, der die Antwort des Servlets blockiert.

Bei Ansatz 1 fragt der Browser den Server über Javascript mit einem AJAX-Aufruf an einen Web-Service auf Tomcat ab; Der AJAX-Aufruf gibt True zurück, wenn die Android-App authentifiziert wurde.Vorteil: Client-Seite, minimale Implementierung auf dem Server, keine Thread-Blockierung auf dem Server. Nachteile: Während der Wartezeit müssen Sie häufig telefonieren (vielleicht eine Sekunde - der Benutzer wird diese Latenz nicht bemerken), was zu vielen Anrufen und zusätzlicher Belastung des Servers führt.

Für Ansatz 2 gibt es wieder die Wahl:

  1. Blockieren Sie den Thread mit einem Object.wait() , der optional die Knoten-ID, IP oder eine andere Kennung in einem freigegebenen Datenspeicher speichert: Wenn dies der Fall ist, muss der Knoten, der die Android-App-Autorisierung erhält:

    1. Finden Sie entweder den Knoten, der gerade blockiert oder an alle Knoten im Cluster sendet
    2. Senden Sie für jeden Knoten in 1. oben eine Nachricht, die angibt, welche Benutzersitzung entsperrt werden soll. Die Nachricht könnte gesendet werden über:

      1. Verfügen Sie über ein internes Servlet auf jedem Knoten - dies wird vom Servlet aufgerufen, das die Android App-Autorisierung ausführt. Das interne Servlet ruft Object.notify im korrekten Thread
      2. auf
      3. Verwenden Sie eine JMS-Pub-Sub-Nachrichtenwarteschlange, um an alle Mitglieder des Clusters zu senden. Jeder Knoten ist ein Abonnent, der beim Empfang einer Benachrichtigung Object.notify() im richtigen Thread aufruft.
  2. Beantworten Sie einen Datenspeicher, bis der Thread zum Fortfahren berechtigt ist: In diesem Fall muss die gesamte Android-App den Status in einer SQL-Datenbank speichern

Andrew Alcock 27.12.2012, 09:16
quelle
1

Die Verwendung von wait / notify kann schwierig sein. Denken Sie daran, dass jeder Thread jederzeit unterbrochen werden kann. Es ist also möglich, dass notify vor dem Warten angerufen wird. In diesem Fall wird wait für immer blockiert.

Ich würde dies in Ihrem Fall nicht erwarten, da Sie Benutzerinteraktion beteiligt sind. Aber für die Art der Synchronisation, die Sie gerade tun, versuchen Sie es mit einem Semaphor. Erstellen Sie einen Semaphor mit 0 (Null) Menge. Der wartende Thread ruft acquire () auf und blockiert, bis ein anderer Thread release () aufruft.

Semaphore auf diese Weise zu verwenden, ist wesentlich robuster als Warten / Benachrichtigen für die Aufgabe, die Sie beschrieben haben.

    
David Roussel 23.12.2012 12:03
quelle
1

Ziehen Sie die Verwendung eines speicherinternen Gitters in Betracht, damit die Instanzen im Cluster den Status gemeinsam nutzen können. Wir haben Hazelcast verwendet, um Daten zwischen Instanzen zu teilen. Falls eine Antwort eine andere Instanz erreicht, kann sie damit umgehen.

z. Sie können den verteilten Countdown-Latch mit dem Wert 1 verwenden, um den nach dem Senden der Nachricht wartenden Thread festzulegen. Wenn die Antwort vom Client auf eine separate Instanz ankommt, kann diese Instanz den Latch auf 0 reduzieren und den ersten Thread ausführen .

    
András Kerekes 27.12.2012 05:38
quelle
1

Ihre Cluster-Bereitstellung bedeutet, dass jeder Knoten im Cluster eine Antwort erhalten kann.

Die Verwendung von wait / notify mit Threads für eine Webanwendung birgt das Risiko, dass viele Threads angehäuft werden, die möglicherweise nicht benachrichtigt werden und Speicher oder viele blockierte Threads verursachen könnten. Dies könnte die Zuverlässigkeit Ihres Servers beeinträchtigen.

Eine robustere Lösung wäre, die Anfrage an die Android-App zu senden und den aktuellen Status der Benutzeranforderung für die spätere Verarbeitung zu speichern und die HTTP-Anfrage abzuschließen. Um den Zustand zu speichern, den Sie in Erwägung ziehen könnten:

  • Ein , mit dem sich alle Tomcat-Knoten mit
  • verbinden
  • Eine Java-Cache-Lösung, die für Tomcat-Knoten wie funktioniert

Dieser Status ist für alle Knoten in Ihrem Tomcat-Cluster sichtbar.

Wenn die Antwort der Android App auf einem anderen Knoten eintrifft, stellen Sie den Zustand des Threads wieder her und fahren Sie mit der Verarbeitung auf diesem Knoten fort.

Wenn die Benutzeroberfläche der Anwendung auf eine Antwort vom Server wartet, können Sie eine Anfrage zur Abfrage des Antwortstatus vom Server. Der Knoten, der die Android-App-Antwort verarbeitet, muss nicht mit den UI-Anforderungen identisch sein.

    
pd40 26.12.2012 12:00
quelle
1

Die Verwendung von Thread.wait in einer Web-Service-Umgebung ist ein kolossaler Fehler. Pflegen Sie stattdessen eine Datenbank mit Benutzer- / Token-Paaren, und führen Sie sie in Intervallen aus.

Wenn Sie einen Cluster möchten, verwenden Sie eine Datenbank, die gruppierbar ist. Ich würde etwas wie memcached empfehlen, da es in-memory (und schnell) und wenig Aufwand (Schlüssel / Wert-Paare sind tot einfach, so dass Sie nicht brauchen RDBMS, etc.). memcached behandelt den Ablauf von Token für Sie bereits, so dass es wie eine perfekte Passform scheint.

Ich denke der Benutzername - & gt; Token - & gt; Die Passwortstrategie ist unnötig, insbesondere weil zwei verschiedene Komponenten die gleiche 2-Faktor-Authentifizierungsverantwortung teilen. Ich denke, dass Sie Ihre Komplexität weiter reduzieren, Verwirrung für Ihre Benutzer reduzieren und sich etwas Geld in SMS-Sendegebühren sparen können.

Die Interaktion mit Ihrem Webservice ist einfach:

  1. Der Benutzer meldet sich mit Benutzername + Passwort
  2. bei Ihrer Website an
  3. Wenn die primäre Authentifizierung (Benutzername / Passwort) erfolgreich ist, generieren Sie ein Token und fügen Sie userid = token in memcached
  4. ein
  5. Senden Sie das Token an das Telefon des Benutzers
  6. Präsentieren Sie die Seite "Token eingeben" für den Benutzer
  7. Der Benutzer erhält das Token per Telefon und trägt es in das Formular
  8. ein
  9. Holt den Token-Wert aus Memcached basierend auf der Benutzer-ID. Wenn es zutrifft, verfallen Sie das Token in memcached und betrachten Sie den zweiten Faktor als erfolgreich
  10. Token werden nach Ablauf der Zeit, die Sie in memcached
  11. festlegen möchten, automatisch ablaufen

Es gibt keine Threading-Probleme mit der obigen Lösung und es werden so viele JVMs skaliert, wie Sie benötigen, um Ihre eigene Software zu unterstützen.

    
Christopher Schultz 27.12.2012 21:16
quelle
1

Nachdem ich Ihre Frage analysiert hatte, kam ich zu dem Schluss, dass das genaue Problem mehrere JVMs in einer Clusterumgebung sind.

    
user1924920 23.12.2012 12:08
quelle
1

Das genaue Problem liegt an der Cluster-Umgebung. Beide Anfragen gehen nicht zur selben JVM. Aber wir wissen, dass eine normale / einfache Benachrichtigung auf der gleichen JVM funktioniert, wenn der vorherige Thread wartet.

Sie sollten versuchen, beide Anfragen auszuführen (erste Anfrage, zweite Anfrage, wenn der Benutzer aus einer Android-Anwendung antwortet).

    
user1924985 23.12.2012 13:17
quelle
1
___ qstnhdr ___ Architekturproblem mit Tomcat Cluster-Umgebung ___ answer14049871 ___

Ziehen Sie die Verwendung eines speicherinternen Gitters in Betracht, damit die Instanzen im Cluster den Status gemeinsam nutzen können. Wir haben Hazelcast verwendet, um Daten zwischen Instanzen zu teilen. Falls eine Antwort eine andere Instanz erreicht, kann sie damit umgehen.

z. Sie können den verteilten Countdown-Latch mit dem Wert 1 verwenden, um den nach dem Senden der Nachricht wartenden Thread festzulegen. Wenn die Antwort vom Client auf eine separate Instanz ankommt, kann diese Instanz den Latch auf 0 reduzieren und den ersten Thread ausführen .

    
___ answer14010799 ___

Die Verwendung von wait / notify kann schwierig sein. Denken Sie daran, dass jeder Thread jederzeit unterbrochen werden kann. Es ist also möglich, dass notify vor dem Warten angerufen wird. In diesem Fall wird wait für immer blockiert.

Ich würde dies in Ihrem Fall nicht erwarten, da Sie Benutzerinteraktion beteiligt sind. Aber für die Art der Synchronisation, die Sie gerade tun, versuchen Sie es mit einem Semaphor. Erstellen Sie einen Semaphor mit 0 (Null) Menge. Der wartende Thread ruft acquire () auf und blockiert, bis ein anderer Thread release () aufruft.

Semaphore auf diese Weise zu verwenden, ist wesentlich robuster als Warten / Benachrichtigen für die Aufgabe, die Sie beschrieben haben.

    
___ answer14052192 ___

Der Grund für Ihre Probleme liegt darin, dass Java EE so konzipiert wurde, dass es auf eine andere Art und Weise funktioniert. Der Versuch, einen Dienst-Thread zu blockieren / zu warten, ist einer der wichtigen No-No-Punkte. Ich werde den Grund dafür zuerst nennen und danach das Problem lösen.

Java EE (sowohl die Web- als auch die EJB-Ebene) wurde so entworfen, dass es auf sehr große Größen skaliert werden kann (Hunderte von Computern in einem Cluster). Um dies zu tun, mussten die Designer jedoch die folgenden Annahmen treffen, die spezifische Einschränkungen bei der Codierung darstellen:

  • Transaktionen sind:

    1. Kurzlebig (z. B. nicht blockieren oder auf Zeiträume von mehr als einer Sekunde warten)
    2. Unabhängig voneinander (zB keine Kommunikation zwischen Threads)
    3. Für EJBs, die vom Container verwaltet werden
  • Der gesamte Benutzerstatus wird in bestimmten Datenspeichercontainern beibehalten, einschließlich:

    1. Ein Datenspeicher, auf den z. B. über JDBC zugegriffen wird. Sie können eine traditionelle SQL-Datenbank oder ein NoSQL-Backend verwenden
    2. Stateful Session-Beans, wenn Sie EJBs verwenden. Stellen Sie sich diese als Java Bean vor, die ihre Felder in einer Datenbank speichert. Stateful Session Beans werden vom Container
    3. verwaltet
    4. Websitzung Dies ist ein Schlüssel-Wert-Speicher (etwa wie eine NoSQL-Datenbank, aber ohne die Skalierungs- oder Suchfunktionen), die Daten für einen bestimmten Benutzer über ihre Sitzung hinweg festhält. Es wird vom Java EE-Container verwaltet und hat die folgenden Eigenschaften:

      1. Es wird automatisch verschoben, wenn der Knoten in einem Cluster abstürzt
      2. Benutzer können mehr als eine aktuelle Websitzung haben (d. h. auf zwei verschiedenen Browsern)
      3. Websitzungen enden, wenn der Benutzer seine Sitzung durch Abmelden beendet oder wenn die Sitzung länger als das konfigurierbare Zeitlimit inaktiv ist.
      4. Alle Werte, die gespeichert werden müssen serialisierbar sein, damit sie zwischen Knoten in einem Cluster persistiert oder übertragen werden können.

Wenn wir diesen Regeln folgen, kann der Java EE-Container erfolgreich einen Cluster verwalten, einschließlich dem Herunterfahren von Knoten, dem Starten neuer Knoten und dem Migrieren von Benutzersitzungen, ohne einen bestimmten Entwicklercode . Entwickler schreiben die grafische Benutzeroberfläche und die Geschäftslogik - alle "Installationen" werden durch konfigurierbare Containerfunktionen verwaltet.

Außerdem kann der Java EE-Container zur Laufzeit von einer ziemlich ausgeklügelten Software überwacht und verwaltet werden, die Anwendungsleistung und Verhaltensprobleme in einem Live-System nachverfolgen kann.

& lt; snark & ​​gt; Nun, das war die Theorie. Die Praxis legt nahe, dass es ziemlich wichtige Einschränkungen gibt, die übersehen wurden, die zu AOSP- und Code-Injektionstechniken führen, aber das ist eine andere Geschichte & lt; / snark & ​​gt;

[Es gibt viele Diskussionen rund um das Internet dazu. Eine, die sich auf EJBs konzentriert, ist hier: Warum Laich-Threads in Java EE-Container wird abgeraten? Genau das gleiche gilt für Webcontainer wie Tomcat]

Entschuldigung für den Aufsatz - aber das ist wichtig für Ihr Problem. Aufgrund der Beschränkungen für Threads sollten Sie die Webanforderung, die auf eine spätere Anforderung wartet, nicht blockieren.

Ein weiteres Problem mit dem aktuellen Design ist, was passiert, wenn der Benutzer vom Netzwerk getrennt wird, keine Stromversorgung mehr hat oder sich einfach entscheidet aufzugeben? Vermutlich wirst du aussteigen, aber nach wie lange? Gerade zu früh für einige Kunden, vielleicht, was Zufriedenheitsprobleme verursachen wird. Wenn das Zeitlimit zu lang ist, können Sie in Tomcat die alle Worker-Threads blockieren und der Server wird einfrieren. Dies öffnet Ihre Organisation für einen Denial-of-Service-Angriff.

BEARBEITEN: Verbesserte Vorschläge, nachdem eine detailliertere Beschreibung des Algorithmus veröffentlicht wurde.

Ungeachtet der obigen Diskussion über die schlechte Praxis des Blockierens eines Web-Worker-Threads und der möglichen Dienstverweigerung ist es klar, dass dem Benutzer ein kleines Zeitfenster angezeigt wird, in dem er auf die Benachrichtigung auf dem Android-Telefon reagieren kann. und das kann relativ klein gehalten werden, um die Sicherheit zu erhöhen. Dieses Zeitfenster kann auch unterhalb von Tomcat's Timeout für Antworten gehalten werden. So könnte der Thread-Blocking-Ansatz verwendet werden.

Es gibt zwei Möglichkeiten, wie dieses Problem gelöst werden kann:

  1. Ändern Sie den Fokus der Lösung auf das Clientende. - Abfragen des Servers mithilfe von JavaScript im Browser
  2. Die Kommunikation zwischen Knoten im Cluster ermöglicht es dem Knoten, die Autorisierungsantwort von der Android App zu erhalten, den Knoten freizugeben, der die Antwort des Servlets blockiert.

Bei Ansatz 1 fragt der Browser den Server über Javascript mit einem AJAX-Aufruf an einen Web-Service auf Tomcat ab; Der AJAX-Aufruf gibt %code% zurück, wenn die Android-App authentifiziert wurde.Vorteil: Client-Seite, minimale Implementierung auf dem Server, keine Thread-Blockierung auf dem Server. Nachteile: Während der Wartezeit müssen Sie häufig telefonieren (vielleicht eine Sekunde - der Benutzer wird diese Latenz nicht bemerken), was zu vielen Anrufen und zusätzlicher Belastung des Servers führt.

Für Ansatz 2 gibt es wieder die Wahl:

  1. Blockieren Sie den Thread mit einem %code% , der optional die Knoten-ID, IP oder eine andere Kennung in einem freigegebenen Datenspeicher speichert: Wenn dies der Fall ist, muss der Knoten, der die Android-App-Autorisierung erhält:

    1. Finden Sie entweder den Knoten, der gerade blockiert oder an alle Knoten im Cluster sendet
    2. Senden Sie für jeden Knoten in 1. oben eine Nachricht, die angibt, welche Benutzersitzung entsperrt werden soll. Die Nachricht könnte gesendet werden über:

      1. Verfügen Sie über ein internes Servlet auf jedem Knoten - dies wird vom Servlet aufgerufen, das die Android App-Autorisierung ausführt. Das interne Servlet ruft %code% im korrekten Thread
      2. auf
      3. Verwenden Sie eine JMS-Pub-Sub-Nachrichtenwarteschlange, um an alle Mitglieder des Clusters zu senden. Jeder Knoten ist ein Abonnent, der beim Empfang einer Benachrichtigung %code% im richtigen Thread aufruft.
  2. Beantworten Sie einen Datenspeicher, bis der Thread zum Fortfahren berechtigt ist: In diesem Fall muss die gesamte Android-App den Status in einer SQL-Datenbank speichern

___ answer14061409 ___

Die Verwendung von %code% in einer Web-Service-Umgebung ist ein kolossaler Fehler. Pflegen Sie stattdessen eine Datenbank mit Benutzer- / Token-Paaren, und führen Sie sie in Intervallen aus.

Wenn Sie einen Cluster möchten, verwenden Sie eine Datenbank, die gruppierbar ist. Ich würde etwas wie memcached empfehlen, da es in-memory (und schnell) und wenig Aufwand (Schlüssel / Wert-Paare sind tot einfach, so dass Sie nicht brauchen RDBMS, etc.). memcached behandelt den Ablauf von Token für Sie bereits, so dass es wie eine perfekte Passform scheint.

Ich denke der Benutzername - & gt; Token - & gt; Die Passwortstrategie ist unnötig, insbesondere weil zwei verschiedene Komponenten die gleiche 2-Faktor-Authentifizierungsverantwortung teilen. Ich denke, dass Sie Ihre Komplexität weiter reduzieren, Verwirrung für Ihre Benutzer reduzieren und sich etwas Geld in SMS-Sendegebühren sparen können.

Die Interaktion mit Ihrem Webservice ist einfach:

  1. Der Benutzer meldet sich mit Benutzername + Passwort
  2. bei Ihrer Website an
  3. Wenn die primäre Authentifizierung (Benutzername / Passwort) erfolgreich ist, generieren Sie ein Token und fügen Sie %code% = %code% in memcached
  4. ein
  5. Senden Sie das Token an das Telefon des Benutzers
  6. Präsentieren Sie die Seite "Token eingeben" für den Benutzer
  7. Der Benutzer erhält das Token per Telefon und trägt es in das Formular
  8. ein
  9. Holt den Token-Wert aus Memcached basierend auf der Benutzer-ID. Wenn es zutrifft, verfallen Sie das Token in memcached und betrachten Sie den zweiten Faktor als erfolgreich
  10. Token werden nach Ablauf der Zeit, die Sie in memcached
  11. festlegen möchten, automatisch ablaufen

Es gibt keine Threading-Probleme mit der obigen Lösung und es werden so viele JVMs skaliert, wie Sie benötigen, um Ihre eigene Software zu unterstützen.

    
___ answer14040292 ___

Ihre Cluster-Bereitstellung bedeutet, dass jeder Knoten im Cluster eine Antwort erhalten kann.

Die Verwendung von wait / notify mit Threads für eine Webanwendung birgt das Risiko, dass viele Threads angehäuft werden, die möglicherweise nicht benachrichtigt werden und Speicher oder viele blockierte Threads verursachen könnten. Dies könnte die Zuverlässigkeit Ihres Servers beeinträchtigen.

Eine robustere Lösung wäre, die Anfrage an die Android-App zu senden und den aktuellen Status der Benutzeranforderung für die spätere Verarbeitung zu speichern und die HTTP-Anfrage abzuschließen. Um den Zustand zu speichern, den Sie in Erwägung ziehen könnten:

  • Ein , mit dem sich alle Tomcat-Knoten mit
  • verbinden
  • Eine Java-Cache-Lösung, die für Tomcat-Knoten wie funktioniert

Dieser Status ist für alle Knoten in Ihrem Tomcat-Cluster sichtbar.

Wenn die Antwort der Android App auf einem anderen Knoten eintrifft, stellen Sie den Zustand des Threads wieder her und fahren Sie mit der Verarbeitung auf diesem Knoten fort.

Wenn die Benutzeroberfläche der Anwendung auf eine Antwort vom Server wartet, können Sie eine Anfrage zur Abfrage des Antwortstatus vom Server. Der Knoten, der die Android-App-Antwort verarbeitet, muss nicht mit den UI-Anforderungen identisch sein.

    
___ answer14010833 ___

Nachdem ich Ihre Frage analysiert hatte, kam ich zu dem Schluss, dass das genaue Problem mehrere JVMs in einer Clusterumgebung sind.

    
___ answer14011259 ___

Das genaue Problem liegt an der Cluster-Umgebung. Beide Anfragen gehen nicht zur selben JVM. Aber wir wissen, dass eine normale / einfache Benachrichtigung auf der gleichen JVM funktioniert, wenn der vorherige Thread wartet.

Sie sollten versuchen, beide Anfragen auszuführen (erste Anfrage, zweite Anfrage, wenn der Benutzer aus einer Android-Anwendung antwortet).

    
___ tag123java ___ Java (nicht zu verwechseln mit JavaScript oder JScript oder JS) ist eine universelle objektorientierte Programmiersprache, die für die Verwendung in Verbindung mit der Java Virtual Machine (JVM) entwickelt wurde. "Java-Plattform" ist der Name für ein Computersystem, auf dem Tools zum Entwickeln und Ausführen von Java-Programmen installiert sind. Verwenden Sie dieses Tag für Fragen, die sich auf die Java-Programmiersprache oder Java-Plattform-Tools beziehen. ___ answer14033361 ___

Ich fürchte, aber Threads können nicht über klassische Java EE Cluster migrieren.

Sie müssen Ihre Architektur überdenken, um das Warten / Benachrichtigen anders (verbindungslos) zu implementieren.

Oder Sie versuchen es mit terracotta.org . Es sieht so aus, als ob damit ein gesamter JVM Prozess über mehrere Rechner hinweg geclustert werden kann. Vielleicht ist es deine einzige Lösung.

Lesen Sie eine kurze Einführung in Einführung in OpenTerracotta .

>     
___ tag123tomcat ___ Verwenden Sie dieses Tag für Fragen zu Apache Tomcat (oder einfach Tomcat, früher auch Jakarta Tomcat), bei dem es sich um einen von der Apache Software Foundation (ASF) entwickelten Open-Source-Servlet-Container handelt. Die meisten Fragen sollten auch ein Tag enthalten, das das Betriebssystem angibt. ___ tag123webapplications ___ Webanwendungen sind Anwendungen, auf die über das "Web" zugegriffen wird, das über das Internet oder ein Intranet, d. h. ein internes Netzwerk, erfolgen kann ___ answer14010886 ___

Ich schätze, das Problem ist, dass Ihr erster Thread eine Benachrichtigung an die Android-Anwendung des Benutzers in JVM 1 sendet und wenn der Benutzer antwortet, geht das Steuerelement an JVM 2. Und das ist das Hauptproblem.

Irgendwie können beide Threads auf dieselbe JVM zugreifen, um die Warte- und Benachrichtigungslogik anzuwenden.

    
___ answer14011337 ___

Lösung:

Erstellen Sie einen einzigen Kontaktpunkt für alle wartenden Threads. Daher werden in einer Clusterumgebung alle Threads auf eine dritte JVM (Single Point of Contact) warten, so dass auf diese Weise alle Anfragen (alle Clustered Tomcat ) wird dieselbe JVM kontaktieren, um auf die Logik zu warten und sie zu benachrichtigen. Daher wird kein Thread für eine unbegrenzte Zeit warten. Wenn es eine Antwort gibt, wird der Thread benachrichtigt, wenn das gleiche Objekt gewartet hat und das zweite Mal benachrichtigt wird.

    
___ tag123clustercomputing ___ Ein Computercluster ist eine Menge verbundener Systeme, die so zusammenarbeiten, dass sie in vielerlei Hinsicht als ein einziges System betrachtet werden können. ___ tag123architecture ___ Architektur umfasst den Prozess, die Artefakte und die High-Level-Struktur einer Lösung. ___ qstntxt ___

Ich arbeite an einem Projekt, in dem wir einen Authentifizierungsmechanismus haben. Wir befolgen die folgenden Schritte im Authentifizierungsmechanismus.

  1. Der Benutzer öffnet einen Browser und gibt seine E-Mail-Adresse in ein Textfeld ein und klickt auf den Login-Button.
  2. Die Anfrage geht an einen Server. Wir generieren eine zufällige Zeichenfolge (z. B. 123456) und senden eine Benachrichtigung an das Android / iPhone und lässt den aktuellen Thread mit Hilfe der %code% -Methode warten.
  3. Der Benutzer gibt ein Passwort auf seinem Telefon ein und klickt auf die Senden-Taste seines Telefons.
  4. Sobald der Benutzer auf den Submit-Button klickt, machen wir einen Webservice, der den Server anspricht und den zuvor generierten String (zum Beispiel 123456) und das Passwort.
  5. übergibt
  6. Wenn das Passwort mit der zuvor eingegebenen E-Mail übereinstimmt, rufen wir die Methode %code% für den zuvor wartenden Thread auf und senden Erfolg als Antwort, und der Benutzer wird in unser System eingegeben.
  7. Wenn das Passwort für die zuvor eingegebene E-Mail falsch ist, rufen wir die %code% -Methode für den zuvor wartenden Thread auf und senden failed als Antwort und zeigen dem Benutzer eine ungültige Anmeldeinformation an.

Alles funktioniert gut, aber kürzlich sind wir in eine Clusterumgebung gewechselt. Wir haben festgestellt, dass einige Threads auch nach einer Antwort des Benutzers und einer unbegrenzten Wartezeit nicht benachrichtigt werden.

Für den Server verwenden wir Tomcat 5.5, und wir folgen The Apache Tomcat 5.5 Servlet / JSP Container für die Tomcat Cluster-Umgebung.

Antwort :: Mögliches Problem und Lösung

Das mögliche Problem sind die mehreren JVMs in einer Clusterumgebung. Jetzt senden wir auch die gruppierte Tomcat-URL zusammen mit der generierten Zeichenfolge an die Android-Anwendung des Benutzers.

Und wenn der Benutzer auf die Antwortschaltfläche klickt, senden wir die generierte Zeichenfolge zusammen mit der gruppierten Tomcat-URL. In diesem Fall werden beide Anfragen an dieselbe JVM gesendet, und es funktioniert einwandfrei.

Aber ich frage mich, ob es eine einzige Lösung für das oben genannte Problem gibt.

Es gibt ein Problem in dieser Lösung. Was passiert, wenn der Clustered-Tomcat abstürzt? Der Load-Balancer sendet eine Anfrage an den zweiten Cluster-Tomcat, und wieder tritt das gleiche Problem auf.

    
___
Luigi R. Viggiano 28.12.2012 01:12
quelle
0

Ich schätze, das Problem ist, dass Ihr erster Thread eine Benachrichtigung an die Android-Anwendung des Benutzers in JVM 1 sendet und wenn der Benutzer antwortet, geht das Steuerelement an JVM 2. Und das ist das Hauptproblem.

Irgendwie können beide Threads auf dieselbe JVM zugreifen, um die Warte- und Benachrichtigungslogik anzuwenden.

    
user1924931 23.12.2012 12:18
quelle
0

Lösung:

Erstellen Sie einen einzigen Kontaktpunkt für alle wartenden Threads. Daher werden in einer Clusterumgebung alle Threads auf eine dritte JVM (Single Point of Contact) warten, so dass auf diese Weise alle Anfragen (alle Clustered Tomcat ) wird dieselbe JVM kontaktieren, um auf die Logik zu warten und sie zu benachrichtigen. Daher wird kein Thread für eine unbegrenzte Zeit warten. Wenn es eine Antwort gibt, wird der Thread benachrichtigt, wenn das gleiche Objekt gewartet hat und das zweite Mal benachrichtigt wird.

    
user1925001 23.12.2012 13:29
quelle