首頁  >  文章  >  php教程  >  PHP與Go語言的溝通詳解

PHP與Go語言的溝通詳解

高洛峰
高洛峰原創
2016-12-28 15:50:412411瀏覽

前言

最近工作中遇到的一個場景,php專案中需要使用一個第三方的功能,而恰好有一個用Golang寫好的類別庫。那麼問題就來了,要如何實現不同語言之間的溝通呢?下面就來一起看看吧。

常規的方案

    1、 用Golang寫一個http/TCP服務,php透過http/TCP與Golang通訊

     2、將Golang經過較多封裝,做為php。

     3、PHP透過系統指令,調取Golang的執行檔

存在的問題

    

     3、PHP每調取一次Golang程序,就需要一次初始化,時間消耗很多


最佳化目標


     1.Golang需要走網


     3.盡量不大量修改程式碼


解決方案


    管線通訊


使用雙向管道通訊優勢


     1:只需要對原有Golang類庫進行很少的封裝


   

     3:不需要走網請求,節省大量時間

     4:程式只需初始化一次,並保持在記憶體中

具體實現步驟


儲存檔案為main.go,就可以運行

2:調整後程式碼為:

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

只需要簡單的幾行調整,即可實現:從標準輸入接收字串,經過分詞再輸出


測試:

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

3:使用cat與Golang通信做簡單測試

# go build test
# ./test
# //等待用户输入,输入”这是一个测试“
# 这是 一个 测试 //程序

正常輸出,表示cat已經可以和Golang正常交互了

4:PHP與Golang通信

  以上所示的cat與Golang通訊

  以上所示的cat與Golang通訊,使用的cat是單向管道。即:只能從cat向Golang傳入數據,Golang輸出的數據並沒有傳回給cat,而是直接輸出到螢幕。但文中的需求是:php與Golang通訊。即php要傳遞資料給Golang,同時Golang也必須把執行結果回傳給php。因此,需要引入雙向管道。

  在PHP中管道的使用:popen("/path/test") ,具體就不展開說了,因為此方法解決不了文中的問題。

雙向管道:

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

解釋:使用proc_open開啟一個進程,呼叫Golang程式。同時傳回一個雙向管道pipes數組,php寫入$pipe['0']數據,從$pipe['1']讀取資料。


好吧,也許你已經發現,我是標題檔,這裡重點要講的不只是PHP與Golang如何溝通。而是在介紹一種方法: 透過雙向管道讓任意語言溝通。 (所有語言都會實現管道相關內容)

測試:

通過對比測試,計算出各個流程佔用的時間。以下提到的title.txt文件,包含100萬行文本,每行文字是從b2b平台取的商品標題

1: 整體流程耗時

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

耗時:14.819秒,消耗時間包含:

   cat讀出文字

     透過管道將資料傳入Golang

     Golang處理數據,將結果回復至螢幕

2:計算分詞函數耗時。方案:去除分詞函數的調取,即:註解掉Golang原始碼中的調取分詞那行的程式碼

time cat title.txt | ./test > /dev/null

耗時:1.817秒時間,消耗時間包含:


    管道將資料傳入Golang


     Golang處理數據,將結果回傳至螢幕


分詞耗時= (第一步耗時) - (上述指令所耗時)

分詞與第一步耗時) - (上述指令所耗時)

分詞詞= 13.002秒

3:測試cat進程與Golang進程之間通訊所佔時間

time cat title.txt | ./test > /dev/null

耗時:0.015秒,消耗時間包含:

      

     go處理數據,將結果返回螢幕


管道通訊耗時:(第二步耗時) - (第三步耗時)

管道通訊耗時: 1.817秒 - 0.002 4:PHP與Golang通訊的時間消耗

編寫簡單的php檔案:

time cat title.txt > /dev/null

流程與上面基本一致,讀出title.txt內容,透過雙向管道傳入Golang進程分詞後,再傳回給php (比上面的測試多一步:數據再通過管道返回)

<?php
 $descriptorspec = array( 
  0 => array("pipe", "r"), 
  1 => array("pipe", "w")
 );
 
 $handle = proc_open(
  &#39;/webroot/go/src/test/test&#39;, 
  $descriptorspec, 
  $pipes
 );
 
 $fp = fopen("title.txt", "rb");
 
 while (!feof($fp)) {
  fwrite($pipes[&#39;0&#39;], trim(fgets($fp))."\n");
  echo fgets($pipes[1]);
 }
 
 fclose($pipes[&#39;0&#39;]);
 fclose($pipes[&#39;1&#39;]);
 proc_close($handle);

耗時:24.037秒,消耗時間包含:

     進程PHP讀出文字

     將資料傳入Golang

     Golang    將結果回到螢​​幕

結論:

1 :整個分詞過程中的耗時分佈

    使用cat控制邏輯耗時:        14.819 秒 多一次管道通訊)


    單向管道通訊耗時:           1.8    秒

    Golang中的分詞函數耗時:    

    以上時間只包括分詞時間,不包括字典載入時間。但在這個方案中,字典只載入一次,所以載入字典時間可以忽略(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進程。

最後,上面都是瞎扯的。如果你了解管道、雙向管道,上面的解釋對你基本上沒啥用。但如果你不了解管道,調試上面的程式碼沒問題,但稍有修改就有可能掉坑裡。

總結


以上就是這篇文章的全部內容了,希望本文的內容對大家學習或使用Golang能有所幫助,如果有疑問大家可以留言交流,謝謝大家對PHP中文網的支持。

更多PHP與Go語言之間的通信詳解相關文章請關注PHP中文網!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn