Как воспроизвести поведение VCL 3.0 vcl_recv / restart в VCL 4.0 - PullRequest
0 голосов
/ 03 ноября 2019

Я обновляю старый сервер Varnish 3 до Varnish 6.11

Этот сервер Varnish был настроен таким образом, чтобы избежать кэширования больших файлов (более 100 МБ) с использованием pipe. Сначала запрос был нормально отправлен на бэкэнд, и, если размер заголовка Content-Length ответа был больше 100 МБ, запрос был повторен: vcl_recv был вызван внутренне второй раз с новым заголовком x-pipe для запроса, указывающимvlc_recv до pipe вместо hash. Это был способ сделать это тогда:

добавлено в vcl_recv:

  /* Bypass cache for large files.  The x-pipe header is
     set in vcl_fetch when a too large file is detected. */
  if (req.http.x-pipe && req.restarts > 0) {
    remove req.http.x-pipe;
    return (pipe);
  }

добавлено в vcl_fetch:

  # don't cache files larger than 10MB
  /* Don't try to cache too large files.  It appears
     Varnish just crashes if we don't filter them. */
  if (beresp.http.Content-Length ~ "[0-9]{8,}" ) {
    set req.http.x-pipe = "1";
    return (restart);
  }

Теперь, когда vcl_fetch было изменено наvcl_backend_response, действие restart исчезло. Замена retry на restart не приводит к тому же поведению, что и функция vcl_recv не вызывается во второй раз. retry только повторяет внутренний запрос.

Возврат pass в vcl_backend_response страдает по той же причине pipe: Varnish должен будет прочитать запрос в память перед отправкой клиенту иэто то, чего pipe избегали.

Мой вопрос: как pipe (отправлять байты непосредственно из серверной части клиенту без какой-либо обработки) в большие файлы с помощью VCL 4.0?

1 Ответ

0 голосов
/ 03 ноября 2019

Это выполнимо, но с Varnish Cache 4.0 все стало немного сложнее из-за разделения между клиентским и внутренним потоками.

Идея состоит в том, что вам нужно (1) перейти с vcl_backend_response на vcl_backend_error;(2) создать и вернуть клиентскому потоку синтетический ответ (и в идеале кэшировать его, чтобы избежать сериализации запроса);и (3) проверить предыдущий ответ во время vcl_deliver и выполнить перезапуск в потоке клиента. Следующий тестовый пример показывает рабочий пример:

varnishtest "..."

server s1 {
    rxreq
    txresp -hdr "X-Large-Response: 1"
} -repeat 3 -start

varnish v1 -vcl+backend {
    sub vcl_recv {
        if (req.restarts == 0) {
            unset req.http.X-Restart-And-Pipe;
        } elsif (req.http.X-Restart-And-Pipe) {
            return (pipe);
        }
    }

    sub vcl_deliver {
        if (resp.http.X-Restart-And-Pipe) {
            set req.http.X-Restart-And-Pipe = "1";
            return (restart);
        }
    }

    sub vcl_backend_fetch {
        if (bereq.retries == 0)  {
            unset bereq.http.X-Restart-And-Pipe;
        }
    }

    sub vcl_backend_response {
        if (beresp.http.X-Large-Response) {
            set bereq.http.X-Restart-And-Pipe = "1";
            return (error);
        } else {
            unset beresp.http.X-Restart-And-Pipe;
        }
    }

    sub vcl_backend_error {
        if (bereq.http.X-Restart-And-Pipe) {
            set beresp.http.X-Restart-And-Pipe = "1";
            set beresp.ttl = 1s;
            set beresp.grace = 0s;
            set beresp.keep = 0s;
            return (deliver);
        }
    }
} -start

client c1 {
    txreq
    rxresp
    expect resp.status == 200
} -start

client c1 {
    txreq
    rxresp
    expect resp.status == 200
} -run

varnish v1 -expect n_object == 1
varnish v1 -expect sess_conn == 2
varnish v1 -expect client_req == 2
varnish v1 -expect s_sess == 2
varnish v1 -expect s_pipe == 2

Плохие новости переходят с vcl_backend_response на vcl_backend_error с использованием return (error) возможно только с Varnish Cache 6.3.0. Использовать более старую версию все еще возможно, но решение немного хакерское: сначала нужно перейти к vcl_backend_fetch, а затем использовать всегда сломанный бэкэнд:

varnishtest "..."

server s1 {
    rxreq
    txresp -hdr "X-Large-Response: 1"
} -repeat 3 -start

varnish v1 -vcl+backend {
    backend always_broken_be {
        .host = "127.0.0.1";
        .port = "666";
    }

    sub vcl_recv {
        if (req.restarts == 0) {
            unset req.http.X-Restart-And-Pipe;
        } elsif (req.http.X-Restart-And-Pipe) {
            return (pipe);
        }
    }

    sub vcl_deliver {
        if (resp.http.X-Restart-And-Pipe) {
            set req.http.X-Restart-And-Pipe = "1";
            return (restart);
        }
    }

    sub vcl_backend_fetch {
        if (bereq.retries == 0)  {
            unset bereq.http.X-Restart-And-Pipe;
        } elsif (bereq.http.X-Restart-And-Pipe) {
            set bereq.backend = always_broken_be;
        }
    }

    sub vcl_backend_response {
        if (beresp.http.X-Large-Response) {
            set bereq.http.X-Restart-And-Pipe = "1";
            return (retry);
        } else {
            unset beresp.http.X-Restart-And-Pipe;
        }
    }

    sub vcl_backend_error {
        if (bereq.http.X-Restart-And-Pipe) {
            set beresp.http.X-Restart-And-Pipe = "1";
            set beresp.ttl = 1s;
            set beresp.grace = 0s;
            set beresp.keep = 0s;
            return (deliver);
        }
    }
} -start

client c1 {
    txreq
    rxresp
    expect resp.status == 200
} -start

client c1 {
    txreq
    rxresp
    expect resp.status == 200
} -run

varnish v1 -expect n_object == 1
varnish v1 -expect sess_conn == 2
varnish v1 -expect client_req == 2
varnish v1 -expect s_sess == 2
varnish v1 -expect s_pipe == 2
...