lesson01.md 5.7 KB

Лекция 1. Контексты

Оглавление

Что это такое

Передача данных от обработчика к обработчику требует сохранения промежуточного состояния -- контекста. Контекст можно расширять или сужать, но по частям он смысла не имеет.

Контекст -- весь необходимый набор данных для выполнени операций.

Зачем нужно

  • накопление, удаление обрабатываемых данных между запросами;
  • контроль родительского процесса за дочерними;
  • охрана запросов в реальном времени за внешними ресурсами.

Примеры использования

Создание глобального контекста

ctxBg := context.Background()

Глобальный контекст использовать нельзя!

Почитать про API

Дочерний контекст с простой отменой

Дочерний контекст с простой отменой:

func FindRecord(ctxApp app.AppContext, numRecord string) error {
    ctxWork, fnCancel := context.WithCancel(ctxApp)
    defer fnCancel()
    chWork := make(chan int, 2)
    fnRead :=func(){
        defer close(chWork)
        err:=file.FindRecord(numRecord)
        if err!=nil{
            ctxApp.Cancel()
        }
    }
    go fnRead()
    select {
        case <-ctxApp.Done(): // Отмена контекста приложения
            return fmt.Errorf("FindRecord(): cancel ctxApp")
        // Ожидание окончания работы, всё-равно
        // когда-нибудь закончится так или иначе.
        case <-chWork:
            return nil
    }
}

Дочерний контекст с таймаутом внешнего ресурса


// LocalStorage -- локальное хранилище ключ-значение
type LocalStorage interface {
    // Получить значение п оключу
    Get(string) (*IndexResponse, error)
}

// IndexRequest -- запрос индексной страницы
type IndexRequest struct {
    Name string // Имя на индексной странице
}

// IndexResponse -- ответ на запрос инжексной страницы
type IndexResponse struct {
  // Определение полей структуры IndexResponse
}

var (
  db LocalStorage // Объект локального хранилища
)

// Index -- запрос индексной страницы
func Index(ctx context.Context, request *IndexRequest) (*IndexResponse, error) {

    // Дочерний контекст и ег офункция отмены
    ctxTimeout, fnCancel := context.WithTimeout(ctx, time.Millisecond*500)
    // Обязательный вызов отмены контекста при выходе, иначе будет утечка памяти!!
    defer fnCancel()

    type Resp struct {
        resp *IndexResponse // Полезный ответ (если нет ошибки)
        err error           // Ошибка (если была)
    }

    chResp := make(chan *Resp, 1) // Канал для получения результата после обработки

    // Эта функция должна работат ьв отдельном потоке. Иначе невозможно
    // устроить соревнование между глобальным контекстом и дочерним.
    fnWork := func() {
        defer close(chResp) // Обязательн озакрыт ьканал при выходе
        resp, err = db.Get(request.Name)
        if err != nil { // Была ошибка -- вернём только ошибку
            resp := &Resp {
                err: err,
            }
            chResp <- resp
            return
    }
    resp := &Resp { // Ошибки не было -- значит вернём только результат.
        err: err,
    }
    chResp <- resp
  }

    go fnWork() // Обязательно! Запуск в отдельном потоке

    select { // Здесь гонки между таймаутом и полезным результатом -- кто быстрее.
        case <-ctxTimeout.Done(): // Был ли таймаут дочернего контекста?
            return nil, fmt.Errorf("Index(): timeout in make response")
        case result := <-chResp: // Что там с результатом?
            if result.err != nil {
                return nil, fmt.Errorf("Index(): in make response, err=\n\t%w", result.err)
            }
            // Всё ок, возвращаем полезный результат
            return result.resp, nil
    }
}