028-86922220

建站动态

根据您的个性需求进行定制 先人一步 抢占小程序红利时代

PHP混合Go协程并发

想法很简单。通过设置 runtime.GOMAXPROCS(1) 让 golang 的进程变成单线程执行的。类似python用gevent的效果。然后通过调度多个协程实现异步I/O并发。php作为一个子函数跑在go的进程内,php需要yield到其他协程时,通过回调到golang函数来实现。从php里调用go提供的子函数时,go保证保存php的当前上下文。当协程执行权让渡回来的时候,把原来的php上下文恢复。关键的代码在:

创新互联坚信:善待客户,将会成为终身客户。我们能坚持多年,是因为我们一直可值得信赖。我们从不忽悠初访客户,我们用心做好本职工作,不忘初心,方得始终。10年网站建设经验创新互联是成都老牌网站营销服务商,为您提供成都网站设计、网站建设、网站设计、H5网站设计、网站制作、品牌网站建设、小程序开发服务,给众多知名企业提供过好品质的建站服务。

 
 
  1.  // 保存当前协程上的php上下文    
  2. oldServerCtx := engine.ServerContextGet() 
  3.     fmt.Println(oldServerCtx) 
  4.     defer engine.ServerContextSet(oldServerCtx) 
  5.     oldExecutorCtx := engine.ExecutorContextGet() 
  6.     fmt.Println(oldExecutorCtx) 
  7.     defer engine.ExecutorContextSet(oldExecutorCtx) 
  8.     oldCoreCtx := engine.CoreContextGet() 
  9.     fmt.Println(oldCoreCtx) 
  10.     defer engine.CoreContextSet(oldCoreCtx) 
  11.  
  12. // 放弃全局的锁,使得其他的协程可以开始执行php 
  13.     engineLock.Unlock() 
  14.     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:

 
 
  1. package main 
  2.  
  3. import ( 
  4.     "fmt" 
  5.     "github.com/deuill/go-php/engine" 
  6.     "os" 
  7.     "runtime" 
  8.     "time" 
  9.     "sync" 
  10. ) 
  11.  
  12. type TestObj struct{} 
  13.  
  14. func newTestObj(args []interface{}) interface{} { 
  15.     return &TestObj{} 
  16. } 
  17. var engineLock *sync.Mutex 
  18.  
  19. func (self *TestObj) Hello() { 
  20.     oldServerCtx := engine.ServerContextGet() 
  21.     fmt.Println(oldServerCtx) 
  22.     defer engine.ServerContextSet(oldServerCtx) 
  23.     oldExecutorCtx := engine.ExecutorContextGet() 
  24.     fmt.Println(oldExecutorCtx) 
  25.     defer engine.ExecutorContextSet(oldExecutorCtx) 
  26.     oldCoreCtx := engine.CoreContextGet() 
  27.     fmt.Println(oldCoreCtx) 
  28.     defer engine.CoreContextSet(oldCoreCtx) 
  29.     engineLock.Unlock() 
  30.     defer engineLock.Lock() 
  31.     time.Sleep(time.Second) 
  32.     fmt.Println("sleep done") 
  33. } 
  34.  
  35. func main() { 
  36.     runtime.GOMAXPROCS(1) 
  37.     theEngine, err := engine.New() 
  38.     engineLock = &sync.Mutex{} 
  39.     if err != nil { 
  40.         fmt.Println(err) 
  41.     } 
  42.     _, err = theEngine.Define("TestObj", newTestObj) 
  43.     wg := &sync.WaitGroup{} 
  44.     wg.Add(2) 
  45.     before := time.Now() 
  46.     fmt.Println("1") 
  47.     go func() { 
  48.         engineLock.Lock() 
  49.         defer engineLock.Unlock() 
  50.         context1, err := theEngine.NewContext() 
  51.         if err != nil { 
  52.             fmt.Println(err) 
  53.         } 
  54.         context1.Output = os.Stdout 
  55.         if err != nil { 
  56.             fmt.Println(err) 
  57.         } 
  58.         fmt.Println("1 enter") 
  59.         _, err = context1.Eval("$testObj = new TestObj(); $testObj->Hello();") 
  60.         fmt.Println("1 back") 
  61.         if err != nil { 
  62.             fmt.Println(err) 
  63.         } 
  64.         //theEngine.DestroyContext(context1) 
  65.         fmt.Println("1 done") 
  66.         wg.Done() 
  67.     }() 
  68.     fmt.Println("2") 
  69.     go func() { 
  70.         engineLock.Lock() 
  71.         defer engineLock.Unlock() 
  72.         context2, err := theEngine.NewContext() 
  73.         if err != nil { 
  74.             fmt.Println(err) 
  75.         } 
  76.         if err != nil { 
  77.             fmt.Println(err) 
  78.         } 
  79.         context2.Output = os.Stdout 
  80.         fmt.Println("2 enter") 
  81.         _, err = context2.Eval("$testObj = new TestObj(); $testObj->Hello();") 
  82.         fmt.Println("2 back") 
  83.         if err != nil { 
  84.             fmt.Println(err) 
  85.         } 
  86.         //theEngine.DestroyContext(context2) 
  87.         fmt.Println("2 done") 
  88.         wg.Done() 
  89.     }() 
  90.     wg.Wait() 
  91.     after := time.Now() 
  92.     fmt.Println(after.Sub(before)) 
  93. }  

执行结果是

 
 
  1. 1 
  2. 2 
  3. 2 enter 
  4. {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]} 
  5. {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] [   ]} 
  6. {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]} 
  7. 1 enter 
  8. {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]} 
  9. {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] [   ]} 
  10. {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]} 
  11. sleep done 
  12. 1 back 
  13. 1 done 
  14. sleep done 
  15. 2 back 
  16. 2 done 
  17. 1.00099211s 

可以看到两个sleep 1s,最终只用了1.00099211s。说明协程是并发的。

一些性能指标。走http调用后端,在i7-6700k上,用ab -n 100 -c 4 可以跑出这样的结果

 
 
  1. Requests per second:    3183.70 [#/sec] (mean) 
  2. Time per request:       1.256 [ms] (mean) 
  3. Time per request:       0.314 [ms] (mean, across all concurrent requests)  

如果不用http调用后端,直接php=>go返回"hello",则可以达到

 
 
  1. Requests per second: 10073.54 [#/sec] (mean) 
  2.  
  3. Time per request: 0.397 [ms] (mean) 
  4.  
  5. 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的包装,从而实现基于协程的调度。


网站题目:PHP混合Go协程并发
当前URL:http://www.tsicrk.com/article/cdesjje.html

其他资讯

让你的专属顾问为你服务

2.7963s