Heim >Backend-Entwicklung >PHP-Tutorial >PHP-Hybrid-Go-Coroutine-Parallelität
Die Idee ist einfach. Durch Festlegen von runtime.GOMAXPROCS(1) wird der Golang-Prozess Single-Threaded. Ähnlich dem Effekt der Verwendung von gevent in Python. Anschließend wird die asynchrone E/A-Parallelität durch die Planung mehrerer Coroutinen erreicht. PHP wird als Unterfunktion im Go-Prozess ausgeführt. Wenn PHP anderen Coroutinen nachgeben muss, kann dies durch einen Rückruf der Golang-Funktion implementiert werden. Beim Aufruf der von Go bereitgestellten Unterfunktion aus PHP garantiert Go, den aktuellen Kontext von PHP zu speichern. Wenn das Coroutine-Ausführungsrecht zurückübertragen wird, wird der ursprüngliche PHP-Kontext wiederhergestellt. Der Schlüsselcode lautet:
// 保存当前协程上的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 Diese Funktionen wurden von mir hinzugefügt, und die drei erhaltenen globalen Kontexte sind PHP (EG/SG/PG) (siehe: http://www. cnblogs.com/ Chance…). Der geänderte Quellcode von github.com/deuill/go-php befindet sich unter: https://github.com/taowen/go-...
Vollständige Demo der PHP/Go-Hybrid-Coroutine:
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)) }
Das Ausführungsergebnis ist
1 2 2 enter {0x2cf2930 {<nil> <nil> <nil> 0 <nil> <nil> <nil> <nil> 0 0 0 [0 0 0 0 0] <nil> <nil> <nil> <nil> <nil> <nil> <nil> 0 0 <nil> 1000 [0 0 0 0]} {{<nil> <nil> 0 16 0x7f682e819780 0 [0 0 0 0 0 0 0] <nil>} 0 1 [0 0 0] <nil> <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 0x2a00f60 <nil> 8388608 0 1 [0 0 0] 0 {8 7 2 [0 0 0 0] 0 0x29f4520 0x29f4520 0x29f4470 0x29f4420 <nil> 1 0 0 [0 0 0 0 0]} <nil> {0 [0 0 0 0 0 0 0] <nil> <nil> <nil> <nil>} 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 [<nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil>] 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 <nil> <nil> <nil> 0x7f682f016a00 <nil> 0 0 1 [0 0 0 0 0]} 0x7ffd30bac590 22527 0 0 [0 0 0 0] 0x7f682f197640 0x29f4f80 0x29f4fd0 0x29f5070 <nil> 0x2cf2950 0x7f682f1989c0 14 0 1 [0 0 0] <nil> <nil> 0 1 [0 0 0 0 0 0] {8 0 0 [0 0 0 0] 1 <nil> <nil> <nil> 0x7f682f016a00 0x7f682e883140 0 0 1 [0 0 0 0 0]} {8 0 0 [0 0 0 0] 0 <nil> <nil> <nil> 0x7f682f016a00 0x7f682e8831d0 1 0 0 [0 0 0 0 0]} 0x7f682f167088 0 [0 0 0 0] <nil> <nil> {0 0 <nil>} {0 0 <nil> <nil> 0 [0 0 0 0 0 0 0]} {0 0 <nil> <nil> 0 [0 0 0 0 0 0 0]} 0 [0 0 0 0] <nil> 0 0 0x29fb2e0 <nil> <nil> {0x7f682f187030 2 1024 -1 [0 0 0 0]} <nil> <nil> <nil> [{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 <nil> {0 [0 0 0 0] <nil> 0 [0 0 0 0] 0 0 [0 0 0 0] <nil> 0 [0 0 0 0] <nil>} 1 [0 0 0 0 0 0 0] <nil> 0x7f682f01bde8 895 [0 0 0 0 0 0] [<nil> <nil> <nil> <nil>]} {1 [0 0 0 0 0 0 0] 0 0 0 [0 0 0 0 0 0] <nil> 0x29ff9a0 17 134217728 -1 0 0 0 1 [0 0 0 0] 1024 0 0 1 [0 0 0 0 0] 0x2a00870 <nil> 0x2a010a0 0x7f682ecc58b0 <nil> 0x7f682ecc5c23 <nil> <nil> <nil> 2097152 <nil> <nil> 0x2a00180 0x2a00230 <nil> <nil> <nil> {0x7f682ec91aa8 0x7f682ec91aa8} 0x2a00910 {0 0 0 [0 0 0 0] 0 <nil> <nil> <nil> <nil> <nil> 0 0 0 [0 0 0 0 0]} 0 0 0 [0 0 0] {0x2b6dc10 0x2b6dc10 1 8 <nil> 1 [0 0 0 0 0 0 0] <nil>} [0x7f682f197330 0x7f682f197040 0x7f682f197410 <nil> <nil> 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] <nil> <nil> 0 [0 0 0 0] 0x2cf27c0 <nil> 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] <nil> 0 [0 0 0 0 0 0 0]} 1 enter {0x7f6818000aa0 {<nil> <nil> <nil> 0 <nil> <nil> <nil> <nil> 0 0 0 [0 0 0 0 0] <nil> <nil> <nil> <nil> <nil> <nil> <nil> 0 0 <nil> 1000 [0 0 0 0]} {{<nil> <nil> 0 16 0x7f682e819780 0 [0 0 0 0 0 0 0] <nil>} 0 1 [0 0 0] <nil> <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 0x2a00f60 <nil> 8388608 0 1 [0 0 0] 0 {8 7 2 [0 0 0 0] 0 0x29f4520 0x29f4520 0x29f4470 0x29f4420 <nil> 1 0 0 [0 0 0 0 0]} <nil> {0 [0 0 0 0 0 0 0] <nil> <nil> <nil> <nil>} 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 [<nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil>] 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 <nil> <nil> <nil> 0x7f682f016a00 <nil> 0 0 1 [0 0 0 0 0]} 0x7f682a4ccce0 22527 0 0 [0 0 0 0] 0x7f682f197d28 0x29f4f80 0x29f4fd0 0x29f5070 <nil> 0x2cf2950 0x7f682f1983e8 14 0 1 [0 0 0] <nil> <nil> 0 1 [0 0 0 0 0 0] {8 0 0 [0 0 0 0] 1 <nil> <nil> <nil> 0x7f682f016a00 0x7f682e883140 0 0 1 [0 0 0 0 0]} {8 0 0 [0 0 0 0] 0 <nil> <nil> <nil> 0x7f682f016a00 0x7f682e8831d0 1 0 0 [0 0 0 0 0]} 0x7f682802f030 0 [0 0 0 0] <nil> <nil> {0 0 <nil>} {0 0 <nil> <nil> 0 [0 0 0 0 0 0 0]} {0 0 <nil> <nil> 0 [0 0 0 0 0 0 0]} 0 [0 0 0 0] <nil> 0 0 0x29fb2e0 <nil> <nil> {0x7f682804efd8 2 1024 -1 [0 0 0 0]} <nil> <nil> <nil> [{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 <nil> {0 [0 0 0 0] <nil> 0 [0 0 0 0] 0 0 [0 0 0 0] <nil> 0 [0 0 0 0] <nil>} 1 [0 0 0 0 0 0 0] <nil> 0x7f682f01bde8 895 [0 0 0 0 0 0] [<nil> <nil> <nil> <nil>]} {1 [0 0 0 0 0 0 0] 0 0 0 [0 0 0 0 0 0] <nil> 0x29ff9a0 17 134217728 -1 0 0 0 1 [0 0 0 0] 1024 0 0 1 [0 0 0 0 0] 0x2a00870 <nil> 0x2a010a0 0x7f682ecc58b0 <nil> 0x7f682ecc5c23 <nil> <nil> <nil> 2097152 <nil> <nil> 0x2a00180 0x2a00230 <nil> <nil> <nil> {0x7f682ec91aa8 0x7f682ec91aa8} 0x2a00910 {0 0 0 [0 0 0 0] 0 <nil> <nil> <nil> <nil> <nil> 0 0 0 [0 0 0 0 0]} 0 0 0 [0 0 0] {0x2b6dc10 0x2b6dc10 1 8 <nil> 1 [0 0 0 0 0 0 0] <nil>} [0x7f682f197a58 0x7f682f198ce0 0x7f682f197b38 <nil> <nil> 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] <nil> <nil> 0 [0 0 0 0] 0x2cf27c0 <nil> 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] <nil> 0 [0 0 0 0 0 0 0]} sleep done 1 back 1 done sleep done 2 back 2 done 1.00099211s
Sie können zwei Schlaf-1s sehen und am Ende wurden nur 1,00099211s verwendet. Zeigt an, dass Coroutinen gleichzeitig sind.
Einige Leistungsindikatoren. Verwenden Sie http, um das Backend aufzurufen. Verwenden Sie auf i7-6700k ab -n 100 -c 4, um die folgenden Ergebnisse zu erhalten:
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)
Wenn Sie http nicht verwenden, um das Backend aufzurufen, verwenden Sie direkt php=> go gibt „Hallo“ zurück, dann kann
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)
erreicht werden. Diese Indikatoren veranschaulichen nur die Kosten des Coroutine-Wechsels. Der tatsächliche Nutzen hängt von der Verzögerung des Back-End-HTTP-Dienstes ab. Wenn es lange dauert, kann der Nutzen durch Coroutine-Parallelität offensichtlich sein.
Dieses Experiment zeigt, dass Golang zur Implementierung eines Anwendungsservers verwendet werden kann, der Nginx PHP-FPM ersetzt. Und bietet einen reibungslosen Migrationspfad von PHP zu Golang. Kombinieren Sie PHP und Go in einer Anwendung.
Und asynchrone E/A kann durch die Bereitstellung von Golang-Funktionen für PHP-Aufrufe erreicht werden. Erweiterungen wie libcurl selbst unterstützen asynchrone Rückrufe, aber PHP ist synchron, sodass nur die synchrone Ausführung für PHP verfügbar gemacht wird. Mit Golang kann „execute“ in einen Wrapper für asynchrone „execute“-Rückrufe umgewandelt werden, um eine koroutinenbasierte Planung zu erreichen.