Heim >PHP-Framework >Swoole >Eine kurze Analyse des Swoole-Servers

Eine kurze Analyse des Swoole-Servers

coldplay.xixi
coldplay.xixinach vorne
2021-03-11 09:52:081976Durchsuche

Eine kurze Analyse des Swoole-Servers

1. Grundkenntnisse

1.1 Swoole

Swoole ist eine asynchrone PHP-Netzwerkkommunikations-Engine für Produktionsumgebungen, mit der PHP-Entwickler leistungsstarke Serverdienste entwickeln können. Der Serverteil von Swoole ist umfangreich und beinhaltet viele Wissenspunkte. Dieser Artikel gibt nur einen kurzen Überblick über den Server. Die spezifischen Implementierungsdetails werden in den folgenden Artikeln ausführlich vorgestellt.

Empfohlen (kostenlos): swoole

1.2 Netzwerkprogrammierung

1. Netzwerkkommunikation bezieht sich auf das Starten eines (oder mehrerer) Prozesse auf einer (oder mehreren) Maschinen, die Überwachung eines (oder mehrerer) Ports, Informationen mit dem Client gemäß einem bestimmten Protokoll austauschen (es kann das Standardprotokoll http, dns sein; es kann auch ein selbstdefiniertes Protokoll sein).

2. Der Großteil der aktuellen Netzwerkprogrammierung basiert auf TCP-, UDP- oder höherschichtigen Protokollen. Der Serverteil von Swoole basiert auf den Protokollen TCP und UDP.

3. Die Programmierung mit udp ist relativ einfach. In diesem Artikel wird hauptsächlich die Netzwerkprogrammierung basierend auf dem TCP-Protokoll vorgestellt.

4. Die TCP-Netzwerkprogrammierung umfasst hauptsächlich 4 Arten von Ereignissen.

Verbindungsaufbau: Bezieht sich hauptsächlich auf den Client Initiieren einer Verbindung (Verbinden) und der Server akzeptiert die Verbindung (Akzeptieren)

