我无法通过一个计时器来跳出一个 for 循环。

huangapple go评论73阅读模式
英文:

I cannot escape a for loop with a ticker

问题

我有一个API,它会每秒钟通过web-socket扫描司机的位置并发送。问题是,当客户端断开连接时,循环无法退出。它似乎会一直运行下去。我正在使用Gin和nhooyr websocket库。

var GetDriverLocations = func(c *gin.Context) {

    wsoptions := websocket.AcceptOptions{InsecureSkipVerify: true}
    wsconn, err := websocket.Accept(c.Writer, c.Request, &wsoptions)
    if err != nil {
        return
    }

    defer wsconn.Close(websocket.StatusInternalError, "the sky is falling")

    driverLocation := &models.DriverLocation{}

    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
        case <-c.Request.Context().Done():
            fmt.Println("done") //这句话永远不会被打印出来
            return

        }
        coords, err := driverLocation.GetDrivers()
        if err != nil {
            break
        }
        err = wsjson.Write(c.Request.Context(), wsconn, &coords)

        if websocket.CloseStatus(err) == websocket.StatusNormalClosure {
            break
        }
        if err != nil {
            break
        }
    }

    fmt.Println("conn ended") //这句话永远不会被打印出来

}

我还尝试了这个循环,但也有同样的问题:

for range ticker.C{

    coords, err := driverLocation.GetDrivers()
    if err != nil {
        break
    }
    err = wsjson.Write(c.Request.Context(), wsconn, &coords)

    if websocket.CloseStatus(err) == websocket.StatusNormalClosure {
        break
    }
    if err != nil {
        break
    }
}
英文:

I have this API that scans for drivers' locations and send them via web-socket every 1 second. The issue is that the loop cannot be escaped when client disconnects. It seems it is alive for ever. I am using Gin with nhooyr websocket library.

var GetDriverLocations = func(c *gin.Context) {

    wsoptions := websocket.AcceptOptions{InsecureSkipVerify: true}
    wsconn, err := websocket.Accept(c.Writer, c.Request, &amp;wsoptions)
    if err != nil {
    	return
    }

    defer wsconn.Close(websocket.StatusInternalError, &quot;the sky is falling&quot;)

    driverLocation := &amp;models.DriverLocation{}

    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()
    for {
    	select {
    	case &lt;-ticker.C:
	    case &lt;-c.Request.Context().Done():
	    	fmt.Println(&quot;done&quot;) //this never gets printed
	    	return

	    }
	    coords, err := driverLocation.GetDrivers()
	    if err != nil {
	    	break
	    }
	    err = wsjson.Write(c.Request.Context(), wsconn, &amp;coords)

    	if websocket.CloseStatus(err) == websocket.StatusNormalClosure {
    		break
    	}
    	if err != nil {
    		break
    	}
    }

    fmt.Println(&quot;conn ended&quot;) //this never gets printed

}

I also tried this loop but also has the same issue:

for range ticker.C{

    coords, err := driverLocation.GetDrivers()
    if err != nil {
        break
    }
    err = wsjson.Write(c.Request.Context(), wsconn, &amp;coords)

    if websocket.CloseStatus(err) == websocket.StatusNormalClosure {
        break
    }
    if err != nil {
        break
    }
}

答案1

得分: 0

因为网络连接被nhooyr websocket库从net/http服务器劫持,所以直到处理程序返回之前,上下文c.Request.Context()不会被取消。

调用CloseRead以获取在连接关闭时取消的上下文。在循环中使用该上下文。

var GetDriverLocations = func(c *gin.Context) {

    wsoptions := websocket.AcceptOptions{InsecureSkipVerify: true}
    wsconn, err := websocket.Accept(c.Writer, c.Request, &wsoptions)
    if err != nil {
        return
    }
    defer wsconn.Close(websocket.StatusInternalError, "")
    ctx := wsconn.CloseRead(c.Request.Context())

    driverLocation := &models.DriverLocation{}

    ticker := time.NewTicker(time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-ticker.C:
        case <-ctx.Done():
            return
        }
        coords, err := driverLocation.GetDrivers()
        if err != nil {
            break
        }
        err = wsjson.Write(c.Request.Context(), wsconn, &coords)
        if err != nil {
            break
        }
    }
}
英文:

Because the network connection is hijacked from the net/http server by the nhooyr websocket library, the context c.Request.Context() is not canceled until handler returns.

Call CloseRead to get a context that's canceled when the connection is closed. Use that context in the loop.

var GetDriverLocations = func(c *gin.Context) {

	wsoptions := websocket.AcceptOptions{InsecureSkipVerify: true}
	wsconn, err := websocket.Accept(c.Writer, c.Request, &amp;wsoptions)
	if err != nil {
		return
	}
	defer wsconn.Close(websocket.StatusInternalError, &quot;&quot;)
	ctx := wsconn.CloseRead(c.Request.Context())

	driverLocation := &amp;models.DriverLocation{}

	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()

	for {
		select {
		case &lt;-ticker.C:
		case &lt;-ctx.Done():
			return
		}
		coords, err := driverLocation.GetDrivers()
		if err != nil {
			break
		}
		err = wsjson.Write(c.Request.Context(), wsconn, &amp;coords)
		if err != nil {
			break
		}
	}
}

huangapple
  • 本文由 发表于 2021年10月31日 04:10:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/69781787.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定