Golang服务器发送事件 – 服务器发送事件处理程序是否会不断调用自身?

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

Golang Server Sent Events - Does Server Sent Event Handler Continually Call Itself?

问题

我正在学习服务器发送事件(Server Sent Events),我有一个简单的程序,它会递增一个计数器变量并将其推送给客户端。但是我不明白其中的一些问题。有人告诉我数据只会被推送给客户端,而不是由客户端拉取或请求。那么在下面的代码中会发生什么呢?/sse/dashboard 处理程序是每秒钟调用自身吗?因为看起来是这样的。它是如何知道要持续"调用自身"的呢?下面是我的服务器端代码,然后是我的客户端代码:

服务器端:

package main

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

var counter int

func main() {
	http.Handle("/", http.FileServer(http.Dir("client")))
	http.Handle("/sse/dashboard", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "text/event-stream")
		w.Header().Set("Cache-Control", "no-cache")
		w.Header().Set("Connection", "keep-alive")
		counter++
		fmt.Fprintf(w, "data: %v\n\n", counter)
	}))
	log.Fatal(http.ListenAndServe(":8080", nil))
}

客户端:

<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
  <h1>Counter: <span id="counter_val"></span></h1>
  <script>
    var source = new EventSource("/sse/dashboard");
    source.onmessage = function (event) {
        var counter = JSON.parse(event.data);
        document.getElementById("counter_val").innerHTML = counter;
    }
  </script>
</body>
</html>
英文:

I'm learning about server sent events and I've a simple program that increments a counter variable and pushes to client. But I don't understand something. I'm told that data is only pushed to the client. Not pulled or requested from client. So what happens in the below code. Does the /sse/dashboard handler call itself every second. Because that's how it would appear. How does it know to continually 'call itself'? Below is my server side code, followed by my client side code:

Server Side:

package main

import (
	&quot;fmt&quot;
	&quot;log&quot;
	&quot;net/http&quot;
)

var counter int

func main() {
	http.Handle(&quot;/&quot;, http.FileServer(http.Dir(&quot;client&quot;)))
	http.Handle(&quot;/sse/dashboard&quot;, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set(&quot;Content-Type&quot;, &quot;text/event-stream&quot;)
		w.Header().Set(&quot;Cache-Control&quot;, &quot;no-cache&quot;)
		w.Header().Set(&quot;Connection&quot;, &quot;keep-alive&quot;)
		counter++
		fmt.Fprintf(w, &quot;data: %v\n\n&quot;, counter)
	}))
	log.Fatal(http.ListenAndServe(&quot;:8080&quot;, nil))
}

Client Side:

&lt;html lang=&quot;en&quot;&gt;
&lt;head&gt;
  &lt;meta charset=&quot;utf-8&quot;&gt;
  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;
&lt;/head&gt;

&lt;body&gt;
  &lt;h1&gt;Counter: &lt;span id=&quot;counter_val&quot;&gt;&lt;/span&gt;&lt;/h1&gt;
  &lt;script&gt;
    var source = new EventSource(&quot;/sse/dashboard&quot;);
    source.onmessage = function (event) {
        var counter = JSON.parse(event.data);
        document.getElementById(&quot;counter_val&quot;).innerHTML = counter;
    }
  &lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;

答案1

得分: 2

处理程序仅在客户端发出对/sse/dashboard的请求时才被调用。处理程序不会自行调用。

客户端的EventSource在服务器关闭事件流后会自动重新连接。服务器在第一条消息后关闭事件流。您会看到一个递增的计数器,因为客户端每一秒或两秒重新连接一次。

使用循环发送递增的计数器事件。当客户端断开连接时,从处理程序返回。将响应写入器缓冲的输出刷新到网络。使用互斥锁来防止counter上的数据竞争。

var (
    mu sync.Mutex
    counter int
)

...

http.Handle("/sse/dashboard", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    flusher, ok := w.(http.Flusher)
    if !ok {
        http.Error(w, "Internal error", 500)
        return
    }
    w.Header().Set("Content-Type", "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")
    t := time.NewTicker(time.Minute)
    defer t.Stop()
    for {
        select {
        case <-t.C:
            mu.Lock()
            counter++
            c := counter
            mu.Unlock()
            fmt.Fprintf(w, "data: %v\n\n", c)
            flusher.Flush()
        case <-r.Context().Done():
            return
        }
    }
}))
英文:

The handler is only invoked when a client issues a request to /sse/dashboard. A handler does not invoke itself.

The client's EventSource automatically reconnects after the server closes the event stream. The server close the event stream after the first message. You see an incrementing counter because the client reconnects every second or two.

Use a loop to send incrementing counter events. Return from the handler when the client disconnects. Flush the output buffered by the response writer to the network. Use a mutex to prevent the data race on counter.

 var (
     mu sync.Mutex
     counter int
 )


 ...

http.Handle(&quot;/sse/dashboard&quot;, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	flusher, ok := w.(http.Flusher)
	if !ok {
		http.Error(w, &quot;Internal error&quot;, 500)
		return
	}
	w.Header().Set(&quot;Content-Type&quot;, &quot;text/event-stream&quot;)
	w.Header().Set(&quot;Cache-Control&quot;, &quot;no-cache&quot;)
	w.Header().Set(&quot;Connection&quot;, &quot;keep-alive&quot;)
	t := time.NewTicker(time.Minute)
	defer t.Stop()
	for {
		select {
		case &lt;-t.C:
			mu.Lock()
			counter++
			c := counter
			mu.Unlock()
			fmt.Fprintf(w, &quot;data: %v\n\n&quot;, c)
			flusher.Flush()
		case &lt;-r.Context().Done():
			return
		}
	}
}))

huangapple
  • 本文由 发表于 2021年11月5日 23:02:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/69855315.html
匿名

发表评论

匿名网友

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

确定