Контекст Go http не может зафиксировать сигнал отмены, когда запрос имеет тело (завиток, почтальон)

Проблему можно воспроизвести с помощью приведенного ниже простого фрагмента кода:

Простой 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 '{}'

Подводя итоги моих вопросов:

  1. Почему и как запрос curl (почтальон) GET, POST (с телом запроса или без него) имеет такое значение?
  2. Как мне правильно обработать этот случай с пакетом контекста go, я имею в виду, чтобы захватить сигнал «клиент ушел», как только он сможет, и, таким образом, отменить ненужную работу на стороне сервера, чтобы освободить ресурсы как можно раньше.

Ре контекст. Передайте r.Context() любой подзадаче, например. SQL-запрос; вызов http/gRPC и т. д., и это приведет к цепочке отмены контекста для каждого дизайна. В результате подзадачи свяжут ошибку отмены. Если вы хотите опросить контекст запроса в своем собственном коде, например. цикл ожидания, затем используйте select на канале контекста вместе с каналом таймера ожидания.

colm.anseo 29.07.2019 05:41

@colminator спасибо за ответ, я понял, что приведенный выше код просто создан для воспроизведения проблемы, с которой я столкнулся: я НЕ могу перехватить сигнал отмены в случае запроса POST.

lnshi 29.07.2019 05:52
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
2
1 089
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Прочитайте тело запроса, чтобы определить, когда клиент закрывает соединение:

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. у вас есть ссылка или документация, связанная с этим?

novalagung 29.07.2019 06:27

ах, ic, отлично ???, я потратил два три часа, чтобы попытаться понять это поведение!

lnshi 29.07.2019 06:33

Я не знаю никаких документов для этого. Такое поведение следует из того факта, что чтение или запись — единственный способ обнаружить закрытое соединение в Go.

Bayta Darell 29.07.2019 06:43

@CeriseLimón я обнаружил, что должен прочитать ПОЛНОЕ тело запроса, тогда обнаружение сработает, если я прочитаю только несколько байтов, событие закрытия соединения НЕ будет обнаружено, есть ли способ избежать чтения ПОЛНОГО тела запроса, но не потеря способности обнаружения? (моя цель - немного задержать чтение тела запроса, чтобы, если клиент уже ушел, я больше не утруждал себя чтением тела запроса)

lnshi 27.05.2021 12:23

Другие вопросы по теме