|
|
@@ -66,32 +66,71 @@ func FindRecord(ctxApp app.AppContext, numRecord string) error {
|
|
|
### Дочерний контекст с таймаутом внешнего ресурса
|
|
|
|
|
|
```go
|
|
|
-func Index(ctx contextContext, request *IndexRequest)(*IndexResponse, error){
|
|
|
- chErr := make (chan error, 2)
|
|
|
- ctxTimeout, fnCancel := cntext.WithTimeout(ctx, time.Millisecond * 500)
|
|
|
+
|
|
|
+// 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()
|
|
|
- var (
|
|
|
- resp = &IndexResponse{}
|
|
|
- err error
|
|
|
- )
|
|
|
- fnWork := func(){
|
|
|
- defer close(chError)
|
|
|
+
|
|
|
+ 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{
|
|
|
- chErr <- err
|
|
|
+ if err != nil { // Была ошибка -- вернём только ошибку
|
|
|
+ resp := &Resp {
|
|
|
+ err: err,
|
|
|
+ }
|
|
|
+ chResp <- resp
|
|
|
return
|
|
|
- }
|
|
|
}
|
|
|
- go fnWork()
|
|
|
- select {
|
|
|
- case <-ctxTimeout.Done(): // Был ли таймаут?
|
|
|
+ resp := &Resp { // Ошибки не было -- значит вернём только результат.
|
|
|
+ err: err,
|
|
|
+ }
|
|
|
+ chResp <- resp
|
|
|
+ }
|
|
|
+
|
|
|
+ go fnWork() // Обязательно! Запуск в отдельном потоке
|
|
|
+
|
|
|
+ select { // Здесь гонки между таймаутом и полезным результатом -- кто быстрее.
|
|
|
+ case <-ctxTimeout.Done(): // Был ли таймаут дочернего контекста?
|
|
|
return nil, fmt.Errorf("Index(): timeout in make response")
|
|
|
- case err<-chErr: // Была ли ошибка?
|
|
|
- if err!=nil{
|
|
|
- eturn nil, fmt.Errorf("Index(): in make response, err=\n\t%w", err)
|
|
|
+ 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
|
|
|
}
|
|
|
- return resp, nil
|
|
|
}
|
|
|
-
|
|
|
```
|