PHP kommuniziert mit Golang

PHPz
Freigeben: 2023-03-07 09:52:01
Original
1857 Leute haben es durchsucht

Eine Szene, auf die ich kürzlich gestoßen bin: phpEine Funktion eines Drittanbieters (Stammwortsegmentierung) muss verwendet werden im Projekt, und es gibt zufällig eine in Golang geschriebene Klassenbibliothek auf gitHub. Dann stellt sich die Frage, wie man die Kommunikation zwischen verschiedenen Sprachen erreichen kann . Was ist mit?

Konventionelle Lösung:

  • Schreiben Sie einen http/TCP-Dienst in Golang, und PHP kommuniziert mit Golang über http/TCP

  • Golang ist als PHP-Erweiterung gekapselt

  • PHP ruft die ausführbare Golang-Datei über Systembefehle auf

Bestehende Probleme:

  • HTTP-Anfragen und Netzwerk-E/A nehmen viel Zeit in Anspruch

  • Viel Code muss gekapselt werden

  • Jedes Mal, wenn PHP ein Golang-Programm aufruft, muss es initialisiert werden, was viel Zeit in Anspruch nimmt

Optimierungsziele:

  • Das Golang-Programm wird nur einmal initialisiert (da die Initialisierung zeitaufwändig ist)

  • Alle Anfragen müssen nicht über das Netzwerk gehen

  • Versuchen Sie, den Code nicht zu stark zu ändern

Lösung:

  • Einfaches Golang-Paket, kompilieren Sie die Klassenbibliothek eines Drittanbieters in eine ausführbare Datei Datei

  • PHP und Golang kommunizieren über bidirektionale Pipes

Vorteile der Verwendung bidirektionaler Pipe-Kommunikation:

1: Nur minimale Kapselung der ursprünglichen Golang-Klassenbibliothek ist erforderlich
2: Beste Leistung (IPC-Kommunikation ist die beste Möglichkeit, zwischen Prozessen zu kommunizieren)
3: Es sind keine Netzwerkanforderungen erforderlich, was viel Zeit spart
4: Das Programm Muss nur einmal initialisiert werden und bleibt im Speicher

Spezifische Implementierungsschritte:

  • 1: Original-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, "/"))
          }
    Nach dem Login kopieren

    Speichern Sie die Datei als main.go, Sie können

  • 2 ausführen: 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")
                  }
              }
          }
    Nach dem Login kopieren

    Es dauert nur eine einige einfache Anpassungen, die erreicht werden müssen: Von der Standardeingabe erhalten Sie Zeichenfolge und geben dann
    nach der Wortsegmentierung aus. Test:

      # go build test
      # ./test
      # //等待用户输入,输入”这是一个测试“
      # 这是 一个 测试 //程序
    Nach dem Login kopieren
  • 3: Verwenden Sie cat, um für einen einfachen Test mit Golang zu kommunizieren

      //准备一个title.txt,每行是一句文本
      # cat title.txt | ./test
    Nach dem Login kopieren

    Normale Ausgabe, die anzeigt, dass cat 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 Pipeline:

          $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]);
    Nach dem Login kopieren

    Erläuterung: Verwenden Sie proc_open, um einen Prozess zu öffnen und das Golang-Programm aufzurufen. Gleichzeitig wird ein bidirektionales Pipeline-Pipesarray zurückgegeben, das PHP 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 stellen wir eine Methode vor: Lassen Sie jede Sprache über eine Zwei-Wege-Pipeline kommunizieren. (Alle Sprachen implementieren Pipeline-bezogene Inhalte)

Test:

Berechnen Sie anhand des 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 ist zeitaufwändig
<a href="http%20://www.php.cn/wiki/1268.html" target="_blank">time<code><a href="http://www.php.cn/wiki/1268.html" target="_blank">time</a> cat title.txt | ./test > /dev/<a href="http://www.php.cn/wiki/62.html" target="_blank">null</a> cat title.txt |./test > /www.php .cn/wiki/62.html" target="_blank">null