Eintreffen der Nachricht: Der Server empfängt die vom Client gesendeten Daten Bei dieser Art von Ereignis können Sie blockierende oder nicht blockierende Ereignisse verwenden. Darüber hinaus muss der Server auch Probleme wie Paketierung und Anwendungsschichtpuffer berücksichtigen. Erfolgreiches Senden bedeutet, dass die Anwendungsschicht Die Daten werden erfolgreich an den Socket-Sendepuffer des Kernels gesendet. Dies bedeutet nicht, dass der Client die Daten erfolgreich akzeptiert hat. Bei Diensten mit geringem Datenverkehr können die Daten in der Regel auf einmal gesendet werden, und es besteht kein Grund, sich um solche Ereignisse zu kümmern. Wenn nicht alle Daten auf einmal an den Kernelpuffer gesendet werden können, müssen Sie darauf achten, ob die Nachricht erfolgreich gesendet wird (bei Blockprogrammierung ist das Senden erfolgreich, nachdem der Systemaufruf (write, writev, send usw.) zurückgegeben wurde. Bei der nicht blockierenden Programmierung müssen Sie die tatsächliche Situation berücksichtigen. Ob die geschriebenen Daten mit den Erwartungen übereinstimmen. (Schließen, Herunterfahren)

5. TCP stellt Verbindung her. Der Vorgang ist wie unten dargestellt: ACK und SYN stellen Flag-Bits dar, seq und ack sind die Sequenznummer und Bestätigungssequenznummer des TCP-Pakets 6. Der Prozess der TCP-Verbindungstrennung ist wie unten dargestellt: ● Die obige Abbildung berücksichtigt die Situation, in der der Server die Verbindung aktiv trennt. ● In der Abbildung stellen FIN und ACK dar Flag-Bits, Seq und Ack sind die Sequenznummer und Bestätigungssequenznummer des TCP-Pakets (Signal), Semaphor (Semaphor), Sockets (Socket), Shared Memory (Shared Memory) und andere Methoden

2 Swoole verwendet Unix-Domain-Sockets (eine Art Socket) für die Kommunikation zwischen mehreren Prozessen (bezogen auf interne Swoole-Prozesse).

1.4 Socketpair wird verwendet, um ein Socketpaar zu erstellen, ähnlich wie bei Pipe. Der Unterschied besteht darin, dass eine bidirektionale Kommunikation erstellt werden muss Die Kommunikation kann durch einen einmaligen Aufruf von socketpair erreicht werden. Da es sich um einen Socket handelt, können Sie auch die Methode des Datenaustauschs definieren

2. Der Aufruf des Socketpair-Systems ist erfolgreich. sv[0], sv[1] speichern jeweils einen Dateideskriptorin sv[0] Schreiben Sie in sv[1], Sie können von sv[1] lesenSchreiben Sie in sv[1], Sie können von sv lesen [0]

Nachdem der Prozess socketpair aufgerufen hat, gibt er den untergeordneten Prozess ab und der untergeordnete Prozess erbt standardmäßig sv. Die beiden Dateideskriptoren [0] und sv[1] können die Kommunikation zwischen übergeordneten und untergeordneten Prozessen realisieren. Beispielsweise schreibt der übergeordnete Prozess in sv[0] und der untergeordnete Prozess liest von sv[1]; der untergeordnete Prozess schreibt in sv[1] und der übergeordnete Prozess liest von sv[0]

1.5 Daemon

1. Der Daemon ist ein spezieller Hintergrundprozess, der vom Terminal getrennt ist und zur regelmäßigen Ausführung bestimmter Aufgaben verwendet wird

Jeder Prozess gehört zu einer Prozessgruppe

Jede Prozessgruppe hat eine Prozessgruppennummer, die die Prozessnummer (PID) des Gruppenleiters ist. Ein Prozess kann die Prozessgruppennummer nur für sich selbst oder seine untergeordneten Prozesse festlegen

  • Eine Sitzung kann höchstens eine Vordergrundprozessgruppe (oder keine) in diesen Prozessgruppen enthalten, und der Rest sind Hintergrundprozessgruppen.
  • Eine Sitzung kann höchstens ein Steuerterminal haben
  • Benutzer können das Terminal über die Terminal-Anmeldung oder Netzwerkanmeldung verwenden, um eine neue Sitzung zu erstellen.
  • Der Prozess, der setsid aufruft, kann nicht der Leiter einer bestimmten Prozessgruppe sein. Nachdem der Aufruf von „setsid“ abgeschlossen ist, wird der Prozess zum ersten Prozess (Leitprozess) der Sitzung und zum Leiter einer neuen Prozessgruppe. Wenn der Prozess zuvor über ein steuerndes Terminal verfügte, wird auch die Verbindung des Prozesses mit dem Terminal getrennt
  • 4. So erstellen Sie einen Daemon-Prozess

Nach dem Forken des untergeordneten Prozesses wird der übergeordnete Prozess beendet, der untergeordnete Prozess führt setsid aus und der untergeordnete Prozess kann zu einem Daemon-Prozess werden. Auf diese Weise ist der untergeordnete Prozess der führende Prozess der Sitzung und kann das Terminal erneut öffnen. Zu diesem Zeitpunkt kann der durch fork generierte untergeordnete Prozess das Terminal nicht mehr öffnen (nur der führende Prozess der Sitzung). Öffnen Sie das Terminal). Der zweite Fork ist nicht notwendig, nur um zu verhindern, dass der untergeordnete Prozess das Terminal erneut öffnet
  • linux stellt die Daemon-Funktion (diese Funktion ist kein Systemaufruf, sondern eine Bibliotheksfunktion) zum Erstellen eines Daemon-Prozesses bereit
1.6 Swoole Beispiel für einen TCP-Server

