Проблему можно воспроизвести с помощью приведенного ниже простого фрагмента кода:
Простой http-сервер:
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func handler(w http.ResponseWriter, r *http.Request) {
go func(done <-chan struct{}) {
<-done
fmt.Println("message", "client connection has gone away, request got cancelled")
}(r.Context().Done())
time.Sleep(30 * time.Second)
fmt.Fprintf(w, "Hi there, I love %s!\n", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Начните выше http-сервера, если я отправлю один простой запрос GET с завитком (также почтальон), например:
curl -X GET http://localhost:8080/
затем нажмите Ctrl+C, чтобы завершить запрос, после чего я смогу увидеть напечатанное сообщение на стороне сервера:
message client connection has gone away, request got cancelled
выше - правильное поведение, которое я ожидаю: для имитации ситуации, когда клиент ушел, сервер может его захватить, а затем отменить всю ненужную работу как можно раньше.
Но когда я отправляю один запрос POST с телом запроса, происходит ожидаемое поведение не, сигнал <-done фиксируется до тех пор, пока не наступит крайний срок запроса.
curl -X POST http://localhost:8080/ -H 'Content-Type: application/json' -d '{}'
Подводя итоги моих вопросов:
GET, POST (с телом запроса или без него) имеет такое значение?@colminator спасибо за ответ, я понял, что приведенный выше код просто создан для воспроизведения проблемы, с которой я столкнулся: я НЕ могу перехватить сигнал отмены в случае запроса POST.





Прочитайте тело запроса, чтобы определить, когда клиент закрывает соединение:
func handler(w http.ResponseWriter, r *http.Request) {
go func(done <-chan struct{}) {
<-done
fmt.Println("message", "client connection has gone away, request got cancelled")
}(r.Context().Done())
io.Copy(ioutil.Discard, r.Body) // <-- read the body
time.Sleep(30 * time.Second)
fmt.Fprintf(w, "Hi there, I love %s!\n", r.URL.Path[1:])
}
Сервер net/http проверяет закрытые соединения, считывая соединение. Чтение не начинается, пока приложение не начнет читать тело запроса (если оно есть).
Я пытался найти какие-либо заметки об этом в go doc, но ничего не нашел, но это действительно решает проблему OP. у вас есть ссылка или документация, связанная с этим?
ах, ic, отлично ???, я потратил два три часа, чтобы попытаться понять это поведение!
Я не знаю никаких документов для этого. Такое поведение следует из того факта, что чтение или запись — единственный способ обнаружить закрытое соединение в Go.
@CeriseLimón я обнаружил, что должен прочитать ПОЛНОЕ тело запроса, тогда обнаружение сработает, если я прочитаю только несколько байтов, событие закрытия соединения НЕ будет обнаружено, есть ли способ избежать чтения ПОЛНОГО тела запроса, но не потеря способности обнаружения? (моя цель - немного задержать чтение тела запроса, чтобы, если клиент уже ушел, я больше не утруждал себя чтением тела запроса)
Ре контекст. Передайте r.Context() любой подзадаче, например. SQL-запрос; вызов http/gRPC и т. д., и это приведет к цепочке отмены контекста для каждого дизайна. В результате подзадачи свяжут ошибку отмены. Если вы хотите опросить контекст запроса в своем собственном коде, например. цикл ожидания, затем используйте
selectна канале контекста вместе с каналом таймера ожидания.