的思路很简单。通过设置runtime.GOMAXPROCS(1),golang的进程变成了单线程。类似于python使用gevent的效果。然后通过调度多个协程实现异步I/O并发。PHP作为子函数运行在go进程中,当PHP需要让步给其他协程时,通过回调golang函数来实现。从php调用go提供的子函数时,go保证保存php的当前上下文。当协程执行权转回时,恢复原来的php上下文。关键代码是://在当前协程上保存php上下文oldServerCtx:=engine.ServerContextGet()fmt.Println(oldServerCtx)deferengine.ServerContextSet(oldServerCtx)oldExecutorCtx:=engine.ExecutorContextGet()fmt.Println(oldExecutorCtx)deferengine.ExecutorContextSet(oldExecutorCtx)oldCoreCtx:=engine.CoreContextGet()fmt.Println(oldCoreCtx)deferengine.CoreContextSet(oldCoreCtx)//放弃全局锁,让其他协程开始执行phpengineLock.Unlock()deferengineLock.Lock()I新增ServerContextGet这几个函数,获取php的三个全局上下文(EG/SG/PG)(参见:http://www.cnblogs.com/chance...)。修改过的github.com/deuill/go-php的源码在:https://github.com/taowen/go-...完整的php/go混合协议的demo:packagemainimport("fmt""github.com/deuill/go-php/engine""os""runtime""time""sync")typeTestObjstruct{}funcnewTestObj(args[]interface{})interface{}{return&TestObj{}}varengineLock*sync.Mutexfunc(self*TestObj)Hello(){oldServerCtx:=engine.ServerContextGet()fmt.Println(oldServerCtx)deferengine.ServerContextSet(oldServerCtx)oldExecutorCtx:=engine.ExecutorContextGet()fmt.Println(oldExecutorCtx)deferengine.ExecutorContextSet(oldExecutorCtx)oldCoreCtx:=engine.CoreContextGet()fmt.Println(oldCoreCtx)deferengine.CoreContextSet(oldCoreCtx)engineLock.Unlock()deferengineLock.Lock()time.Sleep(time.Second)fmt.Println("sleepdone")}funcmain(){runtime.GOMAXPROCS(1)theEngine,err:=engine.New()engineLock=&sync.Mutex{}iferr!=nil{fmt.Println(err)}_,err=theEngine.Define("TestObj",newTestObj)wg:=&符号c.WaitGroup{}wg.Add(2)before:=time.Now()fmt.Println("1")gofunc(){engineLock.Lock()deferengineLock.Unlock()context1,err:=theEngine.NewContext()iferr!=nil{fmt.Println(err)}context1.Output=os.Stdoutiferr!=nil{fmt.Println(err)}fmt.Println("1enter")_,err=context1.Eval("$testObj=newTestObj();$testObj->Hello();")fmt.Println("1back")iferr!=nil{fmt.Println(err)}//theEngine.DestroyContext(context1)fmt.Println("1done")wg.Done()}()fmt.Println("2")gofunc(){engineLock.Lock()deferengineLock.Unlock()context2,err:=theEngine.NewContext()iferr!=nil{fmt.Println(err)}iferr!=nil{fmt.Println(err)}context2.Output=os.Stdoutfmt.Println("2enter")_,err=context2.Eval("$testObj=newTestObj();$testObj->你好();")fmt.Println("2back")iferr!=nil{fmt.Println(err)}//theEngine.DestroyContext(context2)fmt.Println("2done")wg.Done()}()wg.Wait()after:=time.Now()fmt.Println(after.Sub(before))}执行结果是122enter{0x2cf2930{0000[00000]无001000[0000]}{{0160x7f682e8197800[0000000]}01[000]}000[000000]{0000000000000{00}{00}{00}[000]}0x2a002700x2a00f60838860801[000]0{872[0000]00x29f45200x29f45200x29f44700x29f4420100[00000]{0[000]<无><无><无><无>]0x7f682f01ba600x7f682f01b9600x7f682f1671680x7f682f01ba88{64635[0000]00x7f682f1972d80x7f682f1972d80x7f682f1993f80x7f682f1970c80x7f682e862d10001[00000]}{800[0000]00x7f682f016a00001[00000]}0x7ffd30bac5902252700[0000]0x7f682f1976400x29f4f800x29f4fd00x29f50700x2cf29500x7f682f1989c01401[000]01[000000]{800[0000]10x7f682f016a000x7f682e883140001[00000]}{800[0000]00x7f682f016a000x7f682e8831d0100[00000]}0x7f682f1670880[0000]{00}{000[0000000]}{000[0000000]}0[0000]000x29fb2e0{0x7f682f18703021024-1[0000]}[{0x7f682e915050[00000000][00000000][00000000]0014988688050[0x7f5000]0014988688[00000000][00000000]00149888}{0x7f682e915050[00000000][00000000][00000000]00149888}]0x7f682f167168{0[0000]0[0000]00[0000]0[0000]}1[0000000]0x7f682f01bde8895[000000][]}{1[0000000]000[000000]0x29ff9a017134217728-10001[0000]1024001[00000]0x2a008700x2a010a00x7f682ecc58b0x7fc722无<无>2097152<无><无>0x2a001800x2a00230<无><无><无>{0x7f682ec91aa80x7f682ec91aa8}0x2a00910{000[0000]0<无><无><无><无><无>000[00000]}000[000]{0x2b6dc100x2b6dc10181[0000000]}[0x7f682f1973300x7f682f1970400x7f682f1974100x7f682f1974f0]011[00000]0x7f682ec9544b0x7f682ec9544b00[000000]0[00000000]1111101[0]0[0000]<无<无>0[0000]0x2cf27c0<无>00[000000]6410000[0000000]0x7f682ecc62703000x2a009b01[0000000]<无>0[0000000]}1enter{0x7f6818000aa0{0000[00000]001000[0000]}{{0160x7f682e8197800[0000000]}01[000]}000[000000]{00000000000{00}{00}{00}[000]}0x2a002700x2a00f60838860801[000]0{872[0000]00x29f45200x29f45200x29f44700x29f4420100[00000]}{0[0000]00x29f45200x29f45200x29f44700x29f4420{0x7f682a4cccd8{[0000000000000000]200[00]}0x7f682f01b928{[0000000000000000]100[00]}0x7f682f01b948[]0x7f682f01ba600x7f682f01b9600x7f682802f1100x7f682f01ba88{64635[0000]00x7f682f197a000x7f682f197a000x7f682f1983680x7f682f198fa00x7f682e862d10001[00000]}{800[0000]00x7f682f016a00001[00000]}0x7f682a4ccce02252700[0000]0x7f682f197d280x29f4f800x29f4fd00x29f50700x2cf29500x7f682f1983e81401[000]01[000000]{800[0000]10x7f682f016a000x7f682e883140001[00000]}{800[0000]0NIL>]{00}{000[0000000]}{000[0000000]}0[0000]000x29fb2e0{0x7F682804EFD821024-1[0000]}[{0x7F682E915050[0000000000][0000000000]00000000][00000000][00000000]00149888}]0x7f682802f110{0[0000]0[0000]00[0000]0[0000]}1[0000000]0x7f682f01bde8895[000000][>>]}{1[0000000]000[000000]0x29ff9a017134217728-10001[0000]1024001[00000]0x2a008700x2a010a00x7f682ecc58b00x7f682ecc5c2320971520x2a001800x2a00230{0x7f682ec91aa80x7f682ec91aa8}0x2a00910{000[0000]0000[00000]}00012d<000]{c1d02bnil>1[0000000]}[0x7f682f197a580x7f682f198ce00x7f682f197b380x7f682f197c18]011[00000]0x7f682ec9544b0x7f682ec9544b00[000000]0[00000000]1111101[0]0[0000]0[0000]0x2cf27c000[000000]6410000[0000000]0x7f682ecc62703000x2a009b01[0000000]0[0000000]}sleepdone1back1donesleepdone2back2done1.00099211s1s可以看到两个sleep最终只用了1.00099211s,说明协程是并发的。一些性能指标。使用http调用后端。在i7-6700k上,使用ab-n100-c4运行这样的结果。Requestspersecond:3183.70[#/sec](mean)Timeperrequest:1.256[ms](mean)Timeperrequest:0.314[ms](mean,acrossallconcurrentrequests)如果不通过http调用后端,直接返回"hello"fromphp=>go,可以达到Requestspersecond:10073.54[#/sec](mean)Timeperrequest:0.397[ms](mean)Timeperrequest:0.099[ms](mean,acrossallconcurrentrequests)这些指标只是说明协程切换的成本。实际收益取决于后端http服务的延迟。如果需要很长时间,通过并发协程可以明显获益。本次实验表明,golang可以用来实现一个替代nginx+php-fpm的应用服务器。并且它提供了从php到golang的平滑迁移路径。在一个应用程序中混合使用PHP和Go语言。并且可以通过提供golang函数给php调用来实现I/O的异步化。像libcurl这样的扩展本身就支持异步回调,但是php是同步的,所以只有同步execute暴露给php。通过Golang,可以将execute变成异步execute+callback的wrapper,从而实现基于协程的调度。