Сервер инициирует запросы
Я знаю, что HTTP-это протокол запроса-ответа. Короче говоря, моя проблема заключается в том, что клиент делает запрос на сервер для запуска длительного процесса, и я хочу сообщить клиенту о прогрессе с помощью простого сообщения JSON, содержащего информацию о прогрессе.
в HTTP / 1.1 я знаю, что могу использовать WebSocket или серверные события (SSE) или длинный опрос.
теперь я знаю, что HTTP / 2 еще не поддерживает WebSocket.
мой вопрос в том, что является оптимальным способ обработки таких вещей через HTTP / 2?
есть ли какие-либо новые вещи, о которых я не знаю, чтобы обрабатывать запросы, инициированные сервером в HTTP/2?
Я использую язык Go, если это имеет значение.
3 ответов
перед 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.
}
лучшим способом обработки тайм-аута было бы использовать контекст пакета и создайте контекст с таймаутом. Это будет выглядеть что-то например:
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
}
}
}
эта вторая версия просто собирается вернуться в более надежное количество времени, особенно в случае, когда checkStatus
может быть более медленный вызов.
вы можете рассмотреть возможность использования HTML5 text / event-stream a.к. a. события на стороне сервера (SSE). SSE упоминается в вопросе, не будет ли это хорошо работать с http2?
общие статьи о SSE
- http://www.w3schools.com/html/html5_serversentevents.asp
- https://en.wikipedia.org/wiki/Server-sent_events
(IE в настоящее время является единственным браузером, который не поддерживает SSE)
в следующей статье http2 push сочетается с 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 в виде текста, a сервер-отправленное событие (SSE) - это хороший способ сделать это. SSE предназначен для отправки текста. Все данные события кодируются в символах UTF-8. Недостатком является то, что это делает его неэффективным для отправки двоичных данных через ГСП.
Если вы хотите отправить двоичные данные, вы можете быть заинтересованы в Сервер Push механизм, введенный HTTP / 2. Сервер Push позволяет серверу HTTP / 2 отправлять любой файл клиенту на по собственной инициативе. Это называется "ответом" сервера, даже если он отправляется до того, как клиент запрашивает его. Клиент автоматически сохраняет файл, отправленный через сервер Push response, в своем кэше. Последующий запрос на файл немедленно выполняется из кэша без поездки туда и обратно на сервер.
Это эффективный способ передачи двоичных данных в веб-браузер. Загвоздка заключается в том, что объектная модель документа браузера (DOM) не уведомляется, когда поступает ответ толчка сервера. Браузер только обнаруживает, что данные находятся в его кэше, когда он делает запрос на него. Мы можем обойти эту проблему следующим образом. Сразу после отправки двоичных данных с помощью server Push сервер отправляет SSE клиенту, чтобы уведомить его о том, что данные были помещены в его кэш. Теперь клиент может получить данные из своего кэша, запросив их.
но пока вы используете SSE, почему бы не отправить файл через SSE в первую очередь? Потому что если ты имеешь дело с двоичные данные, вы можете извлечь выгоду из меньшего размера файла, что сервер толчок позволяет достичь. Для короткого сообщения JSON может не иметь смысла использовать сервер Push. В ситуациях, когда вы нажимаете двоичные данные, и вам нужно сохранить пропускную способность, рассмотрите отправку данных через толчок сервера с последующим уведомлением SSE.
в отличие от опроса, этот подход не требует периодических запросов от клиента. Сервер может отправить ответ толчка сервера, когда он хочет.