Vorwort
Ein Szenario, auf das ich kürzlich bei der Arbeit gestoßen bin, war, dass eine Funktion eines Drittanbieters im PHP-Projekt verwendet werden musste und es zufällig eine in Golang geschriebene Klassenbibliothek gab. Es stellt sich also die Frage, wie eine Kommunikation zwischen verschiedenen Sprachen erreicht werden kann. Schauen wir uns unten um.
Konventionelle Lösung
1. Verwenden Sie Golang, um einen http/TCP-Dienst zu schreiben, und PHP kommuniziert mit Golang über http/TCP
2. Führen Sie Golang durch mehr Als PHP-Erweiterung gekapselt.
3. PHP ruft die ausführbare Golang-Datei über Systembefehle auf
Probleme
1. HTTP-Anfrage, Netzwerk-E/A wird verbraucht viel Zeit
2. Eine große Menge Code muss gekapselt werden
3. Jedes Mal, wenn PHP ein Golang-Programm aufruft, muss es initialisiert werden, was viel Zeit in Anspruch nimmt viel Zeit
Optimierungsziele
1. Das Golang-Programm wird nur einmal initialisiert (da die Initialisierung zeitaufwändig ist)
2. Alle Anfragen müssen nicht über das Netzwerk erfolgen
3. Versuchen Sie, den Code nicht stark zu ändern
Lösung
1 . Einfache Golang-Kapselung, kompilieren Sie die Klassenbibliothek eines Drittanbieters. Generieren Sie eine ausführbare Datei
2. PHP und Golang kommunizieren über Zwei-Wege-Pipes
Vorteile der Verwendung von Zwei-Wege-Pipes Way-Pipe-Kommunikation
1: Erfordert nur wenig Kapselung der ursprünglichen Golang-Klassenbibliothek
2: Beste Leistung (IPC-Kommunikation ist die beste Möglichkeit, zwischen Prozessen zu kommunizieren)
3: Es sind keine Netzwerkanfragen erforderlich, was viel Zeit spart
4: Das Programm muss nur einmal initialisiert werden und bleibt im Speicher
Spezifische Implementierungsschritte
1: Die ursprüngliche Aufrufdemo in der Klassenbibliothek
package main import ( "fmt" "github.com/yanyiwu/gojieba" "strings" ) func main() { x := gojieba.NewJieba() defer x.Free() s := "小明硕士毕业于中国科学院计算所,后在日本京都大学深造" words := x.CutForSearch(s, true) fmt.Println(strings.Join(words, "/")) }
Speichern Sie die Datei als main.go, und Sie können sie ausführen
2 : Der angepasste Code lautet:
package main import ( "bufio" "fmt" "github.com/yanyiwu/gojieba" "io" "os" "strings" ) func main() { x := gojieba.NewJieba( "/data/tmp/jiebaDict/jieba.dict.utf8", "/data/tmp/jiebaDict/hmm_model.utf8", "/data/tmp/jiebaDict/user.dict.utf8" ) defer x.Free() inputReader := bufio.NewReader(os.Stdin) for { s, err := inputReader.ReadString('\n') if err != nil && err == io.EOF { break } s = strings.TrimSpace(s) if s != "" { words := x.CutForSearch(s, true) fmt.Println(strings.Join(words, " ")) } else { fmt.Println("get empty \n") } } }
Es sind nur ein paar einfache Anpassungen erforderlich, um dies zu erreichen: Empfangen Sie eine Zeichenfolge von der Standardeingabe, segmentieren Sie sie und geben Sie sie dann aus
Test :
# go build test # ./test # //等待用户输入,输入”这是一个测试“ # 这是 一个 测试 //程序
3: Verwenden Sie die Katze, um mit Golang zu kommunizieren. Machen Sie einen einfachen Test.
//准备一个title.txt,每行是一句文本 # cat title.txt | ./test
Normale Ausgabe, was darauf hinweist, dass die Katze normal mit Golang interagieren kann.
4: PHP kommuniziert mit Golang
Katze oben abgebildet Zur Kommunikation mit Golang wird eine Einweg-Pipe verwendet. Das heißt: Daten können nur von cat an Golang übergeben werden. Die von Golang ausgegebenen Daten werden nicht an cat zurückgegeben, sondern direkt auf dem Bildschirm ausgegeben. Die Anforderung im Artikel lautet jedoch: PHP kommuniziert mit Golang. Das heißt, PHP muss Daten an Golang übergeben und Golang muss auch die Ausführungsergebnisse an PHP zurückgeben. Daher muss eine bidirektionale Pipeline eingeführt werden.
Die Verwendung von Pipes in PHP: popen("/path/test"), ich werde nicht auf Details eingehen, da diese Methode das Problem im Artikel nicht lösen kann.
Bidirektionale Pipe:
$descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w") ); $handle = proc_open( '/webroot/go/src/test/test', $descriptorspec, $pipes ); fwrite($pipes['0'], "这是一个测试文本\n"); echo fgets($pipes[1]);
Erklärung: Verwenden Sie proc_open, um einen Prozess zu öffnen und das Golang-Programm aufzurufen. Gleichzeitig wird ein bidirektionales Pipes-Array zurückgegeben, das Daten in $pipe['0'] schreibt und Daten aus $pipe['1'] liest.
Nun, vielleicht haben Sie herausgefunden, dass ich die Titeldatei bin und der Fokus hier nicht nur darauf liegt, wie PHP und Golang kommunizieren. Stattdessen führen wir eine Methode ein: Ermöglichen, dass jede Sprache über eine Zwei-Wege-Pipeline kommuniziert. (Alle Sprachen implementieren Pipeline-bezogene Inhalte)
Test:
Berechnen Sie durch Vergleichstests die Zeit, die jeder Prozess benötigt. Die unten erwähnte Datei title.txt enthält 1 Million Textzeilen. Jede Textzeile ist der Produkttitel, der von der B2B-Plattform übernommen wurde.
1: Der Gesamtprozess braucht Zeit
time cat title.txt | ./test > /dev/null
Zeit Verbrauch: 14,819 Sekunden, die verbrauchte Zeit umfasst:
Prozesskatze liest den Text
Leitet die Daten über die Pipeline an Golang weiter
Golang verarbeitet den Daten und gibt das Ergebnis zurück. Gehen Sie zum Bildschirm
2: Die Berechnung der Wortsegmentierungsfunktion dauert einige Zeit. Lösung: Entfernen Sie den Aufruf der Wortsegmentierungsfunktion, das heißt: Kommentieren Sie die Codezeile aus, die die Wortsegmentierung im Golang-Quellcode aufruft
time cat title.txt | ./test > /dev/null
Zeitverbrauch: 1,817 Sekunden, der Zeitverbrauch umfasst:
Die Prozesskatze liest den Text vor
Übergibt die Daten über die Pipe an Golang
Golang verarbeitet die Daten und gibt das Ergebnis an den Bildschirm zurück
Wortsegmentierung Zeitaufwendig = (im ersten Schritt benötigte Zeit) - (vom obigen Befehl benötigte Zeit)
Wortsegmentierungszeit: 14,819 - 1,817 = 13,002 Sekunden
3: Testen Sie den Cat-Prozess und Golang. Die für die Kommunikation zwischen Prozessen benötigte Zeit
time cat title.txt > /dev/null
beträgt: 0,015 Sekunden, die verbrauchte Zeit umfasst:
Process Cat liest den Text
Die Daten werden durch die Pipeline Pass in Golang geleitet
go verarbeitet die Daten und gibt die Ergebnisse an den Bildschirm zurück
Pipeline-Kommunikation zeitaufwändig: (Der zweite Schritt braucht Zeit) - (Der dritte Schritt braucht Zeit)
Pipeline-Kommunikationszeitverbrauch: 1,817 - 0,015 = 1,802 Sekunden
4: Zeitverbrauch von PHP- und Golang-Kommunikation
Schreiben Sie eine einfache PHP-Datei:
<?php $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w") ); $handle = proc_open( '/webroot/go/src/test/test', $descriptorspec, $pipes ); $fp = fopen("title.txt", "rb"); while (!feof($fp)) { fwrite($pipes['0'], trim(fgets($fp))."\n"); echo fgets($pipes[1]); } fclose($pipes['0']); fclose($pipes['1']); proc_close($handle);
Der Prozess ist im Grunde der gleiche wie oben. Lesen Sie den Inhalt von title.txt und übergeben Sie ihn an den Golang-Prozess Wortsegmentierung über eine Zwei-Wege-Pipe und anschließende Rückgabe an PHP (ein Schritt mehr als der obige Test: Die Daten werden über die Pipe zurückgegeben)
time php popen.php > /dev/null
Zeitverbrauch: 24,037 Sekunden, Zeitverbrauch inklusive :
PHP liest Text verarbeiten
Daten über Pipes an Golang übergeben
Golang verarbeitet die Daten
Golang gibt das Ergebnis zurück Schreiben an die Pipe, PHP empfängt Daten über die Pipe
Ergebnis auf dem Bildschirm zurückgeben
Schlussfolgerung:
1: Im Ganzen Wortsegmentierungsprozess Zeitaufwändige Verteilung
Verwenden Sie die Cat-Steuerungslogik, um zu dauern: 14,819 Sekunden
Verwenden Sie PHP, um die Logik zu steuern: 24,037 Sekunden (eine Pipeline-Kommunikation mehr als Cat)
Einweg-Pipeline-Kommunikation dauert 1,8 Sekunden
Die Wortsegmentierungsfunktion in Golang dauert 13,002 Sekunden
2: Leistung der Wortsegmentierungsfunktion: Einzelvorgang. Die Segmentierung von 1 Million Produkttiteln dauert 13 Sekunden
Die oben genannte Zeit umfasst nur die Wortsegmentierungszeit und nicht die Wörterbuchladezeit. Aber in dieser Lösung wird das Wörterbuch nur einmal geladen, sodass die Ladezeit des Wörterbuchs ignoriert werden kann (ca. 1 Sekunde)
3: PHP ist langsamer als cat (diese Schlussfolgerung ist etwas überflüssig, haha)
Langsam auf Sprachebene: (24.037 - 1.8 - 14.819) / 14.819 = 50 %
In einem Einzelprozess-Vergleichstest sollte es keine Sprache geben, die schneller ist als cat .
Verwandte Fragen:
1: Was im obigen Golang-Quellcode geschrieben steht, ist eine Schleife, das heißt, Daten werden immer aus der Pipe gelesen. Es stellt sich also die Frage: Wird der Golang-Prozess nach dem Ende des PHP-Prozesses noch existieren?
Der Rohrmechanismus selbst löst dieses Problem. Pipes bieten zwei Schnittstellen: Lesen und Schreiben. Wenn der Schreibvorgang unerwartet endet oder aufhängt, meldet der Lesevorgang ebenfalls einen Fehler und die Fehlerlogik im obigen Golang-Quellcode wird ausgeführt und der Golang-Prozess wird beendet.
Wenn der PHP-Prozess jedoch noch nicht beendet ist, aber vorerst keine Daten eingehen, wartet der Golang-Prozess weiter. Der Golang-Prozess wird nicht automatisch beendet, bis PHP beendet ist.
2: Können mehrere PHP-Prozesse dieselbe Pipe parallel lesen und schreiben und der Golang-Prozess bedient sie gleichzeitig?
Nein. Die Pipe ist unidirektional. Wenn mehrere Prozesse gleichzeitig in die Pipe schreiben, wird der Rückgabewert von Golang verwechselt.
Um dies zu erreichen, können Sie mehrere weitere Golang-Prozesse öffnen, und jeder PHP-Prozess entspricht einem Golang-Prozess.
Schließlich ist das oben Genannte alles Unsinn. Wenn Sie sich mit Rohren und bidirektionalen Rohren auskennen, wird Ihnen die obige Erklärung wenig nützen. Wenn Sie sich jedoch nicht mit Pipelines auskennen, ist es in Ordnung, den obigen Code zu debuggen. Sie könnten jedoch in eine Falle tappen, wenn Sie geringfügige Änderungen vornehmen.
Zusammenfassung
Das Obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass der Inhalt dieses Artikels für alle beim Erlernen oder Verwenden von Golang hilfreich sein kann eine Nachricht zur Kommunikation. Vielen Dank für Ihre Unterstützung für die Unterstützung der chinesischen PHP-Website.
Ausführlichere Erläuterungen zur Kommunikation zwischen PHP und der Go-Sprache finden Sie auf der chinesischen PHP-Website!