Une scène que j'ai rencontrée récemment : phpUne fonction tierce (segmentation des mots balbutiants) doit être utilisée dans le projet, et il se trouve qu'il y a une bibliothèque de classes écrite en Golang sur github Alors la question se pose, comment réaliser la communication entre différentes langues. . Qu'en est-il ?
Écrivez un service http/TCP en Golang, et php communique avec Golang via http/TCP
Golang est encapsulé sous forme d'extension php
PHP appelle le fichier exécutable Golang via les commandes système
Requêtes http, les E/S réseau prendront beaucoup de temps
Beaucoup de code doit être encapsulé
Chaque fois que PHP appelle un programme Golang, il doit être initialisé, ce qui prend beaucoup de temps
Le programme Golang n'est initialisé qu'une seule fois (car l'initialisation prend du temps)
Toutes les requêtes n'ont pas besoin de passer par le réseau
Essayez de ne pas trop modifier le code
Package Golang simple, compilez la bibliothèque de classes tierce dans un exécutable fichier
PHP et Golang communiquent via des tuyaux bidirectionnels
1 : encapsulation minimale de la bibliothèque de classes Golang originale est requise
2 : Meilleures performances (la communication IPC est le meilleur moyen de communiquer entre les processus)
3 : Aucune requête réseau n'est requise, ce qui permet de gagner beaucoup de temps
4 : Le programme ne doit être initialisé qu'une seule fois et reste en mémoire
1 : Démo d'appel originale dans la bibliothèque de classe
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, "/")) }
Enregistrez le fichier sous main.go , vous pouvez exécuter
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") } } }
chaîne , puis sortie
après la segmentation des mots Test :
# go build test # ./test # //等待用户输入,输入”这是一个测试“ # 这是 一个 测试 //程序
//准备一个title.txt,每行是一句文本 # cat title.txt | ./test
Chat illustré ci-dessus Pour communiquer avec Golang, un tuyau unidirectionnel est utilisé. Autrement dit : les données ne peuvent être transmises que de cat à Golang. Les données sorties par Golang ne sont pas renvoyées à cat, mais sont directement affichées à l'écran. Mais l'exigence de l'article est la suivante : php communique avec Golang. Autrement dit, php doit transmettre les données à Golang, et Golang doit également renvoyer les résultats d'exécution à php. Un pipeline bidirectionnel doit donc être mis en place.
L'utilisation des pipes en PHP :
, je n'entrerai pas dans les détails car cette méthode ne peut pas résoudre le problème de l'article. popen("/path/test")
Pipeline bidirectionnel :
$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]);
array est renvoyé. PHP écrit les données dans $pipe['0'] et lit les données dans $pipe['1'].
(Toutes les langues implémenteront le contenu lié au pipeline) Test :
, calculez le temps pris par chaque processus. Le fichier title.txt mentionné ci-dessous contient 1 million de lignes de texte. Chaque ligne de texte est le titre du produit extrait de la plateforme b2b1 : Le processus global prend du temps
<a. href="http%20://m.sbmmt.com/wiki/1268.html" target="_blank">time<p> cat title.txt ./test > /www.php .cn/wiki/62.html" target="_blank">null<br></p></a.>
<a href="//m.sbmmt.com/wiki/1268.html" target="_blank">time</a> cat title.txt | ./test > /dev/<a href="//m.sbmmt.com/wiki/62.html" target="_blank">null</a>
Prend du temps : 14,819 secondes, dont :2 : Il faut du temps pour calculer la fonction de segmentation de mots
- Le processus cat lit le texte
- Transmettre les données à Golang via le tuyau
- Golang traite les données et renvoie le résultat à l'écran
. Solution : Supprimez l'appel de la fonction de segmentation de mots, c'est-à-dire : Commentaire et supprimez la ligne de code qui appelle la segmentation de mots dans le code source de Golang time cat title.txt | ./test > /dev/null
Prend du temps : 1,817 secondes, le temps consommé comprend :
- le processus cat lit le texte
- transfère les données vers Golang via un pipeline
Golang traite les données et renvoie le résultat à l'écran
分词耗时 = (第一步耗时) - (以上命令所耗时)
分词耗时 : 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进程。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!