清漆问题

Issue with Varnish

我们正在使用 React 和 Redux 开发一个网站。我们刚刚在我们的 API 上添加了一个 Varnish 缓存,虽然在 Postman 上测试 API 时一切似乎都运行良好,但在我们的网站上却无法正常工作。

出于某种原因,所有 API 查询在第一次完成时都失败并显示 401 状态代码,但是,在第一次失败后它们工作正常。

一旦我们添加了 Varnish 缓存,这种行为就开始发生了,但是,我们无法找到错误。

Varnish配置文件如下:

vcl 4.0;

backend default {
    .host = ...;
    .port = ...;
}

sub vcl_backend_response {
    set beresp.ttl = 5m;

    # Don't cache non-200 responses
    if ( beresp.status != 200 ) {
        set beresp.ttl = 0s;
    }
    # Don't cache if "no-cache" or "private" on cache control
    if (beresp.http.cache-control ~ "(no-cache|private)" ||
        beresp.http.pragma ~ "no-cache") {
            set beresp.ttl = 0s;
    }

}

sub vcl_recv {
    # Do not cache Healthcheck call
    if (req.url == "/api/healthcheck") {
        return (pass);
    }

    # Do not cache POST methods
    if (req.method == "POST") {
        return (pass);
    }

    # Do not cache users' calls as each user will be different
    if (req.url ~ "\/api\/v[a-zA-Z0-9\.]+\/users\/.*") {
        return (pass);
    } 

    # Ensure that cache is enabled to any call to the API
    if (req.url ~ "/api/*") {
        return (hash);
    }   
}

sub vcl_deliver {
  if (req.url ~ "/api/*") {
    set resp.http.Access-Control-Allow-Headers = "Accept, Accept-Encoding, Authorization, Content-Type, Dnt, Origin, User-Agent, X-CSRFToken, X-Requested-With";
    return (deliver);
  } 
}

可能发生了什么?

非常感谢

我终于找到了我的问题,我的配置文件中缺少 return(hash)。

正确的配置文件如下:

vcl 4.0;

backend default {
    .host = ...;
    .port = ...;
}

sub vcl_backend_response {
    set beresp.ttl = 5m;

    # Don't cache non-200 responses
    if ( beresp.status != 200 ) {
        set beresp.ttl = 0s;
    }
    # Don't cache if "no-cache" or "private" on cache control
    if (beresp.http.cache-control ~ "(no-cache|private)" ||
        beresp.http.pragma ~ "no-cache") {
            set beresp.ttl = 0s;
    }

}

sub vcl_recv {
    # Do not cache Healthcheck call
    if (req.url == "/api/healthcheck") {
        return (pass);
    }

    if (req.method != "GET" && req.method != "HEAD") {
        # We only deal with GET and HEAD by default
        return (pass);
    }

    # Do not cache users' calls as each user will be different
    if (req.url ~ "\/api\/v[a-zA-Z0-9\.]+\/users\/.*") {
        return (pass);
    }
    return (hash);
}

sub vcl_deliver {
  if (req.url ~ "/api/*") {
    set resp.http.Access-Control-Allow-Headers = "Accept, Accept-Encoding, Authorization, Content-Type, Dnt, Origin, User-Agent, X-CSRFToken, X-Requested-With";
    return (deliver);
  }
}

我想我仍然 post 对于将 Varnish 应用于 API 需要某种授权的请求的人(即使你说你解决了你的问题)。

如果您碰巧需要对路径 外部 \/api\/v[a-zA-Z0-9\.]+\/users\/.* 范围的授权,那么 401 可能仍会发生,因为这些请求可能仍被缓存。

此外,您还可以(并且应该出于性能原因)缓存经过身份验证的请求。解决该问题和 401 错误的简单方法是根据令牌 header 的值改变缓存。

解决方案 1.

您可以更改您的应用程序代码以简单地发送额外的 HTTP header:Vary: X-Token。这足以指示 Varnish API.

的不同用户之间的缓存应该不同

解决方案 2.

如果你没有办法调整你的应用程序,那么一些VCL可以提供帮助:

sub vcl_backend_response {
     if (!beresp.http.Vary) { # no Vary at all
        set beresp.http.Vary = "X-Token";
    } elsif (beresp.http.Vary !~ "X-Token") { # add to existing Vary
        set beresp.http.Vary = beresp.http.Vary + ", X-Token";
    }
    ...
}

P.S。通常不建议将 "trailing" return 放在 vcl_recv 中,以确保 builtin vcl_recv 例程仍然 运行。