新聞中心
想法很簡單。通過設置 runtime.GOMAXPROCS(1) 讓 golang 的進程變成單線程執(zhí)行的。類似python用gevent的效果。然后通過調(diào)度多個協(xié)程實現(xiàn)異步I/O并發(fā)。php作為一個子函數(shù)跑在go的進程內(nèi),php需要yield到其他協(xié)程時,通過回調(diào)到golang函數(shù)來實現(xiàn)。從php里調(diào)用go提供的子函數(shù)時,go保證保存php的當前上下文。當協(xié)程執(zhí)行權(quán)讓渡回來的時候,把原來的php上下文恢復。關(guān)鍵的代碼在:

創(chuàng)新互聯(lián)堅信:善待客戶,將會成為終身客戶。我們能堅持多年,是因為我們一直可值得信賴。我們從不忽悠初訪客戶,我們用心做好本職工作,不忘初心,方得始終。10年網(wǎng)站建設經(jīng)驗創(chuàng)新互聯(lián)是成都老牌網(wǎng)站營銷服務商,為您提供成都網(wǎng)站設計、網(wǎng)站建設、網(wǎng)站設計、H5網(wǎng)站設計、網(wǎng)站制作、品牌網(wǎng)站建設、小程序開發(fā)服務,給眾多知名企業(yè)提供過好品質(zhì)的建站服務。
- // 保存當前協(xié)程上的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)
- // 放棄全局的鎖,使得其他的協(xié)程可以開始執(zhí)行php
- engineLock.Unlock()
- defer engineLock.Lock()
ServerContextGet 這幾個函數(shù)是我加的,獲得的是php的(EG/SG/PG)這三個全局context(參見:http://www.cnblogs.com/chance...)。修改過的github.com/deuill/go-php的源代碼在:https://github.com/taowen/go-...
完整的php/go混合協(xié)程的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))
- }
執(zhí)行結(jié)果是
- 1
- 2
- 2 enter
- {0x2cf2930 {
0 0 0 0 [0 0 0 0 0] 0 0 1000 [0 0 0 0]} {{ 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 0x2a00f60 8388608 0 1 [0 0 0] 0 {8 7 2 [0 0 0 0] 0 0x29f4520 0x29f4520 0x29f4470 0x29f4420 1 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]} 0x7f682f01b928 {[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 1 0 0 [0 0]} 0x7f682f01b948 [
] 0x7f682f01ba60 0x7f682f01b960 0x7f682f167168 0x7f682f01ba88 {64 63 5 [0 0 0 0] 0 0x7f682f1972d8 0x7f682f1972d8 0x7f682f1993f8 0x7f682f1970c8 0x7f682e862d10 0 0 1 [0 0 0 0 0]} {8 0 0 [0 0 0 0] 0 0x7f682f016a00 0 0 1 [0 0 0 0 0]} 0x7ffd30bac590 22527 0 0 [0 0 0 0] 0x7f682f197640 0x29f4f80 0x29f4fd0 0x29f5070 0x2cf2950 0x7f682f1989c0 14 0 1 [0 0 0] 0 1 [0 0 0 0 0 0] {8 0 0 [0 0 0 0] 1 0x7f682f016a00 0x7f682e883140 0 0 1 [0 0 0 0 0]} {8 0 0 [0 0 0 0] 0 0x7f682f016a00 0x7f682e8831d0 1 0 0 [0 0 0 0 0]} 0x7f682f167088 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] 0 0 0x29fb2e0 {0x7f682f187030 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} {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} {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}] 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] 0x2a00870 0x2a010a0 0x7f682ecc58b0 0x7f682ecc5c23 2097152 0x2a00180 0x2a00230 {0x7f682ec91aa8 0x7f682ec91aa8} 0x2a00910 {0 0 0 [0 0 0 0] 0 0 0 0 [0 0 0 0 0]} 0 0 0 [0 0 0] {0x2b6dc10 0x2b6dc10 1 8 1 [0 0 0 0 0 0 0] } [0x7f682f197330 0x7f682f197040 0x7f682f197410 0x7f682f1974f0] 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 0 0 [0 0 0 0 0 0] 64 1000 0 [0 0 0 0 0 0 0] 0x7f682ecc6270 300 0x2a009b0 1 [0 0 0 0 0 0 0] 0 [0 0 0 0 0 0 0]} - 1 enter
- {0x7f6818000aa0 {
0 0 0 0 [0 0 0 0 0] 0 0 1000 [0 0 0 0]} {{ 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 0x2a00f60 8388608 0 1 [0 0 0] 0 {8 7 2 [0 0 0 0] 0 0x29f4520 0x29f4520 0x29f4470 0x29f4420 1 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]} 0x7f682f01b928 {[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] 1 0 0 [0 0]} 0x7f682f01b948 [
] 0x7f682f01ba60 0x7f682f01b960 0x7f682802f110 0x7f682f01ba88 {64 63 5 [0 0 0 0] 0 0x7f682f197a00 0x7f682f197a00 0x7f682f198368 0x7f682f198fa0 0x7f682e862d10 0 0 1 [0 0 0 0 0]} {8 0 0 [0 0 0 0] 0 0x7f682f016a00 0 0 1 [0 0 0 0 0]} 0x7f682a4ccce0 22527 0 0 [0 0 0 0] 0x7f682f197d28 0x29f4f80 0x29f4fd0 0x29f5070 0x2cf2950 0x7f682f1983e8 14 0 1 [0 0 0] 0 1 [0 0 0 0 0 0] {8 0 0 [0 0 0 0] 1 0x7f682f016a00 0x7f682e883140 0 0 1 [0 0 0 0 0]} {8 0 0 [0 0 0 0] 0 0x7f682f016a00 0x7f682e8831d0 1 0 0 [0 0 0 0 0]} 0x7f682802f030 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] 0 0 0x29fb2e0 {0x7f682804efd8 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} {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} {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}] 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] 0x2a00870 0x2a010a0 0x7f682ecc58b0 0x7f682ecc5c23 2097152 0x2a00180 0x2a00230 {0x7f682ec91aa8 0x7f682ec91aa8} 0x2a00910 {0 0 0 [0 0 0 0] 0 0 0 0 [0 0 0 0 0]} 0 0 0 [0 0 0] {0x2b6dc10 0x2b6dc10 1 8 1 [0 0 0 0 0 0 0] } [0x7f682f197a58 0x7f682f198ce0 0x7f682f197b38 0x7f682f197c18] 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 0 0 [0 0 0 0 0 0] 64 1000 0 [0 0 0 0 0 0 0] 0x7f682ecc6270 300 0x2a009b0 1 [0 0 0 0 0 0 0] 0 [0 0 0 0 0 0 0]} - sleep done
- 1 back
- 1 done
- sleep done
- 2 back
- 2 done
- 1.00099211s
可以看到兩個sleep 1s,最終只用了1.00099211s。說明協(xié)程是并發(fā)的。
一些性能指標。走http調(diào)用后端,在i7-6700k上,用ab -n 100 -c 4 可以跑出這樣的結(jié)果
- Requests per second: 3183.70 [#/sec] (mean)
- Time per request: 1.256 [ms] (mean)
- Time per request: 0.314 [ms] (mean, across all concurrent requests)
如果不用http調(diào)用后端,直接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 concurrent requests)
這些指標只說明了協(xié)程切換的成本。實際的收益取決于后端的http服務的延遲,如果耗時很長,通過協(xié)程并發(fā)則可以收益明顯。
這個實驗說明了可以用golang實現(xiàn)一個代替nginx+php-fpm的應用服務器。并且提供了一條從php向golang遷移的平滑遷移路徑。在一個應用里混合PHP和Go兩種語言。
并且可以通過提供golang函數(shù)給php調(diào)用的方式實現(xiàn)I/O的異步化。像libcurl這樣的擴展自身是支持異步回調(diào)的,只是php是同步的所以只給php暴露了同步的execute。有了Golang之后,可以把execute變成對異步execute+callback的包裝,從而實現(xiàn)基于協(xié)程的調(diào)度。
網(wǎng)頁名稱:PHP混合Go協(xié)程并發(fā)
文章源于:http://fisionsoft.com.cn/article/cdesjje.html


咨詢
建站咨詢
