Управление каналами с использованием контекста HTTP-запроса

У меня есть базовый HTTP-сервер, который принимает запрос и возвращает данные из хранилища данных.

Каждый HTTP-запрос выполняет следующие действия:

  1. Создать контекст с тайм-аутом
  2. Создать запрос на чтение (пользовательский тип)
  3. Отправить запрос на чтение на канал
  4. Дождитесь ответа и отправьте данные

Вот основной псевдокод:

package main

import (
    "context"
    "net/http"
    "time"
)

type dataRequest struct {
    data chan string
    ctx  context.Context
}

func handler(reqStream chan dataRequest) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
        defer cancel()

        req := dataRequest{
            data: make(chan string),
            ctx:  ctx,
        }

        select {
        case reqStream <- req:
            // request pushed to que
        case <-ctx.Done():
            // don't push onto reqStream if ctx done
        }

        select {
        case <-ctx.Done():
            // don't try and serve content if ctx done
        case data := <-req.data:
            // return data to client
        }

    }
}

func main() {
    dataReqs := make(chan dataRequest)
    go func() {
        for {
            select {
            case req := <-dataReqs:
                select {
                case <-req.ctx.Done():
                    // don't push onto data channel if ctx done
                case req.data <- "some data":
                    // get data from store
                }
            }
        }
    }()
    http.HandleFunc("/", handler(dataReqs))
    http.ListenAndServe(":8080", nil)
}

Мой вопрос в том, что контекст может завершиться в любое время из-за превышения крайнего срока или отмены запроса клиентом, правильный ли мой текущий подход для обработки этого в нескольких местах или есть более элегантное решение?


person syscll    schedule 28.04.2017    source источник


Ответы (1)


мне кажется, что это сработает. мало комментариев -

  1. вы можете return в первом случае <- ctx.Done()
  2. вы уже ожидаете req.ctx.Done() в обработчике хранилища данных, поэтому вы можете полностью удалить первый оператор select {} и просто опубликовать его в канале запросов данных. не уверен насчет снижения производительности в редких случаях, когда контекст выполняется так рано, еще до того, как запрос будет опубликован...
person Gal Ben-Haim    schedule 08.12.2017