在websockets之前,我们进行了轮询.这实际上意味着定期向客户端(每隔几秒,或对您的应用程序有意义的任何时间段),向服务器发出请求以找出作业的状态.
许多人使用的优化是"长"轮询.这涉及让服务器接受请求,并在服务器内部检查更改,并在没有服务器时休眠,直到达到特定超时或发生所需事件,然后将消息发送回客户端.
如果达到超时,则关闭连接并且客户端需要发出另一个请求.服务器代码看起来如下所示,假设函数根据其名称和签名做了明智的事情:
import (
"net/http"
"time"
)
func PollingHandler(w http.ResponseWriter, r *http.Request) {
jobID := getJobID(r)
for finish := 60; finish > 0; finish-- { // iterate for ~1 minute
status, err := checkStatus(jobID)
if err != nil {
writeError(w, err)
return
}
if status != nil {
writeStatus(w, status)
return
}
time.Sleep(time.Second) // sleep 1 second
}
writeNil(w) // specific response telling client to request again.
}
Run Code Online (Sandbox Code Playgroud)
处理超时的更好方法是使用上下文包并创建具有超时的上下文.这看起来像是这样的:
import (
"net/http"
"time"
"golang.org/x/net/context"
)
func PollingHandler(w http.ResponseWriter, r *http.Request) {
jobID := getJobID(r)
ctx := context.WithTimeout(context.Background(), time.Second * 60)
for {
select{
case <-ctx.Done():
writeNil(w)
default:
status, err := checkStatus(jobID)
if err != nil {
writeError(w, err)
return
}
if status != nil {
writeStatus(w, status)
return
}
time.Sleep(time.Second) // sleep 1 second
}
}
}
Run Code Online (Sandbox Code Playgroud)
第二个版本将在更可靠的时间内返回,特别是在checkStatus
可能是较慢的呼叫的情况下.
您可以考虑使用HTML5文本/事件流即服务器端事件(SSE).问题中提到了SSE,http2会不会很好用?
关于SSE的一般文章
(IE是目前唯一不支持SSE的浏览器)
在下面的文章中,http2 push与SSE结合使用.文档被推送到客户端缓存中,SSE用于通知客户端可以从其缓存中检索哪些文档(=通过单个http2连接的服务器启动的请求):
SSE的基础知识:在服务器端,您从以下开始:
Content-Type: text/event-stream\n\n
Run Code Online (Sandbox Code Playgroud)
然后每次要向您发送的客户端发送更新
data: { "name": "value", "othername": "othervalue" }\n\n
Run Code Online (Sandbox Code Playgroud)
完成后,在关闭连接之前,您可以选择发送:
retry: 60000\n\n
Run Code Online (Sandbox Code Playgroud)
指示浏览器在60000毫秒后重试新连接
在浏览器中,连接如下所示:
var URL = "http://myserver/myeventstreamer"
if (!!window.EventSource) {
source = new EventSource(URL);
} else {
// Resort to xhr polling :(
alert ("This browser does not support Server Sent Events\nPlease use another browser")
}
source.addEventListener('message', function(e) {
console.log(e.data);
}, false);
source.addEventListener('open', function(e) {
// Connection was opened.
}, false);
source.addEventListener('error', function(e) {
if (e.readyState == EventSource.CLOSED) {
// Connection was closed.
}
}, false);
Run Code Online (Sandbox Code Playgroud)