Вопросов накопилось по работе с буферами. И как оптимально буферизовать весь исходящий поток внутри фильтра?
Валентин Бартенев
ne на vbart.ru
Чт Дек 2 11:13:57 MSK 2010
Добрый день. Надеюсь на помощь гораздо более опытных, чем я.
Мне в модуле-фильтре необходимо буферизовать весь ответ, сложив все in chains
в два целиковых буфера. Размер первого всегда заранее известен, размер второго
зависит от размера данных и известен только в случае если уже установлен
заголовок content-length. После этого, на основе этих двух полученных буферов
формируется совершенно новый набор данных, которые уже посылаются дальше.
Как сделал я. Я создаю первый буфер, его размер всегда известен заранее и
копирую туда данные из приходящих in chains, пока он не заполнится. Также я
решил сделать небольшую оптимизацию, в случае, если все необходимые для него
данные и так содержатся в пределах одного пришедшего буфера, я использую его,
вместо создания своего и копирования туда.
И тут первый вопрос возник. Я обнаружил, что таким образом я блокирую механизм
повторного использования освобожденных буферов в ngx_output_chain, которую
передо мной вызывает copy-фильтр. Вызывается функция ngx_chain_update_chains,
которая в случае использования мной первого пришедшего буфера в итоге всегда
делает break на первой же итерации. Получается фиговая оптимизация, и лучше в
моем случае всегда копировать данные в свой созданный буфер, или эту пустяки?
Далее, мне нужно заполнить оставшимися данными второй буфер. Если заголовок
content-length установлен, то я поступаю аналогично первому буферу. Если нет,
то я сначала складываю отдельно все приходящие цепочки образуя одну большую
единую цепь из данных, попутно считая их размер, а затем уже, по получении
последнего буфера с признаком last_buf, создаю свой второй единый буфер, зная
размер всех данных, и копирую туда их из собранной цепи.
Опять же такой подход не дает высвобождать буфера до последнего момента. Кроме
этого, как я понял, я не могу просто соединять цепочки, используя приходящие
звенья. Мне необходимо создавать свою цепь звеньев, копируя в них указатели на
буфера. Просто соединение приходящих цепей в некоторых случаях приводит к
зацикливанию nginx уже упомянутым вызовом ngx_chain_update_chains из
ngx_output_chain, так цепь в busy замыкается сама на себя при определенных
обстоятельствах.
В image_filter сделано гораздо проще, там если размер картинки заранее
неизвестен, то выделяется буфер размером "image_filter_buffer" (по-молчанию
1Мб) и данные складываются в него, если их оказывается больше, то не повезло,
ошибочка, если меньше, то излишек простаивает впустую. Неужели такой подход
оптимальнее, чем буферизация цепочки с целью вычисления размера данных? Может
мне тоже так сделать?
Заполнили два буфера всеми пришедшими данными. Теперь, если все Ок, происходит
генерация нового потока данных. Я начинаю выделять буфера по мере наполнения
размером ngx_pagesize выстраивая из них новую цепочку, которую затем и посылаю
далее. Как бы тут сэкономить? В принципе, я могу использовать один из уже
созданных буферов, но этого, как правило недостаточно. В то же время, в цепях
busy и free внутри ngx_output_chain_ctx_t скопилось куча уже неиспользуемых
буферов, можно ли взять от туда? Или можно их сразу складывать у себя
"прозапас"? И как лучше сделать? Или еще откуда-то можно взять? Или лучше не
стоит, и я все правильно делаю?
Надеюсь на ваши разъяснения и подсказки. Возможно некоторые механизмы в nginx,
я еще пока плохо понимаю.
И не могли бы кто-нибудь пояснить назначение и использование в некоторых
местах shadow и last_shadow у буферов?
--
Валентин Бартенев
Подробная информация о списке рассылки nginx-ru