Plattformübergreifende zugrunde liegende Bibliothek https://github.com/hujianzhe/util, herunterladen und im BootServer-Verzeichnis ablegen
Einführung:
Der Code implementiert nur das Starten des Serviceknotens, die Aufgabenplanung und die grundlegende Modulbeschreibung. Er ist in reinem C implementiert. Er wird im Allgemeinen in eine dynamische Bibliothek kompiliert und bleibt hinsichtlich der Nutzungsfunktionen äußerst eingeschränkt. Es implementiert einige gängige gemeinsame Protokollflüsse und unterstützt C++. 20 Erweiterung der stapellosen Coroutine und isoliert von einigen Codes der reinen dynamischen C-Bibliothek, um zu verhindern, dass C++ die Framework-Ebene kontaminiert und die Generierung dynamischer Bibliotheken beeinträchtigt
Der Code in util ist für die plattformübergreifende Nutzung verantwortlich, erfordert jedoch keine Installation von Bibliotheken von Drittanbietern
Einführung in den Betriebsprozess:
Der Geschäftsprozess ruft die vom Code kompilierte dynamische Bibliothek auf und ruft die entsprechende Schnittstelle zur Verwendung auf. Weitere Informationen zum aufrufenden Prozess finden Sie im Testknotenbeispiel (main_template im BootServer-Verzeichnis ist eine Reihe prozessbasierter Startcodevorlagen).
Mehrere Threads verarbeiten das Lesen und Schreiben von Netzwerk-E/A innerhalb des Moduls. Ein separater Akzeptierungszugriffsthread öffnet zunächst einen Arbeitsthread, um interne Nachrichten und empfangene Netzwerknachrichten zu verarbeiten und sie an Ihre Geschäftscodelogik weiterzuleiten. Der Arbeitsthread verwendet eine Stapel-Coroutine zur Planung der Verarbeitung (siehe unten zum Grund, warum keine stapellose Coroutine verwendet wird).
Worker-Threads verwenden standardmäßig Stack-Coroutinen für die Planung, um die höchste Kompatibilität zu gewährleisten. Sie können auch problemlos stapellose C++20-Coroutinen (eine in der Util-Bibliothek implementiert) verwenden. Es gibt Stack-Coroutinen und stapellose Coroutinen
Moduleinführung und Beispielcode:
1. BootServer: Hauptcodeteil, notwendige Initialisierung und Betrieb von Serviceknoten
2. ServiceTemplate: Serviceknoten-Codevorlage, die zum Schreiben Ihrer Geschäftslogik verwendet wird
3. SoTestClient, SoTestServer: Testknoten und einige Beispielcodes geschrieben
4. Cpp20-SoTestServer: Knoten testen, Beispielcode geschrieben, stapellose C++20-Coroutine in main.cpp aktiviert (unter Verwendung eines stapellosen C++20-Coroutine-Schedulers in der Util-Bibliothek)
Kompilieren:
Windows Direct VS-Kompilierung
Verwenden Sie make debug / make release / make asan unter Linux/Mac OS X
Start-up:
Bearbeiten Sie die für den Start des Dienstknotens erforderliche Konfigurationsdatei (das spezifische Format finden Sie in der beigefügten Konfigurationsdateivorlage) und geben Sie jedem Knoten eine Konfigurationsdatei und eine eindeutige ID, einen Protokollidentifikationsnamen, eine IP-Adresse und eine Portnummer.
Windows wird direkt in VS geöffnet und das Projekt wird mit den Startparametern <Konfigurationsdatei> konfiguriert
Nachdem Linux/Mac kompiliert wurde, sh run.sh <Dienstprozess> <Konfigurationsdatei>
Einige Designgründe und Erkenntnisse: F: Warum nicht stapellose, sondern gestapelte Coroutinen verwenden? A: Es ist einfach, stapellose Coroutinen in reinem C zu implementieren (für detaillierten Code können Sie den in reinem C implementierten stapellosen Coroutine-Scheduler in der Util-Bibliothek sehen), aber Ressourcenrecycling und Persistenz (insbesondere Variablen auf dem Stapel unterliegen Coroutine-Re). (letztere Situation) ist äußerst schwierig Wenn Sie stapellose Coroutinen reibungslos verwenden möchten, müssen Sie sich weiterhin auf die Compiler-Unterstützung verlassen. Dies wurde in C++20 erreicht. In der Util-Bibliothek gibt es auch eine vollständige C++20-Stackless-Coroutinen-Implementierung.
F: Warum stellt die stapellose Coroutine diese erweiterte Funktion in Form einer Header-Datei bereit?
A: 1. Weil stapellose Coroutinen in den Code eingreifen und eine große Anzahl von Funktionssignaturformen ändern
2. Enthält C++-Objekte, die in C nicht erkannt werden, und die dynamische Bibliothek kann nicht erfolgreich exportiert werden.
3. Die Übergabe der Aktivierungsberechtigung für stapellose Coroutinen an die Anwendungsschicht, die in Form einer Header-Datei erfolgt, ist meiner Meinung nach derzeit die Art und Weise, wie ich mit dem rein dynamischen C-Bibliotheksteil ohne jegliche Verschmutzung umgehen kann.
F: Kann es durch andere Planer ersetzt werden?
A: Der Scheduler des Worker-Threads kann als laufender Träger des Schedulers konzipiert werden. Die Interaktion zwischen dem Netzwerk-Thread und dem Worker-Thread innerhalb des Frameworks kann dem Planungsverhalten über den Schnittstellen-Hook entsprechen.
F: Kann es durch andere Netzwerkbibliotheken ersetzt werden?
A: 1. Es kann derzeit nicht durch andere Netzwerkbibliotheken ersetzt werden, aber dieses Problem wurde zu Beginn des Entwurfs berücksichtigt. Der Netzwerkteil und der Aufgabenplanungsteil werden in Zukunft vollständig getrennt, ähnliche Verhaltensweisen wie bei Worker-Threads Haken werden zum Austausch zur Verfügung gestellt.
2. Obwohl es viele Netzwerkbibliotheken von Drittanbietern gibt, sind ihre Schwerpunkte unterschiedlich. Es gibt TCP- und UDP-Bibliotheken, die für die Anwendungsentwicklung verwendet werden, es gibt auch Bibliotheken, die das gesamte Universum einbeziehen möchten, und es gibt auch Bibliotheken, die den gesamten Netzwerkprotokollstapel auf der Anwendungsebene implementieren, sodass dieser Bereich tatsächlich nicht einheitlich ist.
3. Wenn in Zukunft eine Reihe von Netzwerkbibliotheken in den Standard aufgenommen werden, werde ich sie ersetzen.
F: Warum nicht einfach C++ als Framework verwenden?
A: 1. Als dieser Codesatz geschrieben wurde, war C++20 noch nicht erschienen. Die relativ ausgereifteste Coroutine-Lösung war Stack-Coroutine. Dies konnte mit C durch Aufrufen der entsprechenden Plattformsystem-API erfolgen.
2. Die von dieser Art von Framework zu implementierenden Funktionen wurden gefestigt, und der Lebenszyklus der Ressourcen wird durch den Prozess gefestigt. Es reicht aus, sie nur in reinem C zu implementieren (es gab zuvor eine in C++ implementierte Version). Code war komplizierter)
3. Wenn die dynamische Bibliothek von anderen Modulen aufgerufen wird, muss sie dennoch eine Schnittstelle in C versiegeln
4. Von C++ exportierte Klassen sind ansteckend und der ABI ist nicht einheitlich
F: Kann die Business-Schicht weiterhin mit reinem C entwickelt werden?
A: Es wird dringend davon abgeraten, da das Schreiben asynchroner Prozesse und ausgelöster Ausnahmen in in Hochsprachen geschriebenen Modulen den Zeitpunkt der Ressourcenzerstörung unsicher macht. Derzeit ist es äußerst schwierig, Ressourcen mit reinem C manuell zu steuern Sie sollten eine übergeordnete Sprache verwenden, um diese Dinge zu handhaben. Sie können beispielsweise C++ verwenden, um Geschäftscode der oberen Ebene zu entwickeln, und sein RAII-Mechanismus kann die Freigabe entsprechender Ressourcen sicherstellen.
F: Wenn ich den „Callback“ des alten Projekts in eine „Coroutine“ umwandeln möchte, kann ich dann einfach den Scheduler-Teil durch den entsprechenden Callpoint im Geschäftscode ersetzen?
A: Das erste ist das Problem der Arbeitslast. Unabhängig davon, ob es sich um eine Rückrufform oder eine Coroutine handelt, besteht das Wesentliche darin, während dieses Zeitraums eine Anfrage zu stellen und auf das Ergebnis zu warten ist ein Problem, das gelöst und sorgfältig geprüft werden muss. Wenn es sich um einen Rohzeiger handelt, kann man fast sagen, dass er nicht transformiert werden kann, wenn das Projekt verwendet wurde Mittel wie std::shared_ptr verlängern den Lebenszyklus von Variablen, sodass die Umwandlung in eine „Stack-Coroutine“ weniger schwierig ist. Wenn Sie in eine stapellose C++20-Coroutine umwandeln möchten, ist der Arbeitsaufwand enorm ist gleichbedeutend mit dem Umschreiben des Projekts (da stapellose Coroutinen einen starken Codeeingriff aufweisen), daher wird empfohlen, alte Projekte nicht zu transformieren.
F: Warum darf Coroutine derzeit nicht zur Ausführung in andere Threads migriert werden?
A: Die Migration von Coroutinen kann problemlos durchgeführt werden, der Grund, warum sie jedoch nicht bereitgestellt wird, ist
1. Die zwischen Coroutinen ausgeführten Aufgaben sind unsicher, was dazu führen kann, dass io und Berechnung im selben Planungsthread gemischt werden.
2. Nach der Migration kann derselbe Coroutine-Prozess in verschiedenen Threads ausgeführt werden. In diesem Fall müssen Sie sicherstellen, dass Ihr Code nicht auf lokalen Thread-Variablen basiert. Sie können jedoch nicht sicherstellen, dass die Bibliothek eines Drittanbieters keine lokalen Thread-Variablen verwendet .
F: Wird asan beim Kompilieren und Ausführen abstürzen?
A: 1. Wenn Sie die standardmäßige gestapelte Coroutine des Frameworks verwenden, handelt es sich definitiv nicht um ein Codeproblem. Sie können die Stapelgröße der gestapelten Coroutine in der Knotenkonfigurationsdatei anpassen (wenn ASAN aktiviert ist, wird relativ viel Stapelspeicherplatz verbraucht). es besteht also die Möglichkeit einer Stapelexplosion)
2. ASAN unterstützt die gestapelte Coroutine-API (ucontext) in der Unix-Umgebung nicht vollständig. Obwohl die ucontext-Coroutine-API im Util-Code an ASAN angepasst wurde, kann dennoch nicht zu 100 % garantiert werden, dass es im ASAN zu keinen Problemen kommt Umgebung (soweit so gut)
3. Wenn ASAN in einigen neueren Linux-Distributionen „AddressSanitizer: DEADLYSIGNAL“ unendlich ausgibt, passen Sie „sudo sysctl vm.mmap_rnd_bits=28“ an, um das Problem zu lösen.
TODO:
1. Ich habe wirklich keine Zeit, eine detaillierte Dokumentation zu schreiben.
2. Bieten Sie Unterstützung beim Schreiben von Geschäftslogik in Skriptsprache