проблема
Некоторое время назад я обнаружил, что в сети есть интерфейс сервиса, и он всегда выдает прерывистые сигналы, иногда два-три раза в день, а иногда и не день.
Логика аварийного сигнала заключается в том, что другой интерфейс HTTP вызывается асинхронно в одном интерфейсе, и время вызова интерфейса HTTP истекает. Но я спрашивал у одноклассников, которые заведуют этим HTTP-интерфейсом.Они сказали, что их интерфейсы соответствуют миллисекундам, и они тоже делали скрины для мониторинга.Фотки есть и правда.Что тут еще сказать.
Однако тайм-аут существует, но запрос может еще не достичь другой стороны службы.
Такого рода спорадические проблемы не легко воспроизвести, и это раздражает, чтобы получить случайный сигнал тревоги.Первая реакция состоит в том, чтобы сначала решить проблему.Идея также проста, и повторить попытку после неудачи.
Решение
Оставив в стороне стратегию повторных попыток, давайте поговорим о том, когда инициировать повторную попытку.
Мы можем повторить попытку, когда ошибка запроса интерфейса выдает err, но это не так просто контролировать.Если запрос выходит и нет ответа в течение десяти секунд, сопрограмма будет ждать, пока он сообщит об ошибке, прежде чем повторить попытку, что является пустой тратой времени. времени жизни~
Таким образом, в сочетании с индикаторами ответа на уровне миллисекунд, указанными студентами выше, можно установить период ожидания.Если по истечении указанного периода ожидания результаты не возвращаются, повторите попытку (эта повторная попытка не имеет значения).
func AsyncCall() {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Millisecond*800))
defer cancel()
go func(ctx context.Context) {
// 发送HTTP请求
}()
select {
case <-ctx.Done():
fmt.Println("call successfully!!!")
return
case <-time.After(time.Duration(time.Millisecond * 900)):
fmt.Println("timeout!!!")
return
}
}
инструкция
1. Установите контекст с эффективным временем 800 миллисекунд с помощью контекста WithTimeout.
2. Контекст завершится по истечении 800 миллисекунд или выполнения метода, и по его окончании будет отправлен сигнал в канал ctx.Done.
3. Некоторые люди могут спросить, вы уже установили действительное время контекста здесь, зачем добавлять это время.После?
Это связано с тем, что контекст в этом методе объявляется сам по себе, и соответствующий таймаут можно задать вручную, но в большинстве сценариев здесь всегда передается ctx из апстрима, и сколько времени осталось до контекста, переданного из апстрима , мы не знаем, так что надо задавать ожидаемое время таймаута через time.After в это время.
4. Обратите внимание, что вы должны не забыть вызвать здесь cancel(), иначе, даже если выполнение будет завершено заранее, контекст не будет освобожден до 800 миллисекунд.
Суммировать
Вышеупомянутое управление тайм-аутом используется с ctx.Done и time.After.
Канал Done отвечает за отслеживание завершения контекста, если таймаут установленный в time.After истек, а вы не закончили, то я не буду ждать и выполню код логики по истечении таймаута.
учиться по аналогии
Итак, в дополнение к описанной выше стратегии управления тайм-аутом, существуют ли другие подпрограммы?
Да, но похоже.
Первый: используйте time.NewTimer
func AsyncCall() {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(time.Millisecond * 800))
defer cancel()
timer := time.NewTimer(time.Duration(time.Millisecond * 900))
go func(ctx context.Context) {
// 发送HTTP请求
}()
select {
case <-ctx.Done():
timer.Stop()
timer.Reset(time.Second)
fmt.Println("call successfully!!!")
return
case <-timer.C:
fmt.Println("timeout!!!")
return
}
}
Основное отличие здесь заключается в замене time.After на time.NewTimer, что тоже является той же идеей: если вызов интерфейса завершится заранее, то будет отслеживаться сигнал Done, после чего таймер будет закрыт.
В противном случае бизнес-логика после таймаута будет выполняться через указанный таймер, то есть 900 миллисекунд.
Второй: использовать канал
func AsyncCall() {
ctx := context.Background()
done := make(chan struct{}, 1)
go func(ctx context.Context) {
// 发送HTTP请求
done <- struct{}{}
}()
select {
case <-done:
fmt.Println("call successfully!!!")
return
case <-time.After(time.Duration(800 * time.Millisecond)):
fmt.Println("timeout!!!")
return
}
}
1. Здесь мы в основном используем канал, который может общаться между сопрограммами, при успешном вызове посылается сигнал на сделанный канал.
2. Отслеживайте сигнал «Готово», если он получен до истечения времени. По истечении времени ожидания он вернется в нормальное состояние, в противном случае он перейдет к логике времени ожидания. После и выполните код логики времени ожидания.
3. Здесь используется комбинация канала и времени.After, а также может использоваться комбинация канала и времени.NewTimer.
Суммировать
В этой статье в основном рассказывается, как реализовать контроль тайм-аута, есть три основных типа
1. context.WithTimeout/context.WithDeadline + время.После
2. context.WithTimeout/context.WithDeadline + время.NewTimer
3. канал + время.После/время.НовыйТаймер
Личный публичный аккаунт JackieZheng, добро пожаловать на внимание~