Driver86
@Driver86
Немодератор toster.ru

Почему nginx выдаёт мусор от memcached?

Через php memcached записываю данные в memcached:

$this->memcached->setOptions([
    \Memcached::OPT_COMPRESSION => true,
    \Memcached::OPT_BINARY_PROTOCOL => true,
]);
$key = '...';
$value = '...';
$this->memcached->set($key, $value);


nginx забирает данные и отдаёт пользователю, примерно так:

location ~ \.php$ {
        set $memcached_key "...";
        gunzip                  on;
        memcached_gzip_flag     2;
        memcached_pass          unix:/run/memcached/memcached.socket;
        error_page              404 405 = @php;
}


Однако, nginx упорно выдаёт двоичные данные, вместо нормального html-кода. Думаю, тут дело в том, что nginx считает, что сохранены несжатые данные. Пробовал разные варианты memcached_gzip_flag: 0, 1, 2, 3... не меняется ничего....

Разумеется, если отключить сжатие (Memcached::OPT_COMPRESSION => false) всё работает нормально. А как со включенным?
  • Вопрос задан
  • 1135 просмотров
Решения вопроса 1
Driver86
@Driver86 Автор вопроса
Немодератор toster.ru
Короче, с горем пополам проблему решил.

Проблема оказалась, как я и думал, в неправильном сжатии: php-memcached сжимает данные как deflate, а nginx признает только gzip и, кстати, тут последний прав - gzip, в отличие от deflate, грубо говоря, "более однозначный" и работает даже с самыми глючными браузерами.
Писал разрабам php-memcached с предложением добавить сжатие gzip, но те, судя по всему, консерваторы и ничего менять не хотят (как чувствовал подвох, когда смотрел на дату последней версии). Самое забавное, что добавить поддержку gzip там проще простого - работы на 20 минут максимум, исходники простые даже для меня, далёкого от C. Сначала думал самому предложить им изменения, но да ну их... Расширение снёс. Сделал через сокеты, как оказалось работать с memcached напрямую не так уж и сложно.

$key = 'key';
$flags = 1;
$expire = 0;
$value = gzencode('value', 6, FORCE_GZIP);
$length = strlen($value);
if (!fwrite($memcached, "set {$key} {$flags} {$expire} {$length}\r\n{$value}\r\n")) {
        throw new Exception('Memcached write error.');
}

$flags - битовые флаги. Устанавливаю эту переменную равным 1, если $value сжато, иначе 0.
В конфиге nginx соответственно memcached_gzip_flag устанавливаем в 1.
Итого работает это так: сжатие $value экономит место в оперативке. nginx если видит флаг 1, то отдаёт в заголовке ответ "Content-Encoding: gzip", повторно не сжимая, таким образом ещё и процессорное время экономим. Если браузер не поддерживает gzip, то nginx декодирует $value и отдаст без сжатия.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@bnytiki
MIME укажите.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы