Мой серверный код на golang
package main
import (
"fmt"
"log"
"net/http"
"github.com/rs/cors"
)
func main() {
mux := http.NewServeMux();
mux.HandleFunc("/sse" , handleSse)
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{http.MethodGet, http.MethodPost,
http.MethodDelete , http.MethodPut},
AllowCredentials: true,
})
handler := c.Handler(mux)
log.Fatal(http.ListenAndServe(":6969" , handler))
}
func handleSse(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")
f , ok := w.(http.Flusher);
if !ok{
http.Error( w , "SSE not supported" ,
http.StatusBadRequest)
return;
}
fmt.Fprintf(w,"data:%v\n\n","sample data");
f.Flush();
}
клиентский код
<!DOCTYPE html>
<html lang = "en">
<head>
<title>SSE</title>
</head>
<body>
SSE running
<script>
const event = new EventSource("http://localhost:6969/sse");
event.onmessage = () =>{
console.info("this dude is slow");
};
</script>
</body>
</html>
У меня проблема в том, что на вкладке «Сеть» новый текстовый поток или ответ приходит через 5,4 секунды. Я хочу, чтобы сервер отправлял ответ каждые 2 секунды Я пробовал бесконечный цикл for в серверном коде, который показан в каком-то руководстве, и он не работает.
РЕДАКТИРОВАТЬ : Функция обработчика цикла for
func handleSse(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")
w.WriteHeader(http.StatusOK)
f , ok := w.(http.Flusher);
if !ok{
http.Error( w , "SSE not supported , IE6 bruh" ,
http.StatusBadRequest)
return;
}
for i := 0 ; i < 10 ; i++{
fmt.Fprintln(w,"retry : 1000"); //This line also doesnot help
fmt.Fprintf(w,"data :%v\n\n","Sorry");
f.Flush();
//time.Sleep(1 * time.Second) //This line increase delay to 25 secs
}
}
Я пытался указать время переподключения, но это не помогает. Задержка по-прежнему составляет 5 секунд. Я также отредактировал вопрос и добавил код цикла.
Расстояние важно — fmt.Fprintf(w,"data :%v\n\n","Sorry");
не подойдет; так и должно быть fmt.Fprintf(w, "data:%v\n\n", "Sorry")
.
Ниже приведен рабочий пример; он отправит 10 сообщений, а затем разорвет соединение. Поскольку он также устанавливает retry: 10000
(повторите попытку 10000ms
, т. е. 10s
), браузер повторно подключится через 10 секунд (и получит еще 10 сообщений с интервалом в секунду). Обратите внимание, что я добавил к сообщениям метку времени (что делает вывод более понятным, поскольку браузеры имеют тенденцию объединять одинаковые строки вывода).
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/rs/cors"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, _ *http.Request) {
w.Write([]byte(`<!DOCTYPE html>
<html lang = "en">
<head>
<title>SSE</title>
</head>
<body>
SSE running
<script>
const event = new EventSource("http://localhost:6969/sse");
event.onmessage = () =>{
console.info("this dude is slow");
};
</script>
</body>
</html>
`))
})
mux.HandleFunc("/sse", handleSse)
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{http.MethodGet, http.MethodPost,
http.MethodDelete, http.MethodPut},
AllowCredentials: true,
})
handler := c.Handler(mux)
log.Fatal(http.ListenAndServe(":6969", handler))
}
func handleSse(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")
w.WriteHeader(http.StatusOK)
f, ok := w.(http.Flusher)
if !ok {
http.Error(w, "SSE not supported , IE6 bruh",
http.StatusBadRequest)
return
}
for i := 0; i < 10; i++ {
fmt.Fprintln(w, "retry: 10000") // This means that after the connection drops the browser will wait 10 seconds before reconnecting
fmt.Fprintf(w, "data:%v\n\n", "Sorry")
f.Flush()
time.Sleep(1 * time.Second) // This means a message will be sent every second (until 10 have been sent when the connection will drop)
}
}
В реальной системе обработчик (handleSse
в данном случае) вполне может выйти только в том случае, если при записи он получит ошибку, в противном случае он останется активным, пока программа работает. Такой обработчик будет откуда-то получать данные, возможно, по каналу, и отправлять их браузеру.
Обратите внимание: мое первоначальное предложение добавить fmt.Fprintln(w, "retry: 1000")
было попыткой прояснить происходящее. С вашим исходным кодом браузер подключался, получал одно сообщение (после чего сервер разрывал соединение), а затем повторно подключался через 5 секунд (чтобы получить еще одно сообщение...).
В дополнение к ответу @Britis, запускать бесконечное количество раз каждые 2 секунды. Я использую тикер golang и контекст, чтобы остановить цикл в случае отключения клиента или выключения сервера.
package main
import (
"fmt"
"log"
"net/http"
"time"
"github.com/rs/cors"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/sse", handleSse)
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{http.MethodGet, http.MethodPost,
http.MethodDelete, http.MethodPut},
AllowCredentials: true,
})
handler := c.Handler(mux)
log.Fatal(http.ListenAndServe(":6969", handler))
}
func handleSse(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")
w.WriteHeader(http.StatusOK)
f, ok := w.(http.Flusher)
if !ok {
http.Error(w, "SSE not supported , IE6 bruh",
http.StatusBadRequest)
return
}
// Send retry directive to client
fmt.Fprintln(w, "retry: 10000")
flusher.Flush()
// Send events to the client every 2 seconds
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
for {
select {
case <-r.Context().Done():
fmt.Println("The client has disconnected or the server is shutting down"
return
case <-ticker.C:
fmt.Fprintf(w, "data: %v\n\n", "Sorry")
flusher.Flush()
}
}
}
«Я пробовал бесконечный цикл for в серверном коде» — покажите нам это. Ваш текущий код отправит одно сообщение, а затем закроет соединение (поэтому задержка между сообщениями — это время повторного соединения ; вы можете доказать это, добавив
fmt.Fprintln(w, "retry: 1000")
над строкой, которая отправляетdata
). С помощью SSE вы хотите поддерживать соединение открытым — хороший пример см. в этом ответе.