我希望能够在 Varnish 4 中重试时更改后端。我们已经使用 Varnish 3 在不同的(较旧的)应用程序上进行了此操作,但我无法弄清楚 v4 的情况,也找不到太多文档。我们想要的设置是有 2 组控制器 - 一组用于初始请求,尝试在与 varnish 相同的数据中心中尝试本地服务器,因为这样速度更快,然后只有在失败时,才从其他控制器中随机选择服务器数据中心。
在 v3 中,这很简单:
sub vcl_recv {
if (req.restarts == 0) {
set req.backend = defaultdirector;
} else {
set req.backend = backupdirector;
}
}
#Then in vcl_fetch and/or vcl_error something like:
if (beresp.status >= 500 && req.restarts < some_max) {
return(restart);
}
但现在在 v4 中,重启据说已经被重试取代,整个文档是:
在3.0中,可以在注意到后端响应错误后执行返回(重新启动),以更改为不同的后端。
这现在称为返回(重试),并跳回到 vcl_backend_fetch。
这只影响后端获取线程,客户端处理不受影响。
但是我仍然看到一些人的示例代码包含 return(restart) 而不是 return(retry),并且没有一个使用重试命令的示例。
我理解varnish不应该再次执行vcl_recv中的所有工作(例如剥离cookie),因为这只是与后端的通信失败,所以反弹回后端获取而不是有意义重做所有前端处理,但如果我尝试更改 vcl_backend_fetch 中的后端,则会出现编译错误。我怎样才能做到这一点?
官方文档有点误导。事实上,restart仍然存在:你可以在vcl_deliver中捕获错误,并使用req.backend_hint在vcl_recv中相应地设置后端:
sub vcl_recv {
if (req.restarts == 0) {
set req.backend_hint = defaultdirector.backend();
} else {
set req.backend_hint = backupdirector.backend();
}
}
sub vcl_deliver {
if (resp.status >= 500 && req.restarts < some_max) {
return(restart);
}
}
或者,如果更合适,您可以在 vcl_backend_response 和 vcl_backend_fetch 之间使用 retry:
sub vcl_backend_fetch {
if (bereq.retries > 0) {
set bereq.backend = backupdirector.backend();
}
}
sub vcl_backend_response {
if (beresp.status >= 500 && bereq.retries < some_max) {
return(retry);
}
}
我正在写另一个答案,因为@komuta 答案中没有处理一个案例。有趣的是,我在 2017 年添加了一条评论...
所以缺失的情况就是后端没有回复!如果是这样,则不会调用
vcl_backend_response
,因为根据定义,没有响应!
sub vcl_recv {
set req.backend_hint = defaultdirector.backend();
}
sub vcl_backend_fetch {
// builtin, not sure it's needed...
if (bereq.method == "GET") {
unset bereq.body;
}
// if there was a call to the first director, which failed, then we're back with a (retry) call, now we change the director:
if (bereq.retries > 0) {
set bereq.backend = backupdirector.backend();
}
return (fetch);
}
sub vcl_backend_response {
// for example if you don't expect an HTTP 500 error, then let's retry!
if (beresp.status == 500 && bereq.method == "GET") {
return (retry);
}
[...]
return (deliver);
}
sub vcl_backend_error {
// here we've had an error with the backend, possibly a network error, or webserver crash, so we want to retry it!
return (retry);
}
sub vcl_synth {
// this is where we'll end when bereq.retries will hit max_retries (defaults to 4)
set resp.body = {"oops..."}
return (deliver);
}