摘要:當協程執行權讓渡回來的時候,把原來的上下文恢復。說明協程是并發的。實際的收益取決于后端的服務的延遲,如果耗時很長,通過協程并發則可以收益明顯。
想法很簡單。通過設置 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)) }
執行結果是
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。說明協程是并發的。
一些性能指標。走http調用后端,在i7-6700k上,用ab -n 100 -c 4 可以跑出這樣的結果
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調用后端,直接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)
這些指標只說明了協程切換的成本。實際的收益取決于后端的http服務的延遲,如果耗時很長,通過協程并發則可以收益明顯。
這個實驗說明了可以用golang實現一個代替nginx+php-fpm的應用服務器。并且提供了一條從php向golang遷移的平滑遷移路徑。在一個應用里混合PHP和Go兩種語言。
并且可以通過提供golang函數給php調用的方式實現I/O的異步化。像libcurl這樣的擴展自身是支持異步回調的,只是php是同步的所以只給php暴露了同步的execute。有了Golang之后,可以把execute變成對異步execute+callback的包裝,從而實現基于協程的調度。
參考資料:
https://wiki.php.net/internal...
http://www.cunmou.com/phpbook...
http://www.phpinternalsbook.c...
http://www.php-internals.com/...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/21976.html
摘要:為語言提供了強大的協程編程模式。提供的協程語法借鑒自,在此向開發組致敬協程可以與很好地互補。并發執行使用創建協程,可以讓和兩個函數變成并發執行。協程需要拿到請求的結果。 Swoole4為PHP語言提供了強大的CSP協程編程模式。底層提供了3個關鍵詞,可以方便地實現各類功能。 Swoole4提供的PHP協程語法借鑒自Golang,在此向GO開發組致敬 PHP+Swoole協程可以與...
摘要:現在在后端業務開發編程方面,技術力量強的團隊已經開始將技術棧從同步模式切換為異步了。使用這些技術方案是無法兼容已有程序的。影響了異步回調技術棧的普及。將會成為未來后端開發領域的主流技術方案。 今天太忙,少寫一點,后面再補充。 異步模式 Go 語言越來越熱門,很多大型互聯網公司后端正在轉向 GO 。Java 圈知名的服務化框架 Dubbo 也宣布轉型異步模式。這是一個大趨勢,異步模式已經...
摘要:易用穩定,本次想通過對的學習和個人解析,吸收框架的思想和設計知識,加強自己對的認知和理解。當然,筆者能力水平有限,后續的文章如有錯誤,還請指出和諒解。目錄如下后續添加文章都會記錄在此服務啟動過程以及主體設計流程源碼解析 前言 swoole是什么?官網的原話介紹是這樣的: Swoole 使用純 C 語言編寫,提供了 PHP 語言的異步多線程服務器,異步 TCP/UDP 網絡客戶端,異步 ...
摘要:有研究過框架的同學就會發現,其實最核心的,就是用了拓展加上拓展來實現其底層的網絡服務和多進程調度。我們在模式下,測試起五個進程主進程要等待回收我們,這樣就很簡單的實現了一個多進程的協程服務。 有研究過Workman框架的同學就會發現,其實workman最核心的,就是用了php socket拓展加上pcntl拓展來實現其底層的網絡服務和多進程調度。那我們今天就來探討如何使用Swoole的...
摘要:協程與信箱得益于,我們可以基于的協程與快速實現一個信箱模式調度。樣例代碼比如在一個聊天室中,我們可以定義一個房間模型。 什么是Actor? Actor對于PHPer來說,可能會比較陌生,寫過Java的同學會比較熟悉,Java一直都有線程的概念(雖然PHP有Pthread,但不普及),它是一種非共享內存的并發模型,每個Actor內的數據獨立存在,Actor之間通過消息傳遞的形式進行交互調...
閱讀 2260·2023-04-25 14:50
閱讀 1233·2021-10-13 09:50
閱讀 1866·2019-08-30 15:56
閱讀 1839·2019-08-29 15:29
閱讀 2886·2019-08-29 15:27
閱讀 3548·2019-08-29 15:14
閱讀 1192·2019-08-29 13:01
閱讀 3299·2019-08-26 14:06