Dieser Artikel ist eine kurze Erklärung darüber, wie die Verwendung von Schlüsselwörtern in einer gemeinsamen Sprache das Budget der IT-Infrastruktur eines Projekts beeinflussen oder dazu beitragen kann, einige Einschränkungen / Einschränkungen der Hosting-Infrastruktur zu erreichen, und darüber hinaus die Qualität positiv beeinflusst und Reife des Quellcodes.
Für die Demonstration von Ideen wird in dem Artikel die Sprache C # verwendet, aber die meisten Ideen können in andere Sprachen übersetzt werden.
Aus der Sicht der Sprachfunktionen ist 'Yield' aus meiner Sicht das am meisten unterbewertete Schlüsselwort. Sie können die Dokumentation lesen und im Internet eine Vielzahl von Beispielen finden. Um es kurz zu machen, nehmen wir an, dass 'Yield' das implizite Erstellen von 'Iteratoren' ermöglicht. Ein Iterator sollte standardmäßig eine IEnumerable-Quelle für die öffentliche Verwendung verfügbar machen. Und hier beginnt das Knifflige. Weil wir viele Implementierungen von IEnumerable in der Sprache haben: Liste, Wörterbuch, Hashset, Warteschlange usw. Und meiner Erfahrung nach ist die Wahl eines davon für die Zufriedenheitsanforderungen einer Geschäftsaufgabe falsch. Darüber hinaus wird all dies durch die gewählte Implementierung noch verschärft. Das Programm funktioniert einfach - das ist es, was wirklich für das Geschäft benötigt wird, nicht wahr? Im Allgemeinen funktioniert es, jedoch nur, bis der Dienst in einer Produktionsumgebung bereitgestellt wird.
Zur Demonstration des Problems schlage ich vor, für die meisten Unternehmensprojekte einen sehr häufigen Business Case / Flow zu wählen, den wir während des Artikels erweitern können, und einen Teil dieses Ablaufs zu ersetzen, um das Ausmaß des Einflusses dieses Ansatzes auf Unternehmensprojekte zu verstehen. Und es sollte Ihnen helfen, Ihren eigenen Fall in diesem Set zu finden, um das Problem zu beheben.
Beispiel für die Aufgabe:
- Laden Sie eine Reihe von Datensätzen aus einer Datei oder Datenbank in den Speicher.
- Ändern Sie für jede Spalte des Datensatzes den Wert in einen anderen Wert.
- Speichern Sie die Ergebnisse der Umwandlung in eine Datei oder eine Datenbank.
Nehmen wir mehrere Fälle an, in denen diese Logik anwendbar sein kann. In diesem Moment sehe ich zwei Fälle:
- Dies ist möglicherweise ein Teil des Ablaufs für eine Konsolen-ETL-Anwendung.
- Es ist möglicherweise eine Logik innerhalb der Aktion in Controller der MVC-Anwendung.
Wenn wir die Aufgabe auf eine technischere Art und Weise umschreiben, kann dies folgendermaßen klingen: "(1) Zuweisen einer Speichermenge, (2) Laden von Informationen aus dem Persistenzspeicher in den Speicher, (3) Ändern und (4) Löschen von Datensätzen Änderungen im Speicher des Persistenzspeichers. " Hier kann der erste Satz in der Beschreibung "(1) Zuweisen einer Speichermenge" eine echte Korrelation zu Ihren nicht funktionalen Anforderungen haben. Da Ihr Job / Service in einer Hosting-Umgebung "leben" sollte, die einige Einschränkungen / Einschränkungen aufweisen kann (z. B. 150 MB pro Mikrodienst), und um die Ausgaben für Ihren Service im Budget vorherzusagen, sollten wir in unserem Fall die Speichermenge vorhersagen Welcher Dienst wird verwendet (üblicherweise sagen wir über maximale Speichermengen). Mit anderen Worten, wir sollten einen Speicher-Footprint für Ihren Service ermitteln.
Betrachten wir einen Speicherbedarf für eine wirklich häufige Implementierung, den ich von Zeit zu Zeit in verschiedenen Codebasen von Unternehmensprojekten beobachte. Sie können auch versuchen, es in Ihren Projekten zu finden, z. B. "unter der Haube" der Implementierung von "Repository" -Mustern. Versuchen Sie einfach, die folgenden Wörter zu finden: "ToList", "ToArray", "ToReadonlyCollection" usw. All diese Implementierung bedeutet, dass:
1. Weist für jede Zeile / jeden Datensatz in Datei / Datenbank Speicher zu, um die Eigenschaften des Datensatzes aus Datei / Datenbank zu speichern (dh var user = new User () {Vorname = 'Test', Nachname = 'Test2'})
2. Als nächstes werden mithilfe von beispielsweise 'ToArray' oder manuell die Objektreferenzen in einer Sammlung gespeichert (dh var users = new List (); users.Add (user)). Daher wird jedem Datensatz aus einer Datei eine bestimmte Menge an Speicher zugewiesen. Um dies nicht zu vergessen, wird die Referenz in einer Sammlung gespeichert.
Hier ist ein Beispiel:
private static IEnumerable<User> LoadUsers2() { var list = new List<User>(); foreach(var line in File.ReadLines("text.txt")) { var splittedLine = line.Split(';'); list.Add(new User() { FirstName = splittedLine[0], LastName = splittedLine[1] }); } return list;
Speicherprofilergebnisse:

Genau dieses Bild habe ich jedes Mal in der Produktumgebung gesehen, bevor der Container aufgrund der Ressourcenbeschränkung des Hostings pro Container gestoppt / neu geladen wurde.
Ein Footprint für diesen Fall hängt also ungefähr von der Anzahl der Datensätze in einer Datei ab. Weil der Speicher pro Datensatz in der Datei zugewiesen wird. Und die Summe dieser kleinen Speicherverluste gibt uns eine maximale Speichermenge, die von unserem Dienst verbraucht werden kann - es ist der Fußabdruck des Dienstes. Aber ist dieser Fußabdruck vorhersehbar? Anscheinend nein. Weil wir eine Anzahl von Datensätzen in der Datei nicht vorhersagen können. In den meisten Fällen überschreitet die Dateigröße die beim Hosting zulässige Speichermenge mehrmals. Dies bedeutet, dass es schwierig ist, eine solche Implementierung in der Produktionsumgebung zu verwenden.
Es scheint der Moment zu sein, eine solche Implementierung zu überdenken. Die nächste Annahme bietet uns möglicherweise mehr Möglichkeiten zur Berechnung eines Footprints für den Service: "Ein Footprint sollte von der Größe nur eines Datensatzes in der Datei abhängen." In diesem Fall können wir ungefähr die maximale Größe jeder Spalte von nur einem Datensatz berechnen und summieren. Es ist ziemlich einfach, die Größe eines Datensatzes vorherzusagen, anstatt die Anzahl der Datensätze in der Datei vorherzusagen.
Und es ist wirklich verwunderlich, dass wir einen Dienst implementieren können, der eine unvorhersehbare Menge von Datensätzen verarbeitet und ständig nur ein paar Megabyte verbraucht, mit Hilfe nur eines Schlüsselworts - "Yield" *.Die Zeit für ein Beispiel:
class Program { static void Main(string[] args) {
Wie Sie im obigen Beispiel sehen können, wird jeweils nur für ein Objekt Speicher zugewiesen:'ield return new User () ', anstatt eine Sammlung zu erstellen und diese mit Objekten zu füllen. Dies ist der Hauptpunkt der Optimierung, mit dem wir einen besser vorhersehbaren Speicherbedarf für den Dienst berechnen können. Weil wir nur die Größe von zwei Feldern kennen müssen, in unserem Fall Vorname und Nachname. Wenn ein geänderter Benutzer in einer Datei gespeichert wird (siehe File.AppendAllLines), steht die Instanz des Benutzerobjekts für die Speicherbereinigung zur Verfügung. Und der Speicher, der von dem Objekt belegt wird, wird freigegeben (d. H. Die nächste Iteration der 'foreach'-Anweisung in LoadUsers), so dass die nächste Instanz des Benutzerobjekts erstellt werden kann. Mit anderen Worten, ungefähr wird dieselbe Speichermenge bei jeder Iteration durch dieselbe Speichermenge ersetzt. Aus diesem Grund benötigen wir nicht mehr Speicher als die Größe eines einzelnen Datensatzes in der Datei.
Ergebnisse des Speicherprofilers nach der Optimierung:

Aus einer anderen Perspektive, wenn wir einige Methoden in der obigen Implementierung leicht umbenennen, damit die Verwendung eine sinnvolle Logik für Controller in MVC-Anwendungen erkennen kann:
private static void GetUsersAction() {
Ein wichtiger Hinweis vor der Codeliste: Die meisten wichtigen Bibliotheken wie EntityFramework, ASP.net MVC, AutoMapper, Dapper, NHibernate, ADO.net usw. legen IEnumerables-Quellen offen / verbrauchen sie. Im obigen Beispiel bedeutet dies, dass LoadUsers durch eine Implementierung ersetzt werden können, die beispielsweise EntityFramework verwendet. Womit Daten zeilenweise aus der DB-Tabelle anstelle einer Datei geladen werden. MapToDTO kann durch Automapper ersetzt werden und OkResult kann durch eine "echte" Implementierung von IActionResult in einem MVC-Framework oder unserer eigenen Implementierungsbasis im Netzwerkstrom ersetzt werden, zum Beispiel:
private static void OkResult(IEnumerable<User> users) {
Dieses 'mvc-ähnliche' Beispiel zeigt uns, dass wir auch für Webanwendungen einen Speicherbedarf vorhersagen und berechnen können. In diesem Fall hängt dies jedoch auch von der Anzahl der Anfragen ab. Beispielsweise können die nicht funktionalen Anforderungen folgendermaßen klingen: "Maximale Speichermenge für 1000 Anforderungen nicht mehr als: 200 KB pro Benutzerobjekt x 1000 Anforderungen ~ 200 MB."
Solche Berechnungen sind sehr nützlich für die Leistungsoptimierung bei der Skalierung der Webanwendung. Beispielsweise müssen Sie Ihre Webanwendung auf 100 Container / VMs skalieren. Um in diesem Fall eine Entscheidung darüber zu treffen, wie viele Ressourcen Sie vom Hosting-Anbieter zuweisen sollten, können Sie die Formel wie folgt anpassen: 200 KB pro Benutzerobjekt x 1000 Anforderungen x 100 VM ~ 20 GB. Darüber hinaus ist dies die maximale Speichermenge, die unter der Kontrolle des Projektbudgets steht.
Ich hoffe, dass die Informationen aus diesem Artikel hilfreich sind und viel Geld und Zeit in Ihren Projekten sparen.