Wenn der obige Code im CLI-Modus ausgeführt wird, wird Opcode durch lexikalische Analyse und Syntaxanalyse generiert und dann zur Ausführung an die virtuelle Zend-Maschine übergeben.
  • Wenn die virtuelle Zend-Maschine $ ausführt serv->start(), Swoole-Server starten
  • Der im obigen Code festgelegte Ereignisrückruf wird im Worker-Prozess ausgeführt. Das Swoole-Servermodell wird später im Detail vorgestellt.
  • Der Basismodus verwendet einen Multiprozess Modell, das mit Nginx übereinstimmt. Der Hauptprozess ist für die Verwaltung des Arbeitsprozesses verantwortlich, und der Arbeitsprozess ist für das Abhören des Ports, das Akzeptieren von Verbindungen, das Verarbeiten von Anforderungen und das Schließen von Verbindungen verantwortlich Prozesse lauschen gleichzeitig auf Ports, es wird ein Thundering-Group-Problem geben, das Swoole nicht löst Mehrere Prozesse müssen an denselben Port gebunden werden. Wenn der Kernel eine neue Verbindungsanforderung empfängt, wird einer von ihnen zur Verarbeitung aktiviert. Auf der Kernelebene wird auch ein Lastausgleich durchgeführt, wodurch das oben erwähnte Problem der Donnergruppe gelöst werden kann hat auch diesen Parameter hinzugefügt

Basismodus Unten hat der Parameter „reaktor_number“ keine praktische Wirkung

Wenn die Anzahl der Arbeitsprozesse auf 1 gesetzt ist, wird der Arbeitsprozess nicht ausgespalten und der Hauptprozess verarbeitet die Anfrage direkt Der Modus eignet sich zum Debuggen

2. Startvorgang

php-Codeausführung Wenn $serv->start() erreicht ist, ruft der Hauptprozess die Funktion int swServer_start(swServer *serv) auf, die für den Start des Servers verantwortlich ist

In der Funktion swServer_start wird swReactorProcess_start aufgerufen. Diese Funktion verteilt mehrere Arbeitsprozesse. Der Master-Prozess und der Arbeitsprozess treten jeweils in eine eigene Ereignisschleife ein, um verschiedene Ereignisse zu verarbeiten. 2.2-Prozessmodus. Beschreibung

    Dieser Modus ist ein Multiprozess- und Multithread-Modus mit einem Hauptprozess, einem Managerprozess und einem Arbeitsprozess. Der Task_Worker-Prozess verfügt über mehrere Threads unter dem Hauptprozess. Der Hauptthread ist für die Annahme von Verbindungen verantwortlich und dann an den Reaktionsthread übergeben, um die Anfrage zu verarbeiten. Der Reaktionsthread ist dafür verantwortlich, Datenpakete zu empfangen, die Daten zur Verarbeitung an den Arbeitsprozess weiterzuleiten und dann die vom Arbeitsprozess zurückgegebenen Daten zu verarbeiten. Dieser Prozess ist ein einzelner Thread und hauptsächlich für die Verwaltung des Arbeitsprozesses verantwortlich , ähnlich dem Hauptprozess in Nginx. Wenn der Worker-Prozess abnormal beendet wird, ist der Manager-Prozess für die Neuverzweigung eines Worker-Prozesses verantwortlich. Dieser Prozess ist ein Single-Thread-Prozess und ist für die spezifische Verarbeitung von Anforderungen verantwortlich Prozess. Es wird standardmäßig nicht aktiviert. Der Prozess kommuniziert mit dem React-Thread im Hauptprozess und es findet keine Kommunikation zwischen Worker-Prozessen statt Prozess
  • Swoole-Server-Starteintrag: swServer_start-Funktion
  • falls festgelegt Im Daemon-Modus verwandelt er sich nach Überprüfung der erforderlichen Parameter zunächst in einen Daemon-Prozess, teilt dann den Manager-Prozess auf und erstellt dann einen Reaktor Thread

Der Hauptprozess verzweigt zunächst den Manager-Prozess, und der Manager-Prozess ist für die Verzweigung des Worker-Prozesses und des task_worker-Prozesses verantwortlich. Nachdem der Worker-Prozess in int swWorker_loop

