- For developers transitioning from PHP, some common questions arise:
- How can sequential requests affect each other?
- In many programming languages, this could be easily achieved using global variables
- However, in PHP, each request is completely independent
- This design has both advantages and disadvantages:
- Downside: Requires reloading code and configurations on each request
- Advantage: Automatic memory cleanup after request completion eliminates leakage concerns
- While PHP can achieve cross-request effects using tools like Redis, the approaches differ fundamentally
Sample Go Code Analysis
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func main() {
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
go func() {
var i int
for {
i++
fmt.Println(i)
time.Sleep(time.Second)
}
}()
time.Sleep(time.Second * 5)
c.String(http.StatusOK, "pong")
})
router.Run(":8888")
}
- Accessing http://127.0.0.1:8888/ping
- PHP expectation: Console prints 1-5 then returns response
- Go reality: Numbers keep incrementing indefinitely until server shutdown
- Solution using Context-aware goroutine:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
func main() {
router := gin.Default()
router.GET("/ping", func(c *gin.Context) {
go func() {
var i int
for {
select {
case <-c.Request.Context().Done():
return
default:
i++
fmt.Println(i)
time.Sleep(time.Second)
}
}
}()
time.Sleep(time.Second * 5)
c.String(http.StatusOK, "pong")
})
router.Run(":8888")
}
- Key mechanism: Monitoring
c.Request.Context().Done()
status - Goroutine terminates automatically when request completes
- PHP executes within the request handler’s lifecycle
- Asynchronous operations in PHP require external tools (e.g., Redis queues)
- Go natively supports controlled concurrency via goroutines and Context
Context Parameter Reference Patterns
package main
import (
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-redis/redis/v8"
"net/http"
)
var ctx = context.Background()
func main() {
// Redis client setup omitted
g := gin.Default()
g.GET("/ping", func(c *gin.Context) {
var msg string
// Option 1: Global context
msg = client.Get(ctx, "ping").String()
// Option 2: Empty context
msg = client.Get(context.Background(), "ping").String()
// Option 3: Gin context
msg = client.Get(c, "ping").String()
// Option 4: Request context (Correct)
msg = client.Get(c.Request.Context(), "ping").String()
c.String(http.StatusOK, msg)
})
}
Expert Analysis:
> 4 is the right choice.
1 is problematic due to using a global context
2 creates a pointless empty context
3 incorrectly uses Gin context instead of context.Context
Reference: https://github.com/go-redis/redis/issues/1594