ホームページ  >  記事  >  バックエンド開発  >  PHP ハイブリッド Go コルーチンの同時実行性

PHP ハイブリッド Go コルーチンの同時実行性

高洛峰
高洛峰オリジナル
2016-10-31 13:26:542153ブラウズ

アイデアはシンプルです。 runtime.GOMAXPROCS(1) を設定すると、golang プロセスがシングルスレッドになります。 Python で gevent を使用した場合の効果と似ています。次に、複数のコルーチンをスケジュールすることによって、非同期 I/O の同時実行性が実現されます。 PHP は Go プロセスのサブ関数として実行されます。PHP が他のコルーチンに譲る必要がある場合は、Golang 関数をコールバックすることで実装できます。 go によって提供されるサブ関数を PHP から呼び出すと、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 これらの関数は私によって追加され、取得された 3 つのグローバル コンテキストは PHP (EG/SG/PG) です (参照: http://www.cnblogs.com/chance. ..)。 github.com/deuill/go-php の変更されたソース コードは次のとおりです: https://github.com/taowen/go-...

php/go ハイブリッド コルーチンの完全なデモ:

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 {<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

sleep 1 が 2 つ確認できますが、最終的に使用されたのは 1.00099211 だけです。コルーチンが並行していることを示します。

いくつかのパフォーマンス指標。 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 を 1 つのアプリケーションに混在させます。

そして、php 呼び出しに golang 関数を提供することで、非同期 I/O を実現できます。 libcurl のような拡張機能自体は非同期コールバックをサポートしていますが、PHP は同期であるため、同期実行のみが PHP に公開されます。 Golang を使用すると、execute を非同期実行 + コールバックのラッパーにして、コルーチン ベースのスケジューリングを実現できます。


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。