- 如果一开始是使用
PHP的朋友,写多了就会有一些疑惑 - 两次请求先后顺序的请求该怎么产生影响.
 - 在很多语言中, 可以很轻松的实现,无非是用一个全局变量来共享
 - 但是在我们写的
PHP中,不同的请求之间是完全独立的 - 这样的设计有好处也有坏处
- 坏:每次启动都要重新加载代码,配置
 - 好:不用考虑内存泄漏的问题,请求结束自动释放所有
 
 - 想要两次独立的请求产生影响,但并不是说
PHP做不到, 我们也可以使用Redis之类的来达到共享内存使用 
假如我们有以下代码
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")
}
- 当我们访问http://127.0.0.1:8888/ping
 - 如果我们用
PHP的思想,预想可能会在控制台打印 1~5, 然后浏览器响应pong,请求结束 - 实际的结果, 控制台会不断的打印数字累加,直到我们关闭服务. 因为即可当前请求结束了(5s之后), 但是由于主进程还在, 所以这个
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")
}
- 我们只要读取
c.Request.Context().Done()状态即可 - 如果请求
handle还在处理任务,那么我们就不断打印 - 当请求结束, 我们一同结束
goroutine 
- 从以上就能看出,实际上我们写的
PHP代码都是在goroutine handle的生命周期. - 如果我们想要在
PHP中实现用户注册之后异步发送邮件, 那么就需要借助其它办法实现(redis 队列) - 但在
Go中可以独立开启一个goroutine去执行任何想要的操作,并且我们也可以在这个goroutine判断该什么时候结束. 
Context 参数引用问题
package main
import (
    "context"
    "fmt"
    "github.com/gin-gonic/gin"
    "github.com/go-redis/redis/v8"
    "net/http"
)
var ctx = context.Background()
func main()  {
    host := ""
    port := 0
    password := ""
    client := redis.NewClient(&redis.Options{
        Addr:     fmt.Sprintf("%s:%d", host, port),
        Password: password, // no password set
        DB:       0,        // use default DB
    })
    g := gin.Default()
    g.GET("/ping", func(c *gin.Context) {
        var msg string
        // 1
        msg = client.Get(ctx, "ping").String()
        // 2
        msg = client.Get(context.Background(), "ping").String()
        // 3
        msg = client.Get(c, "ping").String()
        // 4
        msg = client.Get(c.Request.Context(), "ping").String()
        c.String(http.StatusOK, msg)
    })
}
> 4 is the right choice.
1 is is kinda wrong because it uses one global context. Usually you want a separate context for each request.
2 creates empty context which kinda pointless.
3 uses Gin context which can't be used as a full replacement for context.Context.
https://github.com/go-redis/redis/issues/1594