Server Archetektur und ICP
Verfasst: Samstag 16. Dezember 2023, 00:59
Hallo,
ich bitte Euch um Rat und Tipps zu einem Thema, an welchem ich jetzt schon seit einiger Zeit am Recherchieren bin. Es geht darum, ein performantes Backend zu designen, die Architektur zu optimieren und später evtl. zu entwickeln. Da es so viele Möglichkeiten gibt dies zu tun, möchte ich versuchen meine Frage etwas zu spezifizieren und meine bisherigen Überlegungen aufzeigen:
Mein Ansatz sieht wie folgt aus, dabei soll zunächst eine horizontale Skalierung vernachlässigt sein. Ich teile die Server Anwendung in verschiedene Prozesse auf:
Socket_Handling_Process (Zustandsbasiert bzgl. der Verbindungen - Sende/Empfangen von Nachrichten via TCP)
Message_Handling_Process (Zustandslos, die Nachrichten sollen nur vermittelt werden)
Caching_Handling_Process (Zustandsbasiert bzgl. der Anwendungszustände)
Database_Handlin_Process (Datenbank I/O, Schreiben, Lesen in die Datenbank)
.
.
Other_Processes (Dies könnten andere Prozesse sein, welche Aufgaben, Berechnungen usw. erledigen oder eine Schnittstelle zu anderer Software bereitstellen)
Kurze Erklärung, warum ich die Aufteilung zu Prozessen mache und nicht alles in einer asynchronen Implementierung mittels asyncio:
1. Auch wenn eigentlich asyncio für I/O Bound der primäre Ansatz ist, so denke ich mir, könnte später evtl. über Message Queues oder Channel (Redis) skaliert werden, je nachdem wie hoch die Kosten der Synchronisation sind, falls diese notwendig werden sollte (Zustände usw.).
2. Eine moderne CPU hat mehrere Kerne, die ich gerne nutzen möchte.
Innerhalb mancher Prozesse könnten asynchrone Tasks bzw. eigenständige Event_Loops laufen, falls dies sinnvoll ist, zB. bei den Socket Verbindungen. Ich bin mir gerade nicht sicher, wie das Pythonmodul "sockets", low level technisch implementiert ist, also selectors/epoll ... müsste ich nachsehen ... Worauf ich aber hinaus möchte ist, dass es durchaus Sinn ergeben könnte, wenn innerhalb des Socket_Handling_Processes, das Empfangen und Senden asynchron erfolgt. Also in meinem Konzept sieht das dann so aus, dass Nachrichten empfangen und diese in eine Queue gespeichert werden, andersherum beim Versenden. Ich glaube auch, dass dies ein Vorteil ist, weil die Daten dann, je nachdem wann diese vom Socket empfangen wurden, gleich in der richtigen Reihenfolge serialisiert werden und man braucht sich auch keine weiteren Gedanken über die Synchronisation der Daten zwischen den Prozessen zu machen.
Nun zu meinen Fragen:
- Wie beurteilt ihr diese Architektur, gibt es Verbesserungsvorschläge?
- Würdet Ihr Multiprocessing und AsyncIO kombinieren, wenn dies Sinn macht? Würde dies in meinem Szenario sinnvoll sein?
- Welche Technik sollte man für die Interprocess Kommunikation verwenden? Qeues, Redis, Kafka ... Wobei Redis eher Subscriber/Publisher Channels sind und diese können auch für IPC verwendet werden, das habe ich mal getestet, allerdings habe ich keine Benchmarks gemacht. Bei den Queues könnte man zum Beispiel q.put(var, non_blocking) verwenden, das wäre dann mit asyncio kompatibel.
Ich hoffe, hier kann mir weiter geholfen werden, bitte beachtet, dass es sich hier mehr um ein hypothetisches Konzept handelt, wobei ich versuche mein Verständnis etwas weiter auszubauen.
ich bitte Euch um Rat und Tipps zu einem Thema, an welchem ich jetzt schon seit einiger Zeit am Recherchieren bin. Es geht darum, ein performantes Backend zu designen, die Architektur zu optimieren und später evtl. zu entwickeln. Da es so viele Möglichkeiten gibt dies zu tun, möchte ich versuchen meine Frage etwas zu spezifizieren und meine bisherigen Überlegungen aufzeigen:
Mein Ansatz sieht wie folgt aus, dabei soll zunächst eine horizontale Skalierung vernachlässigt sein. Ich teile die Server Anwendung in verschiedene Prozesse auf:
Socket_Handling_Process (Zustandsbasiert bzgl. der Verbindungen - Sende/Empfangen von Nachrichten via TCP)
Message_Handling_Process (Zustandslos, die Nachrichten sollen nur vermittelt werden)
Caching_Handling_Process (Zustandsbasiert bzgl. der Anwendungszustände)
Database_Handlin_Process (Datenbank I/O, Schreiben, Lesen in die Datenbank)
.
.
Other_Processes (Dies könnten andere Prozesse sein, welche Aufgaben, Berechnungen usw. erledigen oder eine Schnittstelle zu anderer Software bereitstellen)
Kurze Erklärung, warum ich die Aufteilung zu Prozessen mache und nicht alles in einer asynchronen Implementierung mittels asyncio:
1. Auch wenn eigentlich asyncio für I/O Bound der primäre Ansatz ist, so denke ich mir, könnte später evtl. über Message Queues oder Channel (Redis) skaliert werden, je nachdem wie hoch die Kosten der Synchronisation sind, falls diese notwendig werden sollte (Zustände usw.).
2. Eine moderne CPU hat mehrere Kerne, die ich gerne nutzen möchte.
Innerhalb mancher Prozesse könnten asynchrone Tasks bzw. eigenständige Event_Loops laufen, falls dies sinnvoll ist, zB. bei den Socket Verbindungen. Ich bin mir gerade nicht sicher, wie das Pythonmodul "sockets", low level technisch implementiert ist, also selectors/epoll ... müsste ich nachsehen ... Worauf ich aber hinaus möchte ist, dass es durchaus Sinn ergeben könnte, wenn innerhalb des Socket_Handling_Processes, das Empfangen und Senden asynchron erfolgt. Also in meinem Konzept sieht das dann so aus, dass Nachrichten empfangen und diese in eine Queue gespeichert werden, andersherum beim Versenden. Ich glaube auch, dass dies ein Vorteil ist, weil die Daten dann, je nachdem wann diese vom Socket empfangen wurden, gleich in der richtigen Reihenfolge serialisiert werden und man braucht sich auch keine weiteren Gedanken über die Synchronisation der Daten zwischen den Prozessen zu machen.
Nun zu meinen Fragen:
- Wie beurteilt ihr diese Architektur, gibt es Verbesserungsvorschläge?
- Würdet Ihr Multiprocessing und AsyncIO kombinieren, wenn dies Sinn macht? Würde dies in meinem Szenario sinnvoll sein?
- Welche Technik sollte man für die Interprocess Kommunikation verwenden? Qeues, Redis, Kafka ... Wobei Redis eher Subscriber/Publisher Channels sind und diese können auch für IPC verwendet werden, das habe ich mal getestet, allerdings habe ich keine Benchmarks gemacht. Bei den Queues könnte man zum Beispiel q.put(var, non_blocking) verwenden, das wäre dann mit asyncio kompatibel.
Ich hoffe, hier kann mir weiter geholfen werden, bitte beachtet, dass es sich hier mehr um ein hypothetisches Konzept handelt, wobei ich versuche mein Verständnis etwas weiter auszubauen.