>  기사  >  백엔드 개발  >  PHP는 Golang과 통신합니다.

PHP는 Golang과 통신합니다.

PHPz
PHPz원래의
2017-04-04 14:40:471803검색

최근에 접한 장면: php타사 기능(더듬거리는 단어 분할)을 사용해야 함 프로젝트에 git허브에 Golang으로 작성된 클래스 라이브러리 가 있습니다. 그런 다음 질문이 생깁니다. 서로 다른 언어 간의 통신을 구현하는 방법입니다. .

기존 솔루션:

  • Golang에서 http/TCP 서비스를 작성하고, php는 http/TCP를 통해 Golang과 통신합니다

  • Golang은 PHP 확장으로 캡슐화됩니다.

  • PHP는 시스템 명령을 통해 Golang 실행 파일을 호출합니다.

기존 문제:

  • http 요청, 네트워크 I/O는 많은 시간을 소모합니다

  • 많은 코드를 캡슐화해야 합니다

  • PHP가 Golang 프로그램을 호출할 때마다 초기화해야 하며 시간이 많이 걸립니다

최적화 목표:

  • Golang 프로그램은 한 번만 초기화됩니다(초기화에 시간이 많이 걸리기 때문입니다)

  • 모든 요청이 네트워크를 거치지 않아도 됩니다

  • 코드를 많이 수정하지 마세요

해결책:

  • 간단한 Golang 패키지, 타사 클래스 라이브러리를 실행 파일로 컴파일 file

  • PHP와 Golang은 양방향 파이프를 통해 통신합니다

양방향 파이프 통신 사용의 장점:

1: 최소한의 캡슐화 원본 Golang 클래스 라이브러리가 필요합니다
2: 최고의 성능(IPC 통신은 프로세스 간 통신에 가장 좋은 방법입니다)
3: 네트워크 요청이 필요하지 않아 시간이 많이 절약됩니다.
4: 프로그램 한 번만 초기화하면 되고 메모리에 남아 있어야 합니다.

구체 구현 단계:

  • 1: 클래스 라이브러리의 원본 호출 데모

          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, "/"))
          }

    파일을 main.go로 저장하고 실행 가능

  • 2: 조정된 코드는

          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")
                  }
              }
          }

    몇 가지 간단한 조정만으로 달성 가능: 표준 입력 문자열 에서 수신, 단어 분할 후 출력
    테스트:

      # go build test
      # ./test
      # //等待用户输入,输入”这是一个测试“
      # 这是 一个 测试 //程序
  • 3: 고양이를 사용하여 통신 간단한 테스트를 위한 Golang 사용

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

    고양이가 Golang과 정상적으로 상호 작용할 수 있음을 나타내는 정상적인 출력

  • 4: PHP는 Golang과 통신합니다
    고양이 위에 표시된 것은 파이프라인에 대한 단일을 사용하여 Golang과 통신합니다. 즉, 데이터는 cat에서 Golang으로만 전달될 수 있습니다. Golang에서 출력된 데이터는 cat으로 다시 전달되지 않고 화면에 직접 출력됩니다. 그러나 기사의 요구 사항은 다음과 같습니다. PHP는 Golang과 통신합니다. 즉, php는 Golang에 데이터를 전달해야 하고, Golang도 실행 결과를 php에 반환해야 합니다. 따라서 양방향 파이프라인을 도입해야 합니다.
    PHP에서 파이프 사용: popen("/path/test"), 이 방법은 기사의 문제를 해결할 수 없기 때문에 자세히 설명하지 않겠습니다.
    양방향 파이프:

          $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]);

    설명: proc_open을 사용하여 프로세스를 열고 Golang 프로그램을 호출합니다. 동시에 양방향 파이프라인배열이 반환됩니다. PHP는 $pipe['0']에 데이터를 쓰고 $pipe['1']에서 데이터를 읽습니다. 양방향 파이프를 통해 모든 언어가 통신할 수 있는 방법을 소개합니다.

    (모든 언어는 파이프라인 관련 내용을 구현합니다)
테스트:

비교 테스트

를 통해 각 프로세스에 소요되는 시간을 계산합니다. 아래에 언급된 title.txt 파일에는 1백만 줄의 텍스트가 포함되어 있으며, 각 텍스트 줄은 b2b 플랫폼에서 가져온 제품 제목입니다

1: 전체 프로세스에 시간이 많이 걸립니다

<a href="http%20://www.php.cn/wiki/1268.html" target="_blank">시간<strong> 고양이 제목.txt | ./test > /dev/<a href="http:/%20/www.php%20.cn/wiki/62.html" target="_blank">null</a></strong></a>


소모 시간: 14.819초, 포함: <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>

고양이가 텍스트를 읽는 프로세스

  • 파이프를 통해 Golang에 데이터를 전달

  • Golang이 데이터를 처리하고 결과를 화면에 반환

  • 2: 단어 분할
  • 함수
를 계산하는 데 시간이 걸립니다. 해결책: 단어 분할 기능 호출, 즉
Comment

를 제거하고 Golang 소스 코드 소요 시간: 1.817초, 소요 시간은 다음을 포함합니다:
time cat title.txt | ./test > /dev/null

고양이가 텍스트를 읽는 프로세스

  • 파이프라인을 통해 Golang으로 데이터 전송

  • Golang이 데이터를 처리

    하고 그 결과를 화면에 반환합니다

分词耗时 = (第一步耗时) - (以上命令所耗时)
分词耗时 : 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);

流程与上面基本一致,读出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 秒

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进程。

위 내용은 PHP는 Golang과 통신합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.