我正在开发一个服务器应用程序,它也是其他后端应用程序的客户端。这是我的基于 http.Client 的客户端:
type Client struct {
http.Client
token Token
}
type Token struct {
tokenString string
mx sync.RWMutex
}
// e.g. readlock
func (c *Client) getDataWithRetries(req *http.Request) (*http.Response, error) {
var res *http.Response
var err error
var status int
c.token.mx.RLock()
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+c.token.tokenString)
c.token.mx.RUnlock()
for i := 0; i < 5; i++ {
if c.token.tokenString == "" || status == 401 {
if err := c.setToken(); err != nil {
log.Println("401")
return nil, err
}
c.token.mx.RLock()
req.Header.Set("Authorization", "Bearer "+c.token.tokenString)
c.token.mx.RUnlock()
}
res, err = c.Do(req)
if err != nil {
log.Println("Do req err")
return nil, err
}
status = res.StatusCode
if res.StatusCode != 401 {
break
}
}
if status == 401 {
return nil, domain.ErrUnauthorized
}
return res, nil
}
// Here's where I write to token
func (c *Client) setToken() error {
tokenURL := os.Getenv("TOKEN_URL")
if tokenURL == "" {
return domain.ErrTokenURLNotFound
}
req, err := http.NewRequest(http.MethodGet, tokenURL, nil)
if err != nil {
return err
}
req.SetBasicAuth(c.username, c.password)
res, err := c.Do(req)
if err != nil {
return err
}
defer res.Body.Close()
var dst bytes.Buffer
if _, err := io.Copy(&dst, res.Body); err != nil {
return err
}
c.token.mx.Lock()
defer c.token.mx.Unlock()
var token string
if err := json.Unmarshal(dst.Bytes(), &token); err != nil {
log.Println("unmarshal error", err)
return err
}
c.token.tokenString = token
return nil
}
我将客户端传递给整个应用程序,它会在其中进行一些连续的 API 调用,并将令牌添加为授权标头。令牌每 20 分钟过期一次,我必须通过从令牌颁发 API 获取它来设置一个新令牌。我相信那是我进行数据竞赛的时候。我尝试使用 RWMutex,但到目前为止帮助不大。
我真的不需要该令牌对于每个请求都相同,只要它没有过期,我就可以走了。令牌发行 API 可以同时发行我想要的任意数量的有效令牌。此外,由于多个用户试图同时访问我的应用程序,我是否会在令牌过期之前获得数据竞争?