(swServer *serv, int worker_id) eintritt, d Thread, Hauptthread und React Die Threads betreten jeweils ihre eigene Ereignisschleife. Der Reaktorthread führt static int swRea-torThread_loop (swThreadParam *param) aus und wartet auf die Verarbeitung von Ereignissen
  • 3
    • Die Struktur des Swoole-Prozesses ist in der folgenden Abbildung dargestellt:

    Die obige Abbildung berücksichtigt nicht den task_worker-Prozess. Standardmäßig beträgt die Anzahl der task_worker-Prozesse 0

    Drei. Prozessmodus)


    3.1 Kommunikation zwischen Reaktor-Thread und Worker-Prozess

    1. Die Kommunikation zwischen Swoole-Masterprozess und Worker-Prozess ist wie in der Abbildung unten dargestellt

    • Swoole verwendet SOCK_DGRAM anstelle von SOCK_STREAM. Dies liegt daran, dass jeder Reaktor-Thread für die Verarbeitung mehrerer Anforderungen verantwortlich ist. Nach Erhalt der Anforderung leitet der Reaktor die Informationen an den Worker-Prozess weiter, der für die Verarbeitung verantwortlich ist. Wenn SOCK_STREAM verwendet wird, kann der Worker-Prozess TCP nicht weiterverarbeiten Anfrage
    • swFactoryProcess_start-Funktion Basierend auf der Anzahl der Worker-Prozesse wird eine entsprechende Anzahl von Socket-Paaren für die Kommunikation zwischen dem Reaktor-Thread und dem Worker-Prozess erstellt (siehe Funktion swPipeUnsock_create für Details)

    2. Angenommen, es gibt 2 Reaktor-Threads und 3 Worker-Prozesse, dann der Reaktor und Die Kommunikation zwischen Workern ist in der folgenden Abbildung dargestellt

    • Jeder Reaktor-Thread ist für die Überwachung mehrerer Worker-Prozesse verantwortlich, und jeder Worker-Prozess verfügt nur über eine Reaktor-Thread-Überwachung (reactor_num < = worker_num). Standardmäßig verwendet Swoole Worker_Process_ID % Reactor_num, um den Worker-Prozess zuzuweisen und ihn zur Überwachung an den entsprechenden Reaktor-Thread zu übergeben. Der Reaktor-Thread verarbeitet die Daten, nachdem er die Daten eines Worker-Prozesses empfangen hat Möglicherweise wird die Anfrage nicht gesendet.
    • 3. Datenpaket für die Kommunikation zwischen Reaktor-Thread und Worker-Prozess

    3.2 Anforderungsverarbeitung

    1 Der Haupt-Thread im Master-Prozess ist für das Abhören des Ports verantwortlich (listen). code>) und das Akzeptieren von Verbindungen (<code>accept, generiert einen fd). Nach dem Akzeptieren der Verbindung wird die Anforderung dem Reaktorthread zugewiesen. Standardmäßig wird fd % Reaktornummer zugewiesen und dann wird fd hinzugefügt Achten Sie beim ersten Beitritt auf den entsprechenden Reaktorthread über epoll_ctl. Da der Schreibpuffer des durch die neu akzeptierte Verbindung erstellten Sockets leer ist, muss er beschreibbar sein und wird sofort ausgelöst Dann führt der Reaktor-Thread einige Initialisierungsvorgänge aus.

    listen), 接受连接(accept, 产生一个fd), 接受连接后将请求分配给reactor线程, 默认通过fd % reactor_number进行分配, 之后通过epoll_ctl将fd加入到对应reactor线程中, 刚加入时监听写事件, 因为新接受连接创建的套接字写缓冲区为空, 故而一定可写, 会被立刻触发, 进而reactor线程进行一些初始化操作

    • 存在多个线程同时操作一个epollfd(通过系统调用epoll_create创建)的情况
    • 多个线程同时调用epoll_ctl是线程安全的(对应一个epollfd), 一个线程正在执行, 其他线程会被阻塞(因为需要同时操作epoll底层的红黑树)
    • 多个线程同时调用epoll_wait也是线程安全的, 但是一个事件可能会被多个线程同时接收到, 实际中不建议多个线程同时epoll_wait一个epollfd。Swoole中也是不存在这种情况的, Swoole中每个reactor线程都有自己的epollfd
    • 一个线程调用epoll_wait, 一个线程调用epoll_ctl, 根据man手册, 如果epoll_ctl新加入的fd已经准备好, 会使得执行epoll_wait的线程变成非阻塞状态(可以通过man epoll_waitEs gibt mehrere Threads, die gleichzeitig ausgeführt werden. Im Fall eines epollfd (erstellt durch den Systemaufruf epoll_create)
    mehrere Threads, die aufrufen epoll_ctl ist gleichzeitig threadsicher (entspricht einem epollfd), ein Thread wird ausgeführt und andere Threads werden blockiert (da der rot-schwarze Baum, der epoll zugrunde liegt, gleichzeitig betrieben werden muss)

    Es ist auch threadsicher, wenn mehrere Threads gleichzeitig epoll_wait aufrufen, aber ein Ereignis kann von mehreren Threads gleichzeitig empfangen werden. In der Praxis wird nicht empfohlen, dass mehrere Threads epoll_wait epollfd gleichzeitig. Diese Situation existiert in Swoole nicht. Jeder Reaktor-Thread in Swoole hat seinen eigenen epollfd

    Ein Thread ruft epoll_wait auf, und ein anderer Thread ruft epoll_ctl auf, so das Man-Handbuch. Wenn der neu hinzugefügte fd von epoll_ctl bereit ist, wird der Thread, der epoll_wait ausführt, nicht blockierend (Sie können die relevanten Informationen über man epoll_wait anzeigen). Inhalt)

      2. Das Schreibereignis von fd im Reaktorthread wird ausgelöst und der Reaktorthread ist für die Verarbeitung verantwortlich. Wenn festgestellt wird, dass es sich um den ersten Beitritt handelt und keine Daten zum Schreiben vorhanden sind , die Leseereignisüberwachung wird aktiviert und ist bereit, die vom Client gesendeten Daten zu akzeptieren.
    • Nach Erhalt der angeforderten Daten leitet er die Daten standardmäßig an den Worker-Prozess weiter , es wird über fd % worker_number zugewiesen.
    • Das vom Reaktor an den Worker-Prozess gesendete Datenpaket enthält einen Header, und die Reaktorinformationen werden im Header aufgezeichnet Aufgrund von Platzbeschränkungen wird die Datenfragmentierung später ausführlich beschrieben.
    • Es können mehrere Reaktoren vorhanden sein. Threads senden Daten gleichzeitig an denselben Arbeitsprozess. Daher verwendet Swoole den SOCK_DGRAM-Modus, um mit dem Arbeitsprozess zu kommunizieren . Anhand des Headers jedes Datenpakets kann der Worker-Prozess erkennen, welcher Reaktor-Thread die Daten gesendet hat.

    4 Nachdem der Worker-Prozess das vom Reaktor gesendete Datenpaket empfangen hat Nachdem die Verarbeitung abgeschlossen ist, wird das Anforderungsergebnis an den Hauptprozess gesendet. Das vom Arbeitsprozess an den Hauptprozess gesendete Datenpaket enthält auch einen Header. Wenn der Reaktor-Thread das Datenpaket empfängt, kann dies der Fall sein Kennen Sie den entsprechenden Reaktor-Thread, den angeforderten FD und andere Informationen
    • Dieser Reaktor-Thread ist nicht unbedingt der Reaktor-Thread, der die Anfrage zuvor an den Worker-Prozess gesendet hat.
    • Jeder Reaktor-Thread des Hauptprozesses ist für die Überwachung der vom Worker-Prozess gesendeten Datenpakete und der von ihm gesendeten Datenpakete verantwortlich Jeder Worker wird nur von einem Reaktor-Thread überwacht, sodass nur ein Reaktor-Thread ausgelöst wird. 6. Der Reaktor-Thread verarbeitet die vom Worker-Prozess gesendeten Daten direkt an den Client kann direkt gesendet werden. Wenn Sie den Verbindungs-Abhörstatus ändern müssen (z. B.
    • ), sind der Reaktor-Verarbeitungsthread und der Reaktor-Abhör-Thread möglicherweise nicht derselbe Thread. Der Reaktor-Abhör-Thread ist für die Überwachung der vom Client gesendeten Daten verantwortlich , und leitet es dann an den Arbeitsprozess weiter. Der Reaktorverarbeitungsthread ist für die Überwachung der vom Arbeitsprozess gesendeten Daten an den Hauptprozess verantwortlich und sendet die Daten dann an den Client.

    close), 则需要先找到监听这个连接的reactor线程, 进而改变这个连接的监听状态(通过调用epoll_ctl Viertens 4.1 Start im Prozessmodus Modus und Prozess Modus. Es erklärt die Netzwerkprogrammierungsmodelle der beiden Modi im Detail und konzentriert sich auf die prozessübergreifende Kommunikationsmethode, den Anforderungsverarbeitungsablauf usw.

      2 Der Hauptprozess, und lassen Sie die Threads die Anforderungen direkt verarbeiten (wodurch der Overhead der Kommunikation zwischen Prozessen vermieden werden kann), erstellen Sie jedoch einen Manager-Prozess und dann Der Worker-Prozess wird vom Manager-Prozess erstellt und die Anforderung wird vom Manager-Prozess verarbeitet Worker-Prozess?
    • Persönlich denke ich, dass die Unterstützung von PHP für Multithreading meist nicht sehr freundlich ist.
    • Obwohl das von ZendVM bereitgestellte TSRM auch Multithreading-Umgebungen unterstützt Tatsache ist, dass dies eine Lösung zum Isolieren des Speichers nach Threads ist. Multithreading macht keinen Sinn

    3 Im Prozessmodus kann jeder Reaktorthread im Hauptprozess mehrere Anforderungen gleichzeitig verarbeiten, und mehrere Anforderungen werden gleichzeitig verarbeitet. Wir betrachten es aus zwei Dimensionen

    Aus der Perspektive des Hauptprozesses verarbeitet der Hauptprozess mehrere Anfragen gleichzeitig. Wenn alle Anfragepakete empfangen werden, werden sie zur Verarbeitung an den Workerprozess weitergeleitet

    Aus der Perspektive eines Worker-Prozesses Aus der Sicht sind die von diesem Worker-Prozess empfangenen Anfragen seriell. Wenn eine einzelne Anfrage blockiert ist (Swooles Worker-Prozess ruft die von PHPer geschriebene Ereignisverarbeitungsfunktion zurück). Diese Funktion kann blockieren. Nachfolgende Anforderungen können nicht verarbeitet werden. In diesem Fall kann die Coroutine von Swool verwendet werden, wenn eine einzelne Anforderung blockiert wird

    4. Da es sich bei TCP um ein Byte-Stream-Protokoll handelt, kann Swoole keine Unteraufträge vergeben, ohne das Kommunikationsprotokoll zwischen dem Client und dem Server zu kennen Wird vom Reaktor an den Arbeitsprozess übergeben. Es kann nur ein Bytestrom sein und muss vom Benutzer verarbeitet werden. Natürlich ist es im Allgemeinen nicht erforderlich, selbst ein Protokoll zu erstellen, da Swoole bereits HTTP, HTTPS und andere Protokolle unterstützt

Das obige ist der detaillierte Inhalt vonEine kurze Analyse des Swoole-Servers. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:csdn.net. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen
Vorheriger Artikel:Swoole与HTTPNächster Artikel:Swoole与HTTP