In such a scenario, the php project needs to use a third-party function, and there happens to be a class library written in Golang. So the question comes, how to realize the communication between different languages? Let's have a look.
Conventional scheme
1. Write an http/TCP service with Golang, and php communicates with Golang through http/TCP
2. Golang is encapsulated as a php extension.
3. PHP calls the executable file of Golang through the system command
Existing problems
1. http request, network I/O will consume a lot of time
2. Need to encapsulate a lot of code
3. Every time PHP calls the Golang program, it needs an initialization, which consumes a lot of time
Optimization objectives
1. The Golang program is initialized only once (because initialization is time-consuming)
2. All requests do not need to go online
3. Try not to modify the code a lot
Solution
1. The simple Golang encapsulation compiles the third-party class library into an executable file
2. PHP communicates with Golang through a two-way pipeline
Advantages of using two-way pipeline communication
1: Only a small amount of encapsulation is required for the original Golang class library
2: Best performance (IPC communication is the best way to communicate between processes)
3: No network request is needed, which saves a lot of time
4: The program only needs to be initialized once and remains in memory all the time
Specific implementation steps
1: Original call demo in class library
package main import ( "fmt" "github.com/yanyiwu/gojieba" "strings" ) func main() { x := gojieba.NewJieba() defer x.Free() s := "Xiao Ming graduated from the Institute of computing of the Chinese Academy of Sciences and later studied at Kyoto University in Japan" words := x.CutForSearch(s, true) fmt.Println(strings.Join(words, "/")) }
Save the file as main Go, you can run
2: The adjusted code is:
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") } } }
It only needs a few simple lines of adjustment to realize: receive the string from the standard input, and then output it after word segmentation
Test:
# go build test # ./test #/ / wait for user input, enter "this is a test“ #This is a test / / program
3: Use cat to communicate with Golang for simple test
//Prepare a title Txt, each line is a sentence of text # cat title.txt | ./test
Normal output indicates that cat can interact with Golang normally
4: Communication between PHP and Golang
The cat shown above communicates with Golang using a one-way pipe. That is, data can only be transferred from cat to Golang. The data output by Golang is not returned to cat, but directly output to the screen. But the requirement in this article is: php communicates with Golang. That is, php needs to send data to Golang, and Golang must also return the execution result to php. Therefore, two-way pipeline needs to be introduced.
The use of pipeline in PHP: popen("/path/test"), I won't expand it, because this method can't solve the problem in this paper.
Two way pipe:
$descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w") ); $handle = proc_open( '/webroot/go/src/test/test', $descriptorspec, $pipes ); fwrite($pipes['0'], "This is a test text\n"); echo fgets($pipes[1]);
Explanation: using proc_open opens a process and calls the Golang program. At the same time, a bidirectional pipeline pipes array is returned. php writes data to $pipe['0 '] and reads data from $pipe['1'].
Well, maybe you've found that I'm the title file. The focus here is not just how PHP communicates with Golang. Instead, we are introducing a method to let any language communicate through two-way pipeline. (all languages implement pipeline related content)
Test:
Through the comparative test, the time occupied by each process is calculated. The title mentioned below Txt file, containing 1 million lines of text, each line of text is the product title taken from the b2b platform
1: The overall process takes time
time cat title.txt | ./test > /dev/null
Time consuming: 14.819 seconds, including:
Process cat reads text
Pipe data into Golang
Golang processes the data and returns the results to the screen
2: It takes time to calculate the word segmentation function. Scheme: remove the calling of word segmentation function, that is, comment out the code in the line of calling word segmentation in Golang source code
time cat title.txt | ./test > /dev/null
Time consumption: 1.817 seconds, including:
Process cat reads text
Pipe data into Golang
Golang processes the data and returns the results to the screen
Word segmentation time = (time spent in the first step) - (time spent in the above command)
Word segmentation time: 14.819 - 1.817 = 13.002 seconds
3: Test the time spent communicating between the cat process and the Golang process
time cat title.txt > /dev/null
Time consumption: 0.015 seconds, including:
Process cat reads text
Pipe data into Golang
go processes the data and returns the results to the screen
Pipeline communication time consuming: (time consuming in the second step) - (time consuming in the third step)
Pipeline communication time: 1.817 - 0.015 = 1.802 seconds
4: Time consumption of communication between PHP and Golang
Write a simple php file:
<?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);
The process is basically the same as above. Read out the title Txt content is passed into the Golang process through a two-way pipeline, and then returned to php (one step more than the above test: the data is returned through the pipeline)
time php popen.php > /dev/null
Time consuming: 24.037 seconds, including:
Process PHP reads text
Pass data into the Golang pipeline
Golang processing data
Golang writes the returned results to the pipeline, and PHP receives the data through the pipeline
Return results to screen
Conclusion:
1: Time-consuming distribution in the whole word segmentation process
Time consuming for using cat control logic: 14.819 seconds
Using PHP to control logic takes 24.037 seconds (one more pipeline communication than cat)
One way pipeline communication time: 1.8 seconds
The word segmentation function in Golang takes 13.002 seconds
2: Performance of word segmentation function: single process, 1 million commodity Title word segmentation, taking 13 seconds
The above time only includes word segmentation time, excluding dictionary loading time. However, in this scheme, the dictionary is loaded only once, so the loading time of the dictionary can be ignored (about 1 second)
3: PHP is slower than cat (this conclusion is a little redundant, ha ha)
Slow language level: (24.037 - 1.8 - 14.819) / 14.819 = 50%
For single process comparison test, there should be no language faster than cat.