머리말
최근 직장에서 접한 시나리오는 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 프로그램은 한 번만 초기화됩니다(초기화에 시간이 많이 걸리기 때문입니다)
2. 모든 요청은 네트워크를 거치지 않아도 됩니다
3. 코드를 많이 수정하지 마세요
해결책
1 . 간단한 Golang 캡슐화, 타사 클래스 라이브러리 컴파일 실행 파일 생성
2. 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']에서 데이터를 읽습니다.
글쎄, 아마도 내가 제목 파일이고 여기서 초점은 PHP와 Golang의 통신 방식에만 있는 것이 아니라는 사실을 발견했을 것입니다. 대신, 양방향 파이프를 통해 모든 언어가 통신할 수 있도록 하는 방법을 도입합니다. (모든 언어는 파이프라인 관련 내용을 구현합니다)
테스트:
비교 테스트를 통해 각 프로세스에 소요되는 시간을 계산합니다. 아래에 언급된 title.txt 파일에는 100만 줄의 텍스트가 포함되어 있습니다. 각 텍스트 줄은 b2b 플랫폼에서 가져온 제품 제목입니다.
1: 전체 프로세스에 시간이 걸립니다
time cat title.txt | ./test > /dev/null
시간 소비: 14.819초, 소비된 시간은 다음과 같습니다.
프로세스 고양이가 텍스트를 읽습니다.
파이프라인을 통해 Golang으로 데이터를 전달합니다.
Golang이 텍스트를 처리합니다. 데이터를 입력하고 결과를 반환합니다. 화면으로 이동
2: 단어 분할 기능을 계산하는 데 시간이 걸립니다. 해결 방법: 단어 분할 기능 호출을 제거합니다. 즉, Golang 소스 코드에서 단어 분할을 호출하는 코드 줄을 주석 처리합니다.
time cat title.txt | ./test > /dev/null
시간 소비: 1.817초, 시간 소비에는 다음이 포함됩니다.
프로세스 cat이 텍스트를 읽습니다.
파이프를 통해 Golang에 데이터를 전달합니다.
Golang은 데이터를 처리하고 결과를 화면에 반환합니다.
단어 분할 시간 소요 = (첫 번째 단계에서 소요된 시간) - (위 명령에서 소요된 시간)
단어 분할 시간: 14.819 - 1.817 = 13.002초
3: 테스트 고양이 프로세스와 Golang 프로세스 간 통신에 걸리는 시간
time cat title.txt > /dev/null
소요 시간: 0.015초, 소요 시간은 다음과 같습니다.
프로세스 고양이가 텍스트를 읽습니다.
데이터는 파이프라인을 통해 전달됩니다. Pass in 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개 더 많음)
단방향 파이프라인 통신 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 프로세스는 계속 대기합니다. Golang 프로세스는 PHP가 끝날 때까지 자동으로 종료되지 않습니다.
2: 여러 PHP 프로세스가 동일한 파이프를 병렬로 읽고 쓸 수 있으며 Golang 프로세스는 이를 동시에 제공할 수 있습니까?
아닙니다. 파이프는 단방향입니다. 여러 프로세스가 동시에 파이프에 쓰면 Golang의 반환 값이 혼동됩니다.
이를 달성하기 위해 여러 개의 Golang 프로세스를 더 열 수 있으며 각 PHP 프로세스는 Golang 프로세스에 해당합니다.
결국 위의 내용은 모두 말도 안되는 소리입니다. 파이프와 양방향 파이프를 이해한다면 위의 설명은 거의 쓸모가 없을 것입니다. 하지만 파이프라인을 이해하지 못한다면 위의 코드를 디버깅해도 괜찮지만, 조금만 수정하면 함정에 빠질 수 있습니다.
요약
이 글의 내용이 Golang을 배우거나 사용하는 모든 분들께 도움이 되었으면 좋겠습니다. PHP 중국어 웹사이트 지원에 감사드립니다.
PHP와 Go언어의 통신에 대한 더 자세한 글은 PHP 중국어 홈페이지를 참고해주세요!