Ibco ist eine C/C++-Coroutine-Bibliothek, die in großem Umfang im WeChat-Backend verwendet wird. Sie läuft seit 2013 stabil auf Zehntausenden von Maschinen im WeChat-Backend. Libco wurde 2013 zum ersten Mal als eines der sechs großen Open-Source-Projekte von Tencent veröffentlicht. Wir haben kürzlich ein großes Update durchgeführt, das auf github.com/tencent/libco synchronisiert ist. libco unterstützt das Backend-Programmiermodell im agilen Synchronisierungsstil und bietet gleichzeitig hohe Parallelitätsfähigkeiten des Systems.
Von libco unterstützte Funktionen
Es besteht keine Notwendigkeit, in die Geschäftslogik einzudringen, Multiprozess- und Multithread-Dienste in Coroutine-Dienste umzuwandeln, und die Parallelitätsfähigkeit wird um das Hundertfache verbessert 🎜> Unterstützt CGI
Framework
, einfach zu erstellende Webdienste (Neu); Unterstützt häufig verwendete dritte Bibliotheken wie gethostbyname, mysqlclient, ssl (Neu); Shared-Stack-Modus, einfacher Standalone-Zugriff auf zig Millionen Verbindungen (Neu);
Perfekte und prägnante Coroutine-Programmierung
Schnittstelle
- pthread-ähnliches Schnittstellendesign, durch einfache und klare Schnittstellen wie co_create und co_resume Vervollständigen Sie die Erstellung und Wiederherstellung der Coroutine – Thread-ähnliche Coroutine private Variable , Coroutine-Semaphor co_signal (Neu) für die Kommunikation zwischen Coroutinen – Lambda auf Nicht-Sprachebene Implementierung, kombiniert mit Coroutine-In-Place-Schreiben und Ausführen von asynchronen Hintergrundaufgaben (Neu); – Ein kleines und leichtes Netzwerk-Framework basierend auf epoll/kqueue, einem Hochleistungs-Timer basierend auf Zeit-Roulette
; >
Generiert von libco Hintergrund In den frühen Tagen des WeChat-Backends verwendeten die meisten Module aufgrund komplexer und sich ändernder Geschäftsanforderungen und schneller Produktiteration ein halbsynchrones und halbasynchrones Modell . Die Zugriffsschicht ist ein asynchrones Modell und die Geschäftslogikschicht ist ein synchrones Multiprozess- oder Multithread-Modell. Die Parallelitätsfähigkeit der Geschäftslogik beträgt nur zehn bis Hunderte. Mit dem Wachstum des Geschäfts von WeChat wird der Systemumfang immer größer und jedes Modul wird leicht durch Back-End-Service-/Netzwerk-Jitter beeinträchtigt.
Die Wahl der asynchronen Transformation
Um die Parallelitätsfähigkeit des WeChat-Backends zu verbessern, besteht der allgemeine Ansatz darin, alle Dienste im bestehenden Netzwerk auf ein asynchrones Modell umzustellen. Dieser Ansatz erfordert einen enormen Arbeitsaufwand, vom Framework bis zum Geschäftslogikcode, der eine vollständige Transformation erfordert, was zeitaufwändig, arbeitsintensiv und riskant ist. Also begannen wir über die Verwendung von Coroutinen nachzudenken. Bei der Verwendung von Coroutinen treten jedoch folgende Herausforderungen auf:
Die Branche hat keine Erfahrung mit der groß angelegten Anwendung von Coroutinen in einer C/C++-Umgebung.
Wie man Coroutinen steuert Planung;
Wie man mit synchronisierten Stil-
API
-Aufrufen wie Socket, MySQLclient usw. umgeht;
Wie man mit der Verwendung vorhandener globaler Variablen und privater Thread-Variablen umgeht ;
Am Ende haben wir alle oben genannten Probleme durch libco gelöst und eine nicht-invasive asynchrone Transformation der Geschäftslogik erreicht. Wir haben libco verwendet, um Hunderte von WeChat-Backend-Modulen in Coroutinen und asynchrone Transformationen umzuwandeln. Während des Transformationsprozesses blieb der Geschäftslogikcode im Wesentlichen unverändert. Bisher handelt es sich bei den meisten Diensten im WeChat-Backend um Multiprozess- oder Multithread-Coroutine-Modelle. Die Parallelitätsfähigkeiten wurden im Vergleich zu früher qualitativ verbessert, und libco ist zum Eckpfeiler des WeChat-Backend-Frameworks geworden. libco-Framework
Libco ist im Framework in drei Schichten unterteilt, nämlich Schnittstellenschicht, System
Funktion
Hook-Schicht und
Ereignis
Treiber
Schicht.
Verarbeitung von APIs im synchronen Stil
Bei APIs im synchronen Stil, hauptsächlich synchronen Netzwerkaufrufen, besteht die Hauptaufgabe von libco darin, die Ressourcenbelegung durch diese Wartezeiten zu beseitigen und die Parallelitätsleistung des Systems zu verbessern. Bei einem regulären Netzwerk-Hintergrunddienst können wir Schritte wie Verbinden, Schreiben, Lesen usw. durchlaufen, um eine vollständige Netzwerkinteraktion abzuschließen. Wenn diese APIs synchron aufgerufen werden, bleibt der gesamte Thread hängen und wartet auf eine Netzwerkinteraktion.
Obwohl die Parallelitätsleistung des synchronen Programmierstils nicht gut ist, bietet er die Vorteile einer klaren Codelogik und eines einfachen Schreibens und kann eine schnelle Geschäftsiteration und eine agile Entwicklung unterstützen. Um die Vorteile der synchronen Programmierung weiterhin aufrechtzuerhalten, ohne den vorhandenen Geschäftslogikcode online zu ändern, hat libco innovativ die Netzwerkaufrufschnittstelle (Hook) übernommen und die Übergabe und Wiederherstellung der Coroutine als Ereignis im asynchronen Netzwerk-IO mit Rückrufen registriert . Wenn die Geschäftsverarbeitung auf eine synchrone Netzwerkanforderung stößt, registriert die Libco-Schicht die Netzwerkanforderung als asynchrones Ereignis. Diese Coroutine gibt die CPU-Belegung auf und die CPU wird zur Ausführung an andere Coroutinen übergeben. Libco setzt die Coroutine-Ausführung automatisch fort, wenn ein Netzwerkereignis auftritt oder eine Zeitüberschreitung auftritt.
Wir haben die meisten APIs im Synchronisierungsstil über die Hook-Methode übernommen, und libco plant die Coroutine so, dass sie die Ausführung zum richtigen Zeitpunkt wieder aufnimmt.
Dutzende Millionen von Coroutinen werden unterstützt
Standardmäßig erlaubt libco, dass jede Coroutine ihren eigenen laufenden Stapel hat. Wenn die Coroutine erstellt wird, wird ein Speicher fester Größe aus dem Heap-Speicher als laufender Stapel zugewiesen für die Coroutine. Wenn wir eine Coroutine verwenden, um eine Zugriffsverbindung am Front-End abzuwickeln, wird bei einem massiven Zugriffsdienst die Parallelitätsbeschränkung unseres Dienstes leicht durch den Speicher begrenzt. Zu diesem Zweck bietet libco auch einen stapellosen Coroutine-Sharing-Stack-Modus, der es Ihnen ermöglicht, mehrere Coroutinen so einzurichten, dass sie denselben laufenden Stack teilen. Beim Wechsel zwischen Coroutinen unter demselben gemeinsam genutzten Stack muss der aktuell ausgeführte Stack-Inhalt in den privaten Speicher der Coroutine kopiert werden. Um die Anzahl solcher Speicherkopien zu reduzieren, erfolgt die Speicherkopie des gemeinsam genutzten Stapels nur beim Wechsel zwischen verschiedenen Coroutinen. Wenn sich der Beleger des gemeinsam genutzten Stapels nicht geändert hat, besteht keine Notwendigkeit, den laufenden Stapel zu kopieren.
Der Shared-Coroutine-Stack-Modus von libco coroutine macht es einer einzelnen Maschine leicht, auf zig Millionen Verbindungen zuzugreifen, indem einfach genügend Coroutinen erstellt werden. Wir erstellen 10 Millionen Coroutinen (E5-2670 v3 bei 2,30 GHz * 2, 128 G Speicher) über den libco-Shared-Stack-Modus. Jede 100.000 Coroutine verwendet 128 KB Speicher, und der gesamte echo-Dienst ist stabil Der Gesamtspeicherverbrauch ist etwa 66G.
Private Coroutine-Variablen
Wenn ein Multiprozessprogramm in ein Multithread-Programm umgewandelt wird, können wir Threads verwenden, um globale Variablen schnell zu ändern. In der Coroutine-Umgebung haben wir die Coroutine-Variable ROUTINE_VAR erstellt, was das Reduzieren erheblich vereinfacht die Arbeitsbelastung der Coroutine-Transformation.
Da Coroutinen im Wesentlichen seriell innerhalb eines Threads ausgeführt werden, kann es beim Definieren einer privaten Thread-Variablen zu Wiedereintrittsproblemen kommen. Wenn wir beispielsweise eine private Thread-Variable eines Threads definieren, wollten wir ursprünglich, dass jede Ausführungslogik exklusiven Zugriff auf diese Variable hat. Wenn unsere Ausführungsumgebung jedoch auf Coroutinen migriert wird, kann dieselbe private Thread-Variable von mehreren Coroutinen bedient werden, was zu dem Problem des Eindringens von Variablen führt. Aus diesem Grund haben wir bei der asynchronen Transformation von libco die meisten privaten Thread-Variablen in private Variablen auf Coroutine-Ebene geändert. Private Coroutine-Variablen weisen die folgenden Merkmale auf: Wenn der Code in einer Multithread-Nicht-Coroutine-Umgebung ausgeführt wird, ist die Variable Thread-privat. Wenn der Code in einer Coroutine-Umgebung ausgeführt wird, ist diese Variable Coroutine-privat. Die zugrunde liegenden privaten Variablen der Coroutine vervollständigen automatisch die Beurteilung der laufenden Umgebung und geben den erforderlichen Wert korrekt zurück.
Private Coroutine-Variablen spielen eine entscheidende Rolle bei der Umstellung der bestehenden Umgebung von Synchronisation auf Asynchronisation. Gleichzeitig haben wir eine sehr einfache und praktische Methode zum Definieren privater Coroutine-Variablen definiert, die so einfach ist wie nur eine Deklarationszeile Code.
Hook-Methode von gethostbyname
Für bestehende Netzwerkdienste kann es erforderlich sein, DNS abzufragen, um die tatsächliche Adresse über die gethostbyname-API-Schnittstelle des Systems zu erhalten. Während der Coroutine-Transformation haben wir festgestellt, dass die Socket-Familienfunktion unseres Hooks nicht auf gethostbyname anwendbar ist. Wenn eine Coroutine gethostbyname aufruft, wartet sie synchron auf das Ergebnis, was dazu führt, dass die Ausführung anderer Coroutinen im selben Thread verzögert wird. Wir haben den gethostbyname-Quellcode von glibc untersucht und festgestellt, dass der Hook hauptsächlich deshalb nicht wirksam wird, weil glibc eine Abfragemethode zum Warten auf Ereignisse anstelle einer allgemeinen Abfragemethode definiert und gleichzeitig auch eine private Thread-Variable zum Umschalten definiert Verschiedene Coroutinen können zu ungenauen Daten führen. Die endgültige Asynchronisierung der gethostbyname-Coroutine wird durch die Hook-Polling-Methode und die Definition privater Coroutine-Variablen gelöst.
Gethostbyname ist eine von glibc bereitgestellte synchrone Abfrage-DNS-Schnittstelle. Es gibt viele hervorragende asynchrone Lösungen für gethostbyname in der Branche, aber diese Implementierungen erfordern die Einführung einer Bibliothek eines Drittanbieters und erfordern, dass die zugrunde liegende Schicht einen asynchronen Rückrufbenachrichtigungsmechanismus bereitstellt . Durch die Hook-Methode realisiert libco die Asynchronisierung von gethostbyname, ohne den Glibc-Quellcode zu ändern.
Coroutine-Semaphor
In einer Multithread-Umgebung müssen wir beispielsweise die Ausführung eines Threads auf das Signal eines anderen Threads warten pthread_signal, um es zu lösen. In libco definieren wir das Coroutine-Semaphor co_signal, um die Parallelitätsanforderungen zwischen Coroutinen zu erfüllen. Eine Coroutine kann entscheiden, eine wartende Coroutine zu benachrichtigen oder alle wartenden Coroutinen über co_cond_signal und co_cond_broadcast aufzuwecken.
Zusammenfassung
Libco ist eine effiziente C/C++-Coroutine-Bibliothek, die eine vollständige Coroutine-Programmierschnittstelle, häufig verwendete Funktions-Hooks der Socket-Familie usw. bereitstellt und es Unternehmen ermöglicht, synchrone Programmiermodelle für eine schnelle iterative Entwicklung zu verwenden. Mit seinem stabilen Betrieb in den letzten Jahren hat libco als Eckpfeiler des Backend-Frameworks von WeChat eine entscheidende Rolle gespielt.
[Verwandte Empfehlungen]
1. Quellcode der WeChat-Plattform herunterladen
2. Quellcode des Alizi-Bestellsystems kostenlos herunterladen
Das obige ist der detaillierte Inhalt vonErfahren Sie, wie libco große Mengen an Dateninformationen unterstützt. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!