前書き
最近、仕事でサードパーティ関数を PHP プロジェクトで使用する必要があるというシナリオに遭遇しましたが、たまたま Golang で書かれたクラス ライブラリがありました。そこで問題となるのは、異なる言語間のコミュニケーションをどのように実現するかということです。以下を見てみましょう。
従来のソリューション
1. Golang を使用して http/TCP サービスを作成し、php は http/TCP 経由で Golang と通信します
2. Golang を php 拡張機能としてカプセル化します。
3. PHP はシステムコマンドを通じて Golang 実行可能ファイルを呼び出します
問題点
1. HTTP リクエストとネットワーク I/O に多くの時間がかかります
2. 大量のコードをカプセル化する必要があります
3. PHP が Golang プログラムを呼び出すたびに、一度初期化する必要があり、多くの時間がかかります
最適化の目標
1. Golang プログラムは 1 回だけ初期化されます (初期化には時間がかかるため) ) 2. すべてのリクエストはオンラインにする必要はありません
3. コードをあまり変更しないようにしてください
解決策
1. シンプルな Golang カプセル化、サードパーティのクラス ライブラリを実行可能ファイルにコンパイルします
2. PHP と Golang は双方向パイプ通信を通じて通信します
双方向パイプ通信を使用する利点
1: 元の Golang クラス ライブラリの最小限のカプセル化のみが必要です
2: 最高のパフォーマンス (IPC 通信)プロセス間の通信に最適な方法です)
3: ネットワークリクエストが不要で、時間を大幅に節約 4: プログラムは一度初期化するだけで済み、メモリに残ります
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: 簡単なテストのために cat を使用して Golang と通信します
//准备一个title.txt,每行是一句文本 # cat title.txt | ./test
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'] からデータを読み取ります。
さて、おそらく私がタイトル ファイルであることに気づいたでしょう。ここで焦点を当てているのは、PHP と Golang がどのように通信するかだけではありません。代わりに、あらゆる言語が双方向パイプを介して通信できるようにする方法を導入しています。 (すべての言語でパイプライン関連のコンテンツが実装されます)
テスト:
比較テストを通じて、各プロセスにかかる時間を計算します。下記の title.txt ファイルには 100 万行のテキストが含まれており、テキストの各行は B2B プラットフォームから取得した製品タイトルです 1: 全体のプロセスにはtime cat title.txt | ./test > /dev/null
処理 CAT はテキストを読み取ります 道 Golang にデータを支払います
Golang はパイプを通じてデータを処理し、結果を画面に返します 2: 文言関数の時間を計算します。解決策: 単語セグメンテーション関数の呼び出しを削除します。つまり、単語セグメンテーションを呼び出す Golang ソース コードのコード行をコメント アウトします
time cat title.txt | ./test > /dev/null所要時間: 1.817 秒、消費時間には以下が含まれます:
使う という言葉を使うセグメンテーション関数
パイプラインはデータを Golang に転送します
Golang はデータを処理し、結果を画面に返します
単語のセグメンテーション時間 = (最初のステップには時間がかかります) - (上記のコマンドは時間がかかります) time)
単語分割時間: 14.819 - 1.817 = 13.002 秒
3: cat プロセスと Golang プロセス間の通信にかかる時間をテストする
time cat title.txt > /dev/null消費時間: 0.015 秒、消費時間には以下が含まれます: process cat がテキストを読み取ります
パイプライン経由で Golang にデータを転送します
GO でデータを処理し、結果を画面に返します
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 に返します (上記のテストにはもう 1 つのステップがあります。データはパイプラインを通じて返されます)
time php popen.php > /dev/null所要時間: 24.037 秒、所要時間には以下が含まれます:
PHP がテキストを読み出すプロセス
パイプを通じて Golang にデータを渡す
結論:
1: 単語分割プロセス全体での分散に時間がかかる
cat を使用してロジックを制御するのにかかる時間: 14.819 秒
PHP を使用してロジックを制御する: 24.037 秒 (cat よりパイプライン通信が 1 つ多い)
パイプラインへの単一通信にかかる時間: 1.8 秒
Golang の単語分割関数にかかる時間: 13.002 秒
2: のパフォーマンス単語分割機能: 単一プロセス、100 万件の商品タイトル分割に 13 秒かかります
上記の時間は単語分割時間を含むのみで、辞書の読み込み時間は含まれません。ただし、このソリューションでは、辞書は 1 回だけロードされるため、辞書のロード時間は無視できます (約 1 秒)
3: PHP は cat よりも遅いです (この結論は少し冗長です、笑)
言語レベル: (24.037 - 1.8 - 14.819) / 14.819 = 50%
単一プロセスの比較テストでは、cat より速い言語は存在しないはずです。
1: 上記の Golang ソース コードに書かれているのはループ、つまりデータは常にパイプから読み取られます。そこで質問があります: PHP プロセスが終了した後も、Golang プロセスはまだ存在しますか?
ただし、PHP プロセスが終了していなくても、一時的にデータが入ってこない場合、Golang プロセスは待機し続けます。 Golang プロセスは、php が終了するまで自動的に終了しません。
2: 複数の php プロセスが同じパイプを並行して読み書きし、Golang プロセスがそれらのプロセスを同時に処理することはできますか?
いいえ。パイプは一方向です。複数のプロセスが同時にパイプに書き込むと、Golang の戻り値が混乱します。
これを実現するためにさらにいくつかの Golang プロセスを開くことができ、各 php プロセスは Golang プロセスに対応します。
最後に、上記はすべてナンセンスです。パイプと双方向パイプを理解していれば、上記の説明はほとんど役に立ちません。ただし、パイプラインを理解していなくても、上記のコードをデバッグするのは問題ありませんが、わずかな変更を加えると罠に陥る可能性があります。
概要
上記がこの記事の全内容です。この記事の内容が Golang の学習や使用に役立つことを願っています。ご質問がある場合は、メッセージを残してください。 PHP 中国語 Web サイトへのサポートをお願いします。
PHP と Go 言語間の通信に関する詳細な記事については、PHP 中国語 Web サイトに注目してください。