Hintergrund
Ich arbeite an einer Actix-Webanwendung mit Diesel über r2d2 und bin mir nicht sicher, wie ich am besten asynchrone Abfragen durchführen kann. Ich habe drei Optionen gefunden, die vernünftig erscheinen, bin mir aber nicht sicher, welche die beste ist.
Potentielle Lösungen
Schauspieler synchronisieren
Zum einen könnte ich das Actix-Beispiel verwenden , aber es ist ziemlich kompliziert und erfordert einiges an Boilerplate, um es zu bauen. Ich hoffe, es gibt eine vernünftigere Lösung.
Actix_web::web::block
Als weitere Option könnte ich actix_web::web::block
meine Abfragefunktionen in eine Zukunft einbinden, bin mir jedoch nicht sicher, welche Auswirkungen dies auf die Leistung hat.
Läuft die Abfrage dann im selben Tokio-System? Nach dem, was ich in der Quelle finden konnte, wird ein Thread im zugrunde liegenden Actix-Web-Threadpool erstellt . Ist das ein Problem?
Wenn ich den Code richtig lese, blockiert r2d2 seinen Thread beim Erwerb einer Verbindung, wodurch ein Teil des zentralen Actix-Web-Pools blockiert wird. Gleiches gilt für Datenbankabfragen. Dies würde dann das gesamte actix-web blockieren, wenn ich mehr Abfragen mache, als ich Threads in diesem Pool habe? Wenn ja, großes Problem.
Futures-cpupool
Schließlich ist futures-cpupool die sichere Wette, die möglicherweise unnötigen Overhead verursacht . Das Hauptproblem ist, dass dies bedeutet, meinem Projekt eine weitere Kiste hinzuzufügen, obwohl mir die Idee, dass mehrere CPU-Pools unnötig in meiner Anwendung herumschweben, nicht gefällt.
Da sowohl r2d2 als auch Diesel blockieren, gibt es hier überraschend viele knifflige Dinge.
Teilen Sie diesen cpupool vor allem nicht mit Benutzern, die nicht denselben r2d2-Pool verwenden (da alle erstellten Threads möglicherweise nur das Warten auf eine r2d2-Verbindung blockieren und den gesamten Pool sperren, wenn Arbeit vorhanden ist).
Zweitens (etwas offensichtlicher) sollten Sie daher nicht mehr r2d2-Verbindungen als Threads im Pool haben und umgekehrt, da der größere Ressourcen verschwenden würde (nicht verwendete Verbindungen / ständig blockierte Threads) (vielleicht ein weiterer Thread, vielleicht schneller Verbindungsübergabe durch den OS-Scheduler und nicht durch den cpupool-Scheduler.
Denken Sie schließlich daran, welche Datenbank Sie verwenden und welche Leistung Sie dort haben. Das Ausführen einer einzelnen Verbindung r2d2 und eines einzelnen Threads im Pool ist möglicherweise am besten in einer schreiblastigen SQLite-Anwendung (obwohl ich eine geeignete Datenbank für eine solche empfehlen würde).
Alte Antworten
Alte Lösungen, die funktionieren könnten
https://www.reddit.com/r/rust/comments/axy0hp/patterns_to_scale_actixweb_and_diesel/
Im Wesentlichen empfiehlt Futures-cpupool.
Was ist der beste Ansatz, um blockierende E / A in zukünftigen Daten zu kapseln?
Empfiehlt Futures-cpupool für allgemeine Fälle.
Alte Lösungen, die nicht funktionieren
https://www.reddit.com/r/rust/comments/9fe1ye/noob_here_can_we_talk_about_async_and_databases/
Eine wirklich schöne Lösung für eine alte Actix-Web-Version. Nach allem, was ich finden kann, haben Anfragen keinen CPU-Pool mehr.
quelle
futures-cpupool
die empfohlene Problemumgehung für die mangelndeasync
Unterstützung von Diesel ist.Antworten:
Ich gehe mit Futures-Cpupool. Es ist die beste Lösung, da meine Interaktionen blockierend sind.
Die Verwendung von actix_web :: web :: block ist anständig genug, verwendet jedoch einen gemeinsam genutzten Thread-Pool in actix (und aufgrund der von mir verwendeten blockierenden Aufrufe kann dies den gesamten Thread-Pool blockieren und andere Aufgaben von actix_web stören).
Es ist besser, futures-cpupool zu verwenden, um einen separaten Threadpool pro Datenbank nur für Datenbankinteraktionen zu erstellen. Auf diese Weise gruppieren Sie alle Aufgaben, die aufeinander warten müssen (wenn mehr Aufgaben als Verbindungen vorhanden sind), in einem Pool, um zu verhindern, dass andere Aufgaben blockiert werden, für die keine Verbindung erforderlich ist, und begrenzen möglicherweise die Anzahl der Threads auf die Anzahl der Verbindungen (damit die Aufgabe nur geplant wird, wenn sie nicht blockiert wird).
In dem Fall, dass Sie nur eine Datenbankverbindung (oder nur sehr wenige) verwenden möchten, ist der Synchronisierungsakteur eine ziemlich gute Option. Es verhält sich wie ein Futures-cpupool mit einem Thread und stellt sicher, dass alle Aufgaben einzeln ausgeführt werden, außer dass einer der zugrunde liegenden Threads von actix-web anstelle eines separaten Threads verwendet wird (daher nur für sehr wenige Verbindungen geeignet). . Ich finde das Boilerplate jedoch zu groß, um es wert zu sein.
quelle