The idea is simple. By setting the runtime. Gomaxprocs (1) Let the Golang process become single-threaded execution. Similar to Python with gevent effect. asynchronous I/O concurrency is then implemented by scheduling multiple threads. PHP runs in the go process as a sub-function, and PHP needs to yield to other threads, through callbacks to the Golang function. When you invoke the child function provided by go from PHP, go guarantees that the current context of PHP is saved. The original PHP context is restored when the co-process execution is allowed to return. The key code is:
Saves the PHP context oldserverctx on the current thread : = 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) //Discard global locks so that other threads can start executing PHP enginelock.unlock () defer enginelock.lock ()
Servercontextget These functions are I add, get the PHP (EG/SG/PG) These three global context (see: Http://www.cnblogs.com/chance ...). Modified github.com/deuill/go-php source code in: Https://github.com/taowen/go-...
Demo of the complete Php/go hybrid process:
Package main import ("FMT" "Github.com/deuill/go-php/engine" "OS" "Runtime" "Time" "Sync") Typ E 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: = Theengin E.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: = Theengin E.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))}
Execution results are
1 2 2 Enter {0x2cf2930 {
0
0 0 0 [0 0 0 0 0]
0 0
1000 [0 0 0 0]} {{
0 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] 1 0 0 [0 0]} 0x7f682f01b948 [
] 0x7f682f01ba60 0x7f682f01b960 0x7f682f167168 0x7f682f01ba88 {5 [0 0 0 0] 0 0x7f682f1972d8 0x7f682f1972d8 0x7f682f1 993f8 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 134217728-1 0 0 0 1 [0 0 0 0] 1024x768 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] 0 [0 0 0 0 0 0 0] 0x7f682ecc6270 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 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] 1 0 0 [0 0]} 0x7f682f01b948 [
] 0x7f682f01ba60 0x7f682f01b960 0x7f682802f110 0x7f682f01ba88 {5 [0 0 0 0] 0 0x7f682f197a00 0x7f682f197a00 0x7f682f1 98368 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 134217728-1 0 0 0 1 [0 0 0 0] 1024x768 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] 0 [0 0 0 0 0 0 0] 0x7f682ecc6270 0x2a009b0 1 [0 0 0 0 0 0 0]
0 [0 0 0 0 0 0 0]} Sleep do 1 back 1 do sleep done 2 back 2 done 1.00099211s
You can see two sleep 1s, and finally use only 1.00099211s. Indicates that the co-process is concurrent.
Some performance metrics. Go to the HTTP call back end, on i7-6700k, with Ab-n 100-c 4 can run out of such results
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)
If you do not use the HTTP call back end, direct php=>go return "Hello", you can achieve
Requests per second:10073.54 [#/sec] (mean) time per request:0.397 [MS] (mean) time per request:0.099 [MS] (MEA N, across all concurrent requests)
These metrics only illustrate the cost of the co-process switchover. The actual gain depends on the latency of the backend HTTP service, and if it takes a long time, the benefits can be significant through concurrent concurrency.
This experiment shows that an application server can be implemented with Golang instead of NGINX+PHP-FPM. It also provides a smooth migration path from PHP to Golang. Mix PHP and go in one app in two languages.
I/O can also be asynchronous by providing the Golang function to the PHP call. Extensions like Libcurl support asynchronous callbacks, except that PHP is synchronous, so only the synchronous execute is exposed to PHP. With Golang, execute can be transformed into an asynchronous execute+callback wrapper, enabling scheduling based on the co-scheduling.