当浏览器意外关闭时,如何在Golang中通过WebSocket接收通知?

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

golang websocket how to be notified when browser closed unexpectly

问题

我正在使用golang编写一个WebSocket服务。该程序使用gollira WebSocket库来接受WebSocket请求,并在每个请求处理程序中监听RabbitMQ队列以获取消息。

问题是,当我关闭浏览器窗口时,处理程序线程仍然在运行,我猜测有一种机制可以在连接断开时通知。

我尝试监听通道request.Context().Done(),但它没有起作用。

以下是代码的翻译:

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/gorilla/websocket"
	"github.com/streadway/amqp"
)

var (
	addr     = "localhost:9999"
	upgrader = websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool { return true },
	}
)

var conn *amqp.Connection

func watch(w http.ResponseWriter, r *http.Request) {
	ns := r.URL.Query().Get("ns")
	if ns == "" {
		return
	}
	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	defer c.Close()

	ch, err := conn.Channel()
	failOnError(err, "打开通道失败")
	defer ch.Close()

	err = ch.ExchangeDeclare(
		"notify",    // 名称
		"fanout",    // 类型
		true,        // durable
		false,       // auto-deleted
		false,       // internal
		false,       // no-wait
		nil,         // 参数
	)
	failOnError(err, "声明交换机失败")

	q, err := ch.QueueDeclare(
		"",     // 名称
		false,  // durable
		false,  // delete when unused
		true,   // exclusive
		false,  // no-wait
		nil,    // 参数
	)
	failOnError(err, "声明队列失败")

	err = ch.QueueBind(
		q.Name,              // 队列名称
		ns,                  // 路由键
		"dsm_tasks_notify",  // 交换机
		false,
		nil)
	failOnError(err, "绑定队列失败")

	msgs, err := ch.Consume(
		q.Name, // 队列
		"",     // 消费者
		true,   // 自动确认
		false,  // 独占
		false,  // 不接收本地发布的消息
		false,  // no-wait
		nil,    // 参数
	)
	failOnError(err, "注册消费者失败")

	for {
		select {
		case d := <-msgs:
			err = c.WriteMessage(websocket.TextMessage, d.Body)
			if err != nil {
				log.Println("写入消息失败:", err)
				break
			}
		case <-r.Context().Done():
			log.Println("连接断开")
			return
		}
	}

}

func failOnError(err error, msg string) {
	if err != nil {
		log.Fatalf("%s: %s", msg, err)
		panic(fmt.Sprintf("%s: %s", msg, err))
	}
}

func main() {
	var err error
	conn, err = amqp.Dial("amqp://guest:guest@localhost:5672/")
	failOnError(err, "连接RabbitMQ失败")
	defer conn.Close()

	http.HandleFunc("/watch", watch)
	log.Fatal(http.ListenAndServe(addr, nil))
}

希望对你有帮助!

英文:

I am writing a websocket service in golang.
The program use gollira websocket to accept ws request, and in each request handler, it listen to rabbitmq queue for messages.

The problem is, when i close browser window, the handler thread is still running, i guess there is an mechanism to be notified when connection disconnected.

I try to listen to channel request.Context().Done(), when i doesn't work.

package main
import (
&quot;fmt&quot;
&quot;log&quot;
&quot;net/http&quot;
&quot;github.com/gorilla/websocket&quot;
&quot;github.com/streadway/amqp&quot;
)
var (
addr     = &quot;localhost:9999&quot;
upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
}
)
var conn *amqp.Connection
func watch(w http.ResponseWriter, r *http.Request) {
ns := r.URL.Query().Get(&quot;ns&quot;)
if ns == &quot;&quot; {
return
}
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print(&quot;upgrade:&quot;, err)
return
}
defer c.Close()
ch, err := conn.Channel()
failOnError(err, &quot;Failed to open a channel&quot;)
defer ch.Close()
err = ch.ExchangeDeclare(
&quot;notify&quot;, // name
&quot;fanout&quot;,           // type
true,               // durable
false,              // auto-deleted
false,              // internal
false,              // no-wait
nil,                // arguments
)
failOnError(err, &quot;Failed to declare an exchange&quot;)
q, err := ch.QueueDeclare(
&quot;&quot;,    // name
false, // durable
false, // delete when usused
true,  // exclusive
false, // no-wait
nil,   // arguments
)
failOnError(err, &quot;Failed to declare a queue&quot;)
err = ch.QueueBind(
q.Name,             // queue name
ns,                 // routing key
&quot;dsm_tasks_notify&quot;, // exchange
false,
nil)
failOnError(err, &quot;Failed to bind a queue&quot;)
msgs, err := ch.Consume(
q.Name, // queue
&quot;&quot;,     // consumer
true,   // auto-ack
false,  // exclusive
false,  // no-local
false,  // no-wait
nil,    // args
)
failOnError(err, &quot;Failed to register a consumer&quot;)
for {
select {
case d := &lt;-msgs:
err = c.WriteMessage(websocket.TextMessage, d.Body)
if err != nil {
log.Println(&quot;write:&quot;, err)
break
}
case &lt;-r.Context().Done():
log.Println(&quot;Disconnect&quot;)
return
}
}
}
func failOnError(err error, msg string) {
if err != nil {
log.Fatalf(&quot;%s: %s&quot;, msg, err)
panic(fmt.Sprintf(&quot;%s: %s&quot;, msg, err))
}
}
func main() {
var err error
conn, err = amqp.Dial(&quot;amqp://guest:guest@localhost:5672/&quot;)
failOnError(err, &quot;Failed to connect to RabbitMQ&quot;)
defer conn.Close()
http.HandleFunc(&quot;/watch&quot;, watch)
log.Fatal(http.ListenAndServe(addr, nil))
}

答案1

得分: 4

如果浏览器干净地关闭了连接,那么在读取webssocket连接时会返回一个错误。像处理任何读取错误一样清理websocket连接。

应用程序必须PING连接并期望相应的PONG来检测其他情况。聊天示例展示了如何发送PING并接收PONG。

英文:

If the browser cleanly closes the connection, then read on the webssocket connection returns an error. Cleanup the websocket connection as you would on any read error.

The application must PING the connection and expect the corresponding PONGs to detect other situations. The chat example shows how to send PINGs and receive PONGs.

huangapple
  • 本文由 发表于 2017年6月8日 12:24:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/44426606.html
匿名

发表评论

匿名网友

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

确定