服务器发起的请求
Server-initiated requests
我知道 HTTP 是一种请求-响应协议。简而言之,我的问题是客户端向服务器发出请求以启动一个长 运行 进程,我想通过包含进度信息的简单 JSON 消息通知客户端进度.
在 HTTP/1.1 中,我知道我可以使用 WebSocket 或服务器发送的事件 (SSE) 或长轮询。
现在我知道HTTP/2还不支持WebSocket
我的问题是,在 HTTP/2 上处理此类事情的最佳方式是什么?
在 HTTP/2 中是否有任何我不知道的新东西来处理服务器发起的请求?
如果重要的话,我正在使用 Go 语言。
在 websockets 之前我们有轮询。这实际上意味着让客户端定期(每隔几秒,或任何对您的应用程序有意义的时间段)向服务器发出请求以了解作业的状态。
许多人使用的优化是 "long" 轮询。这涉及让服务器接受请求,并在服务器内部检查更改,并在有 none 时休眠,直到达到特定超时或发生所需事件,然后将其消息返回给客户.
如果达到超时,连接将关闭,客户端需要发出另一个请求。服务器代码看起来像下面这样,假设函数根据它们的名称和签名做一些明智的事情:
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.
}
处理超时的更好方法是使用 context package 并创建具有超时的上下文。这看起来像:
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
}
}
}
第二个版本将在更可靠的时间内到达 return,尤其是在 checkStatus
调用速度较慢的情况下。
你可以考虑使用HTML5text/event-streama.k.a。服务器端事件(SSE)。问题中提到了 SSE,它不能与 http2 一起使用吗?
关于 SSE 的一般文章
- http://www.w3schools.com/html/html5_serversentevents.asp
- https://en.wikipedia.org/wiki/Server-sent_events
(IE是目前唯一不支持SSE的浏览器)
后面的文章中,http2推送结合了SSE。文档被推送到客户端缓存中,SSE 用于通知客户端可以从其缓存中检索哪些文档(= 服务器通过单个 http2 连接发起的请求):
SSE 基础知识:在服务器端,您从:
Content-Type: text/event-stream\n\n
然后每次您想向您发送的客户端发送更新
data: { "name": "value", "othername": "othervalue" }\n\n
完成后,在关闭连接之前,您可以选择发送:
retry: 60000\n\n
指示浏览器在 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);
如果您想将 JSON 消息作为文本发送,server-sent event (SSE) 是一个很好的方法。 SSE 旨在发送文本。所有事件数据均以 UTF-8 字符编码。缺点是这使得通过 SSE 发送二进制数据效率低下。
如果你想发送二进制数据,你可能对HTTP/2引入的Server Push机制感兴趣。服务器推送允许 HTTP/2 服务器主动向客户端发送任何类型的文件。它被称为服务器推送 "response",即使它是在客户端请求它之前发送的。客户端自动将通过服务器推送响应发送的文件存储在其缓存中。对文件的后续请求会立即从缓存中得到满足,而无需往返于服务器。
这是一种将二进制数据推送到网络浏览器的有效方法。问题在于,当服务器推送响应到达时,浏览器的文档对象模型 (DOM) 没有得到通知。浏览器只有在发出请求时才会发现数据在其缓存中。我们可以通过以下方式解决此问题。在使用 Server Push 发送二进制数据后,服务器立即向客户端发送 SSE 以通知它数据已被推送到其缓存中。现在客户端可以通过请求从其缓存中检索数据。
但只要您使用 SSE,为什么不首先通过 SSE 发送文件呢?因为如果您正在处理二进制数据,您可以受益于服务器推送允许您实现的更小的文件大小。对于简短的 JSON 消息,使用 Server Push 可能没有意义。在推送二进制数据并且必须节省带宽的情况下,请考虑通过服务器推送发送数据,然后发送 SSE 通知。
与轮询不同,这种方法不需要来自客户端的周期性请求。服务器可以随时发送服务器推送响应。
我知道 HTTP 是一种请求-响应协议。简而言之,我的问题是客户端向服务器发出请求以启动一个长 运行 进程,我想通过包含进度信息的简单 JSON 消息通知客户端进度.
在 HTTP/1.1 中,我知道我可以使用 WebSocket 或服务器发送的事件 (SSE) 或长轮询。
现在我知道HTTP/2还不支持WebSocket
我的问题是,在 HTTP/2 上处理此类事情的最佳方式是什么?
在 HTTP/2 中是否有任何我不知道的新东西来处理服务器发起的请求?
如果重要的话,我正在使用 Go 语言。
在 websockets 之前我们有轮询。这实际上意味着让客户端定期(每隔几秒,或任何对您的应用程序有意义的时间段)向服务器发出请求以了解作业的状态。
许多人使用的优化是 "long" 轮询。这涉及让服务器接受请求,并在服务器内部检查更改,并在有 none 时休眠,直到达到特定超时或发生所需事件,然后将其消息返回给客户.
如果达到超时,连接将关闭,客户端需要发出另一个请求。服务器代码看起来像下面这样,假设函数根据它们的名称和签名做一些明智的事情:
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.
}
处理超时的更好方法是使用 context package 并创建具有超时的上下文。这看起来像:
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
}
}
}
第二个版本将在更可靠的时间内到达 return,尤其是在 checkStatus
调用速度较慢的情况下。
你可以考虑使用HTML5text/event-streama.k.a。服务器端事件(SSE)。问题中提到了 SSE,它不能与 http2 一起使用吗?
关于 SSE 的一般文章
- http://www.w3schools.com/html/html5_serversentevents.asp
- https://en.wikipedia.org/wiki/Server-sent_events
(IE是目前唯一不支持SSE的浏览器)
后面的文章中,http2推送结合了SSE。文档被推送到客户端缓存中,SSE 用于通知客户端可以从其缓存中检索哪些文档(= 服务器通过单个 http2 连接发起的请求):
SSE 基础知识:在服务器端,您从:
Content-Type: text/event-stream\n\n
然后每次您想向您发送的客户端发送更新
data: { "name": "value", "othername": "othervalue" }\n\n
完成后,在关闭连接之前,您可以选择发送:
retry: 60000\n\n
指示浏览器在 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);
如果您想将 JSON 消息作为文本发送,server-sent event (SSE) 是一个很好的方法。 SSE 旨在发送文本。所有事件数据均以 UTF-8 字符编码。缺点是这使得通过 SSE 发送二进制数据效率低下。
如果你想发送二进制数据,你可能对HTTP/2引入的Server Push机制感兴趣。服务器推送允许 HTTP/2 服务器主动向客户端发送任何类型的文件。它被称为服务器推送 "response",即使它是在客户端请求它之前发送的。客户端自动将通过服务器推送响应发送的文件存储在其缓存中。对文件的后续请求会立即从缓存中得到满足,而无需往返于服务器。
这是一种将二进制数据推送到网络浏览器的有效方法。问题在于,当服务器推送响应到达时,浏览器的文档对象模型 (DOM) 没有得到通知。浏览器只有在发出请求时才会发现数据在其缓存中。我们可以通过以下方式解决此问题。在使用 Server Push 发送二进制数据后,服务器立即向客户端发送 SSE 以通知它数据已被推送到其缓存中。现在客户端可以通过请求从其缓存中检索数据。
但只要您使用 SSE,为什么不首先通过 SSE 发送文件呢?因为如果您正在处理二进制数据,您可以受益于服务器推送允许您实现的更小的文件大小。对于简短的 JSON 消息,使用 Server Push 可能没有意义。在推送二进制数据并且必须节省带宽的情况下,请考虑通过服务器推送发送数据,然后发送 SSE 通知。
与轮询不同,这种方法不需要来自客户端的周期性请求。服务器可以随时发送服务器推送响应。