Разрешить одному потоку обновлять значение (refre sh токен авторизации), другим ждать и пропустить критический раздел - PullRequest
0 голосов
/ 10 января 2020

Я отправляю HTTP-запросы из нескольких потоков по нескольким адресатам. Эти запросы требуют авторизации. У меня есть один сервер авторизации, откуда я получаю токен авторизации.

Итак, когда срок действия токена авторизации истекает (то есть статус ответа HTTP 401), я бы хотел, чтобы один из потоков go и refre sh токен, пока другие потоки ждут. Когда токен обновляется, все потоки должны продолжать отправлять запросы, не пытаясь повторно обновить sh токен.

Вот моя реализация:

type httpSenderAgent struct {
    // irrelevant members omited
    ...
    client *http.Client
    tokenState int // (valid = 1, expired = 0)
    token string
    cond sync.Cond
}
func (a *httpSenderAgent) send(url string, body interface{}, retrycount int) error {

    if retrycount >= MAX_AUTH_RETRY {
        return errors.New("Max retry exceeded")
    }

    buf := &bytes.Buffer{}
    json.NewEncoder(buf).Encode(body)
    req, err := http.NewRequest("POST", url, buf)
    if err != nil {
        return err
    }

    req.Header.Add("Content-Type", "application/json; charset=utf-8")
    req.Header.Set("Authorization", "Bearer "+a.token)

    res, err := a.client.Do(req)
    if err != nil {
        return err
    }

    defer res.Body.Close()

    if res.StatusCode == http.StatusUnauthorized {
        a.refreshAuthToken()
        return a.send(url , body, retrycount + 1)
    }

    if res.StatusCode < 200 || res.StatusCode > 299 {
        return errors.New(http.StatusText(res.StatusCode))
    }

    return nil
}
func (a *httpSenderAgent) refreshAuthToken() {
    // lock section 1
    a.cond.L.Lock()
    if a.tokenState == valid {
        a.tokenState = expired
        go func() {
            // getAuthToken() is a http call to auth server
            tkn := a.getAuthToken()


            // lock section 2
            a.cond.L.Lock()
            a.token = tkn
            a.tokenState = valid
            a.cond.L.Unlock()

            a.cond.Broadcast()
        }()
    }

    for a.tokenState != valid {
        a.cond.Wait()
    }
    a.cond.L.Unlock()
}

, но , проблема возникает в следующей ситуации:

Допустим, первый поток получает ответ 401, срок его действия истекает tokenState, он запускает процедуру загрузки токена и ожидает, чтобы токен был действительным. Тем временем другой нить получает 401 и вызывает refreshAuthToken(). К этому времени поток извлечения токенов входит в lock section 2. Итак, второй поток не может ввести lock section 1, он ожидает разблокировки мьютекса. Когда поток извлечения токенов обновляет tokenState на действительный и разблокирует мьютекс, второй поток заблокирует мьютекс и обнаружит, что токен действителен. Итак, он будет повторять весь процесс извлечения токена.

Я проверил вопрос SO с таким же заголовком Java - разрешить одному потоку обновлять значение, другим ждать и пропускать критический раздел . Но ответы не охватывают мой вариант использования.

Итак, как я могу изменить это, чтобы получать токен аутентификации только один раз, пока не истечет снова? Какой примитив синхронизации я должен использовать для достижения этой цели?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...