Zeitaufwendig: 14,819 Sekunden, einschließlich:
  • Die Prozesskatze liest den Text vor
  • Übergeben Sie die Daten über die Pipe an Golang
  • Golang verarbeitet die Daten und gibt das Ergebnis auf dem Bildschirm zurück

2: Die Berechnung der Wortsegmentierungsfunktion dauert einige Zeit. Lösung: Entfernen Sie den Aufruf der Wortsegmentierungsfunktion, also: Kommentar
und entfernen Sie die Codezeile, die die Wortsegmentierung im Golang-Quellcode aufruft time cat title.txt | ./test > /dev/null

Zeitaufwendig: 1,817 Sekunden, die verbrauchte Zeit umfasst:
  • Prozesskatze liest Text vor
  • Daten über Pipeline in Golang übertragen
  • Golang verarbeitet die Daten

    und gibt das Ergebnis auf dem Bildschirm zurück

分词耗时 = (第一步耗时) - (以上命令所耗时)
分词耗时 : 14.819 - 1.817 = 13.002

3:测试cat进程与Golang进程之间通信所占时间
time cat title.txt > /dev/null

耗时:0.015秒,消耗时间包含:

  • 进程cat读出文本

  • 通过管道将数据传入Golang

  • go处理数据,将结果返回到屏幕

管道通信耗时:(第二步耗时)  - (第三步耗时)
管道通信耗时: 1.817 - 0.015 = 1.802秒

4:PHP与Golang通信的时间消耗
编写简单的php文件:

        <?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);
Nach dem Login kopieren

流程与上面基本一致,读出title.txt内容,通过双向管道传入Golang进程分词后,再返回给php (比上面的测试多一步:数据再通过管道返回)
time php popen.php > /dev/null

耗时:24.037秒,消耗时间包含:

  • 进程PHP读出文本

  • 通过管道将数据传入Golang

  • Golang处理数据

  • Golang将返回结果再写入管道,PHP通过管道接收数据

  • 将结果返回到屏幕

结论:

1 :整个分词过程中的耗时分布

使用cat控制逻辑耗时:        14.819 秒
使用PHP控制逻辑耗时:         24.037 秒(比cat多一次管道通信)
单向管道通信耗时:           1.8    秒
Golang中的分词函数耗时:     13.002 秒
Nach dem Login kopieren

2:分词函数的性能: 单进程,100万商品标题分词,耗时13秒
以上时间只包括分词时间,不包括词典载入时间。但在本方案中,词典只载入一次,所以载入词典时间可以忽略(1秒左右)

3:PHP比cat慢 (这结论有点多余了,呵呵)
语言层面慢: (24.037 - 1.8 - 14.819) / 14.819 = 50%
单进程对比测试的话,应该不会有哪个语言比cat更快。

相关问题:

  • 1:以上Golang源码中写的是一个循环,也就是会一直从管道中读数据。那么存在一个问题:是不是php进程结束后,Golang的进程还会一直存在?

    管道机制自身可解决此问题。管道提供两个接口:读、写。当写进程结束或者意外挂掉时,读进程也会报错,以上Golang源代码中的err逻辑就会执行,Golang进程结束。
    但如果PHP进程没有结束,只是暂时没有数据传入,此时Golang进程会一直等待。直到php结束后,Golang进程才会自动结束。

  • 2:能否多个php进程并行读写同一个管道,Golang进程同时为其服务?

    不可以。管道是单向的,如果多个进程同时向管道中写,那Golang的返回值就会错乱。
    可以多开几个Golang进程实现,每个php进程对应一个Golang进程。

Das obige ist der detaillierte Inhalt vonPHP kommuniziert mit Golang. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage
Über uns Haftungsausschluss Sitemap
Chinesische PHP-Website:Online-PHP-Schulung für das Gemeinwohl,Helfen Sie PHP-Lernenden, sich schnell weiterzuentwickeln!