您所在的位置:首页 / 知识分享

PHP 混合 Go 协程并发

2021.01.06

1119

wng_png

想法很简单。通过设置 runtime.GOMAXPROCS(1) 让 golang 的进程变成单线程执行的。
类似 python 用 gevent 的效果。然后通过调度多个协程实现异步 I/O 并发。php 作为
一个子函数跑在 go 的进程内,php 需要 yield 到其他协程时,通过回调到 golang 函
数来实现。从 php 里调用 go 提供的子函数时,go 保证保存 php 的当前上下文。当协
程执行权让渡回来的时候,把原来的 php 上下文恢复。关键的代码在:
// 保存当前协程上的 php 上下文
oldServerCtx := engine.ServerContextGet()
fmt.Println(oldServerCtx)
defer engine.ServerContextSet(oldServerCtx)
oldExecutorCtx := engine.ExecutorContextGet()
fmt.Println(oldExecutorCtx)
defer engine.ExecutorContextSet(oldExecutorCtx)
oldCoreCtx := engine.CoreContextGet()
fmt.Println(oldCoreCtx)
defer engine.CoreContextSet(oldCoreCtx)
// 放弃全局的锁,使得其他的协程可以开始执行 php
engineLock.Unlock()
defer engineLock.Lock()
ServerContextGet 这几个函数是我加的,获得的是 php 的(EG/SG/PG)这三个全局
context(参见:http://www.cnblogs.com/chance...)。修改过的
github.com/deuill/go-php 的源代码在:https://github.com/taowen/go-...
完整的 php/go 混合协程的 demo:
package main
import (
"fmt"
"github.com/deuill/go-php/engine"
"os"
"runtime"
"time"
"sync"
)
type TestObj struct{}
func newTestObj(args []interface{}) interface{} {
return &TestObj{}
}var engineLock *sync.Mutex
func (self *TestObj) Hello() {
oldServerCtx := engine.ServerContextGet()
fmt.Println(oldServerCtx)
defer engine.ServerContextSet(oldServerCtx)
oldExecutorCtx := engine.ExecutorContextGet()
fmt.Println(oldExecutorCtx)
defer engine.ExecutorContextSet(oldExecutorCtx)
oldCoreCtx := engine.CoreContextGet()
fmt.Println(oldCoreCtx)
defer engine.CoreContextSet(oldCoreCtx)
engineLock.Unlock()
defer engineLock.Lock()
time.Sleep(time.Second)
fmt.Println("sleep done")
}
func main() {
runtime.GOMAXPROCS(1)
theEngine, err := engine.New()
engineLock = &sync.Mutex{}
if err != nil {
fmt.Println(err)
}
_, err = theEngine.Define("TestObj", newTestObj)
wg := &sync.WaitGroup{}
wg.Add(2)
before := time.Now()
fmt.Println("1")
go func() {
engineLock.Lock()
defer engineLock.Unlock()
context1, err := theEngine.NewContext()
if err != nil {
fmt.Println(err)
}
context1.Output = os.Stdout
if err != nil {
fmt.Println(err)
}
fmt.Println("1 enter")
_, err = context1.Eval("$testObj = new TestObj(); $testObj->Hello
();")
fmt.Println("1 back")
if err != nil {
fmt.Println(err)
}
//theEngine.DestroyContext(context1)
fmt.Println("1 done")
wg.Done()
}()
fmt.Println("2")
go func() {
engineLock.Lock()
defer engineLock.Unlock()
context2, err := theEngine.NewContext()
if err != nil {
fmt.Println(err)
}
if err != nil {
fmt.Println(err)
}
context2.Output = os.Stdout
fmt.Println("2 enter")
_, err = context2.Eval("$testObj = new TestObj(); $testObj->Hello
();")
fmt.Println("2 back")
if err != nil {
fmt.Println(err)
}
//theEngine.DestroyContext(context2)
fmt.Println("2 done")
wg.Done()
}()
wg.Wait()
after := time.Now()
fmt.Println(after.Sub(before))
}
执行结果是
122 enter
{0x2cf2930 {00 0 0 [0 0 0 0 0]
0 01000 [0 0 0 0]} {{ il>0 16 0x7f682e819780 0 [0 0 0 0 0 0 0]} 0 1 [0 0 0]
} 0 0 0 [0 0 0 0 0 0] {0 0 0 0 0 0 0 0 0 0 0 {0 0} {0 0} {0 0} [0 0
0]} 0x2a00270 0x2a00f608388608 0 1 [0 0 0] 0 {8 7 2 [0 0 0 0] 0 0x
29f4520 0x29f4520 0x29f4470 0x29f44201 0 0 [0 0 0 0 0]}{0
[0 0 0 0 0 0 0]} 0 [0 0 0 0 0 0 0]}
{0x7ffd30bac588 {[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 2 0 0 [0 0]} 0x7f682f0
1b928 {[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 1 0 0 [0 0]} 0x7f682f01b948 [ l> l> l>] 0x7f682f01ba60 0x7f682f01b
960 0x7f682f167168 0x7f682f01ba88 {64 63 5 [0 0 0 0] 0 0x7f682f1972d8 0x7
f682f1972d8 0x7f682f1993f8 0x7f682f1970c8 0x7f682e862d10 0 0 1 [0 0 0 0
0]} {8 0 0 [0 0 0 0] 00x7f682f016a000 0 1 [0 0
0 0 0]} 0x7ffd30bac590 22527 0 0 [0 0 0 0] 0x7f682f197640 0x29f4f80 0x29f
4fd0 0x29f50700x2cf2950 0x7f682f1989c0 14 0 1 [0 0 0]
0 1 [0 0 0 0 0 0] {8 0 0 [0 0 0 0] 10x7f682f016a00 0x7
f682e883140 0 0 1 [0 0 0 0 0]} {8 0 0 [0 0 0 0] 00x7f6
82f016a00 0x7f682e8831d0 1 0 0 [0 0 0 0 0]} 0x7f682f167088 0 [0 0 0 0] il>{0 0} {0 00 [0 0 0 0 0 0 0]} {0 0 l> 0 [0 0 0 0 0 0 0]} 0 [0 0 0 0]0 0 0x29fb2e0{0x7f68
2f187030 2 1024 -1 [0 0 0 0]}[{0x7f682e915050 [0 0 0 0
0 0 0 0] [0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0] 0 0 149 8 8 8} {0x7f682e9150
50 [0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0] 0 0 149 8 8 8} {0
x7f682e915050 [0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0] 0 0 14
9 8 8 8}] 0x7f682f167168{0 [0 0 0 0]0 [0 0 0 0] 0 0 [0 0 0
0]0 [0 0 0 0]} 1 [0 0 0 0 0 0 0]0x7f682f01bde8 895 [0
0 0 0 0 0] []}
{1 [0 0 0 0 0 0 0] 0 0 0 [0 0 0 0 0 0]0x29ff9a0 17 134217728 -1 0 0
0 1 [0 0 0 0] 1024 0 0 1 [0 0 0 0 0] 0x2a008700x2a010a0 0x7f682ec
c58b00x7f682ecc5c2320971520x2a001
80 0x2a00230{0x7f682ec91aa8 0x7f682ec91aa8} 0x2a00910
{0 0 0 [0 0 0 0] 00 0 0 [0 0 0 0 0]} 0 0 0
[0 0 0] {0x2b6dc10 0x2b6dc10 1 81 [0 0 0 0 0 0 0]} [0x7f682
f197330 0x7f682f197040 0x7f682f1974100x7f682f1974f0] 0 1 1
[0 0 0 0 0] 0x7f682ec9544b 0x7f682ec9544b 0 0 [0 0 0 0 0 0] 0 [0 0 0 0 0
0 0 0] 1 1 1 1 1 0 1 [0] 0 [0 0 0 0]0 [0 0 0 0] 0x2cf27c0 l> 0 0 [0 0 0 0 0 0] 64 1000 0 [0 0 0 0 0 0 0] 0x7f682ecc6270 300 0x2a009
b0 1 [0 0 0 0 0 0 0]0 [0 0 0 0 0 0 0]}1 enter
{0x7f6818000aa0 {00 0 0 [0 0 0
0 0]0 01000 [0 0 0 0]}
{{0 16 0x7f682e819780 0 [0 0 0 0 0 0 0]} 0 1 [0 0 0] <
nil>} 0 0 0 [0 0 0 0 0 0] {0 0 0 0 0 0 0 0 0 0 0 {0 0} {0 0} {0 0}
[0 0 0]} 0x2a00270 0x2a00f608388608 0 1 [0 0 0] 0 {8 7 2 [0 0 0 0]
0 0x29f4520 0x29f4520 0x29f4470 0x29f44201 0 0 [0 0 0 0 0]}
{0 [0 0 0 0 0 0 0]} 0 [0 0 0 0 0 0 0]}
{0x7f682a4cccd8 {[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 2 0 0 [0 0]} 0x7f682f0
1b928 {[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 1 0 0 [0 0]} 0x7f682f01b948 [ l> l> l>] 0x7f682f01ba60 0x7f682f01b
960 0x7f682802f110 0x7f682f01ba88 {64 63 5 [0 0 0 0] 0 0x7f682f197a00 0x7
f682f197a00 0x7f682f198368 0x7f682f198fa0 0x7f682e862d10 0 0 1 [0 0 0 0
0]} {8 0 0 [0 0 0 0] 00x7f682f016a000 0 1 [0 0
0 0 0]} 0x7f682a4ccce0 22527 0 0 [0 0 0 0] 0x7f682f197d28 0x29f4f80 0x29f
4fd0 0x29f50700x2cf2950 0x7f682f1983e8 14 0 1 [0 0 0]
0 1 [0 0 0 0 0 0] {8 0 0 [0 0 0 0] 10x7f682f016a00 0x7
f682e883140 0 0 1 [0 0 0 0 0]} {8 0 0 [0 0 0 0] 00x7f6
82f016a00 0x7f682e8831d0 1 0 0 [0 0 0 0 0]} 0x7f682802f030 0 [0 0 0 0] il>{0 0} {0 00 [0 0 0 0 0 0 0]} {0 0 l> 0 [0 0 0 0 0 0 0]} 0 [0 0 0 0]0 0 0x29fb2e0{0x7f68
2804efd8 2 1024 -1 [0 0 0 0]}[{0x7f682e915050 [0 0 0 0
0 0 0 0] [0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0] 0 0 149 8 8 8} {0x7f682e9150
50 [0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0] 0 0 149 8 8 8} {0
x7f682e915050 [0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0] [0 0 0 0 0 0 0 0] 0 0 14
9 8 8 8}] 0x7f682802f110{0 [0 0 0 0]0 [0 0 0 0] 0 0 [0 0 0
0]0 [0 0 0 0]} 1 [0 0 0 0 0 0 0]0x7f682f01bde8 895 [0
0 0 0 0 0] []}
{1 [0 0 0 0 0 0 0] 0 0 0 [0 0 0 0 0 0]0x29ff9a0 17 134217728 -1 0 0
0 1 [0 0 0 0] 1024 0 0 1 [0 0 0 0 0] 0x2a008700x2a010a0 0x7f682ec
c58b00x7f682ecc5c2320971520x2a001
80 0x2a00230{0x7f682ec91aa8 0x7f682ec91aa8} 0x2a00910
{0 0 0 [0 0 0 0] 00 0 0 [0 0 0 0 0]} 0 0 0
[0 0 0] {0x2b6dc10 0x2b6dc10 1 81 [0 0 0 0 0 0 0]} [0x7f682
f197a58 0x7f682f198ce0 0x7f682f197b380x7f682f197c18] 0 1 1
[0 0 0 0 0] 0x7f682ec9544b 0x7f682ec9544b 0 0 [0 0 0 0 0 0] 0 [0 0 0 0 0
0 0 0] 1 1 1 1 1 0 1 [0] 0 [0 0 0 0]0 [0 0 0 0] 0x2cf27c0 l> 0 0 [0 0 0 0 0 0] 64 1000 0 [0 0 0 0 0 0 0] 0x7f682ecc6270 300 0x2a009
b0 1 [0 0 0 0 0 0 0]0 [0 0 0 0 0 0 0]}
sleep done1 back1 done
sleep done2 back2 done1.00099211s
可以看到两个 sleep 1s,最终只用了 1.00099211s。说明协程是并发的。
一些性能指标。走 http 调用后端,在 i7-6700k 上,用 ab -n 100 -c 4 可以跑出这样
的结果
Requests per second: 3183.70 [#/sec] (mean)Time per request: 1.2
56 [ms] (mean)Time per request: 0.314 [ms] (mean, across all concur
rent requests)
如果不用 http 调用后端,直接 php=>go 返回"hello",则可以达到
Requests per second: 10073.54 [#/sec] (mean)Time per request: 0.
397 [ms] (mean)Time per request: 0.099 [ms] (mean, across all concu
rrent requests)
这些指标只说明了协程切换的成本。实际的收益取决于后端的 http 服务的延迟,如果
耗时很长,通过协程并发则可以收益明显。
这个实验说明了可以用 golang 实现一个代替 nginx+php-fpm 的应用服务器。并且提
供了一条从 php 向 golang 迁移的平滑迁移路径。在一个应用里混合 PHP 和 Go 两种
语言。
并且可以通过提供 golang 函数给 php 调用的方式实现 I/O 的异步化。像 libcurl 这样
的扩展自身是支持异步回调的,只是 php 是同步的所以只给 php 暴露了同步的
execute。有了 Golang 之后,可以把 execute 变成对异步 execute+callback 的包装,
从而实现基于协程的调度。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

相关新闻

让人头疼的python 编码问题!!!

2018.10.29

0

中文编码问题是用中文的程序员经常头大的问题,在python下也是如此,那么应该怎么理解和解决python的编码问题呢?

让人头疼的python 编码问题!!!

2018.10.29

0

中文编码问题是用中文的程序员经常头大的问题,在python下也是如此,那么应该怎么理解和解决python的编码问题呢?

让人头疼的python 编码问题!!!

2018.10.29

0

中文编码问题是用中文的程序员经常头大的问题,在python下也是如此,那么应该怎么理解和解决python的编码问题呢?