Иногда так случается, что вдруг появилось свободное время, но для чего-то путного его не хватает, а просто
Чтобы сделать все-таки что-нибудь полезного, решил заняться анализом логов с некоторых серверов одного проекта, насколько удастся впихнуть это в пару свободных минут.
После небольшого разбора и оценки в сравнении с результатами предыдущего анализа, заметил одну странность — абсолютная скорость отдачи nginx упала в среднем от 5 до 15%.
Объяснить, чем это вызвано с налета никак не удавалось, больших изменений вроде не было, объемы данных тоже настолько не выросли. Да и на отдаче динамики сильных изменений немного.
Покрутив логи и так и сяк, зацепился за отдачу маленькой статики — выяснилась одна закономерность: чем длиннее путь (url) — тем «медлительней» становился nginx (независимо от размера файла).
Итак после нескольких экспериментов, имеем следующие факты:
- скорость отдачи падает прямо пропорционально увеличению длины пути до файла
- скорость практически не зависит от длинны URL, т.е. если URL короткий, но увеличиваем длину root/alias, скорость отдачи падает также, т.е. это все-таки длинна пути, а не URL
- ну и наконец, поиграв с путями файла, а именно его вложенности, выяснилось, что скорость отдачи падает в зависимости от количества поддиректорий, и не зависит от длины как-таковой. Т.е. файл «D:\...\ms-ms-ms-ms-ms-ms-ms-ms\test.gif» отдается много быстрее «D:\...\ms\ms\ms\ms\ms\ms\ms\ms\test.gif»
И тут пришло озарение — я вспомнил, что в этом проекте изменилась файловая структура, и вложенность до некоторой статики и динамики, отдаваемой файлом (по redirect), увеличилась на два-три, а местами до пяти каталогов.
Компилировать debug-версию под профайлер-анализатор было лень, будем искать «дедуктивным» способом.
Измеряем с помощью siege среднюю скорость отдачи «test.gif» — порядка 5500 rps (nginx 4 workers, concurency 15).
Создаем тестовый location с root «D:\...\ms\ms\ms\ms\ms\ms\ms\ms», где сегмент пути ms повторяется 60 раз и запрашиваем URL «test-loc/test.gif».
Снова измеряем среднюю скорость отдачи — уже 900 rps.
Возможно это что-то где-то на уровне проверки легальности пути. Пробуем замерить 404 для несуществующего файла с URL «test-loc/test-file-not-exists.gif» — скорость вырастает до 13167 rps и что странно вложенность пути уже не имеет значения.
Включаем дебаг в логгировании nginx, и затем детально анализируем логи в сравнении малая — большая вложенность пути. В среднем время от «http request line» до «http filename» одинаково, но запись «http static fd» для длинного пути уже ощутимо отстает.
Довольно странно, поэтому пытаемся проверить что происходит, используя процесс-мониторинг.
15:23:43,2205361 nginx.exe 9780 CreateFile D:\ SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2206208 nginx.exe 9780 QueryDirectory D:\Projects SUCCESS Filter: Projects, 1: Projects
15:23:43,2206430 nginx.exe 9780 CloseFile D:\ SUCCESS
15:23:43,2207337 nginx.exe 9780 CreateFile D:\Projects SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2207844 nginx.exe 9780 QueryDirectory D:\Projects\app_srv SUCCESS Filter: app_srv, 1: app_srv
15:23:43,2208009 nginx.exe 9780 CloseFile D:\Projects SUCCESS
15:23:43,2208728 nginx.exe 9780 CreateFile D:\Projects\app_srv SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2209238 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp SUCCESS Filter: tmp, 1: tmp
15:23:43,2209383 nginx.exe 9780 CloseFile D:\Projects\app_srv SUCCESS
15:23:43,2210075 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2210615 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp\ms SUCCESS Filter: ms, 1: ms
15:23:43,2210764 nginx.exe 9780 CloseFile D:\Projects\app_srv\tmp SUCCESS
15:23:43,2211466 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp\ms SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2211995 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp\ms\ms SUCCESS Filter: ms, 1: ms
15:23:43,2212141 nginx.exe 9780 CloseFile D:\Projects\app_srv\tmp\ms SUCCESS
15:23:43,2212813 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp\ms\ms SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2213326 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp\ms\ms\ms SUCCESS Filter: ms, 1: ms
15:23:43,2213462 nginx.exe 9780 CloseFile D:\Projects\app_srv\tmp\ms\ms SUCCESS
15:23:43,2214193 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp\ms\ms\ms SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2214677 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp\ms\ms\ms\ms SUCCESS Filter: ms, 1: ms
15:23:43,2214806 nginx.exe 9780 CloseFile D:\Projects\app_srv\tmp\ms\ms\ms SUCCESS
# ... 106 lines ... #
15:23:43,2265328 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2265851 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Filter: ms, 1: ms
15:23:43,2265980 nginx.exe 9780 CloseFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS
15:23:43,2266665 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2267165 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Filter: ms, 1: ms
15:23:43,2267291 nginx.exe 9780 CloseFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS
15:23:43,2267976 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2268489 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Filter: ms, 1: ms
15:23:43,2268622 nginx.exe 9780 CloseFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS
15:23:43,2269314 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2269814 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Filter: ms, 1: ms
15:23:43,2269946 nginx.exe 9780 CloseFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS
15:23:43,2270615 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2271135 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Filter: ms, 1: ms
15:23:43,2271264 nginx.exe 9780 CloseFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS
15:23:43,2271989 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2272525 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Filter: ms, 1: ms
15:23:43,2272654 nginx.exe 9780 CloseFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS
15:23:43,2273343 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2273876 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Filter: ms, 1: ms
15:23:43,2274005 nginx.exe 9780 CloseFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS
15:23:43,2274743 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2275246 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Filter: ms, 1: ms
15:23:43,2275375 nginx.exe 9780 CloseFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS
15:23:43,2276034 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2276531 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Filter: ms, 1: ms
15:23:43,2276657 nginx.exe 9780 CloseFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS
15:23:43,2277332 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Desired Access: Read Data/List Directory, Synchronize, Disposition: Open, Options: Directory, Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
15:23:43,2277819 nginx.exe 9780 QueryDirectory D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS Filter: ms, 1: ms
15:23:43,2277944 nginx.exe 9780 CloseFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms SUCCESS
15:23:43,2278987 nginx.exe 9780 CreateFile D:\Projects\app_srv\tmp\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\ms\test.gif SUCCESS Desired Access: Generic Read, Disposition: Open, Options: Synchronous IO Non-Alert, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a, OpenResult: Opened
Причем на каждый запрос это наблюдается дважды, вероятно один раз при открытии, второй раз при чтении атрибутов файла.
Лезем в исходники nginx, довольно быстро находим в
[src]\os\win32\ngx_files.c
функцию ngx_open_file
, которая только в случае успешного открытия файла (а мы помним про быстрый 404), вызывает другую функцию ngx_win32_check_filename
(проверка легальности имени файла).Эту же функцию
ngx_win32_check_filename
вызывает и другая функция ngx_file_info
. Ну и рассмотрим под лупой наш кандидат на роль «ручного тормоза».Сперва идет проверка правильности пути сегментов, исключения NTFS streams (":"), точек и пробелов в конце и т.д. Но в этом теле цикла проверкой не осуществляются никакие системные вызовы.
Идем дальше и видим вызов GetLongPathNameW в блоке, проверяющем длинное имя файла.
Для пробы закомментируем эту проверку, компилируем nginx — в результате имеем 9100 rps, там где ранее nginx отдавал 900 rps.
Видимо Максим когда делал эту проверку не знал (ваш покорный слуга кстати тоже), что Windows возвращает длинное имя файла рекурсивно, т.е. для каждого подкаталога (и как видим эта информация не кэшируется в OS).
К сожалению, таким образом (т.е. полностью) не стоит убирать эту проверку, чтобы исключить доступ к файлам по коротким именам 8.3 (вида «testpa~1/testfi~2.gif»), а лучшей функции делающей тоже самое, я к сожалению не знаю.
Однако можно добавить выше установку специального флажка, проверкой существования символа "~", только при наличии которого мы и будем осуществлять проверку длинного пути, вызывая
GetLongPathNameW
.В результате при минимальных изменениях имеем рост скорости от 2 до 10 раз (зависит от вложенности и размеров файла) и в разы меньшую нагрузку на дисковую подсистему.
Ну и как обычно сравнения производительности, до (первый и третий столбцы помечены **NF) и после.
Первые два — для короткого (12 сегментов) пути.
Следующие два — для длинного (55 сегментов) пути.
Path, Segments | Short, 12 **NF | Short, 12 | Long, 55 **NF | Long, 55 |
---|---|---|---|---|
Transactions | 16732 hits | 28380 hits | 2962 hits | 26820 hits |
Availability | 100.00 % | 100.00 % | 100.00 % | 100.00 % |
Elapsed time | 2.96 secs | 2.96 secs | 2.97 secs | 2.97 secs |
Data transferred | 7.21 MB | 12.23 MB | 1.28 MB | 11.56 MB |
Response time | 0.00 secs | 0.00 secs | 0.00 secs | 0.00 secs |
Transaction rate | 5645.07 trans/sec | 9591.08 trans/sec | 996.04 trans/sec | 9030.30 trans/sec |
Throughput | 2.43 MB/sec | 4.13 MB/sec | 0.44 MB/sec | 3.89 MB/sec |
Concurrency | 14.92 | 14.72 | 14.80 | 14.12 |
Successful transactions | 16732 | 28380 | 2962 | 26820 |
Failed transactions | 0 | 0 | 0 | 0 |
Longest transaction | 0.11 | 0.11 | 0.12 | 0.12 |
Shortest transaction | 0.00 | 0.00 | 0.00 | 0.00 |
У себя в branch nginx-mod я уже интегрировал этот этот PR.
Будет время, сделаю changeset для оригинального hg-репозитария и отправлю CR в nginx-dev.
Комментарии (29)
dMetrius
08.10.2015 16:13-1nginx, windows?
Version of nginx for Windows uses the native Win32 API (not the Cygwin emulation layer). Only the select() connection processing method is currently used, so high performance and scalability should not be expected. Due to this and some other known issues version of nginx for Windows is considered to be a beta version.TimsTims
09.10.2015 00:02+2Посмотрите его первую статью http://habrahabr.ru/post/260133/
Там автор довольно неплохо скомпилил nginx для хорошей работе на Windows с блэкджеком и шлюзами
baldr
08.10.2015 16:13А может кто-нибудь проверить — есть ли и в исходниках для *nix похожая функция ngx_linux_check_filename?
На хабре есть isysoev — возможно он что-нибудь прокомментирует?sebres
08.10.2015 16:24+2ngx_open_file в *nix-ах это define на стандартный open
Но там полно другого чего, например подозреваю что директивы типа «disable_symlinks» могут натянуть ручник на некоторых FS.#define ngx_open_file(name, mode, create, access) open((const char *) name, mode|create|O_BINARY, access)
Хотя в *nix-ах он получше оттестирован, хотя бы из-за его распространенности.
isysoev
08.10.2015 16:30+9В стандартных файловых системах Линукса, к счастью, нет способа обратиться к файлу миллионами разных способов, поэтому нет и необходимости в ngx_linux_check_filename().
isysoev
08.10.2015 16:30+1В стандартных файловых системах Линукса, к счастью, нет способа обратиться к файлу миллионами разных способов, поэтому нет и необходимости в ngx_linux_check_filename().
ildarz
08.10.2015 17:39К сожалению, таким образом (т.е. полностью) не стоит убирать эту проверку, чтобы исключить доступ к файлам по коротким именам 8.3 (вида «testpa~1/testfi~2.gif»),
А можете пояснить для чайников, зачем эта проверка вообще нужна?sebres
08.10.2015 17:45Конкретно доступ к файлу под коротким именем? Думается, чтобы исключить перебор (типа брута и т.д.).
В принципе по той же причине, по какой выключают directory listing.ildarz
08.10.2015 18:02Я просто в целом логику работы не понимаю в данном случае.
находим в [src]\os\win32\ngx_files.c функцию ngx_open_file, которая только в случае успешного открытия файла (а мы помним про быстрый 404), вызывает другую функцию ngx_win32_check_filename (проверка легальности имени файла).
Т.е. мы сначала открываем файл, и, если операция успешна, проверяем длинное имя. Зачем? Что должно дальше происходить по результатам этой проверки?
P.S. Btw, генерация 8.3 имен в винде отключается. Может, просто имеет смысл включить это в системные требования к установке и не париться с проверкой?isysoev
08.10.2015 18:33+1Например, в такой конфигурации
location /authorization/ { auth_basic ... fastcgi_pass ... }
запрос "/author~1/script.php" не будет обработан в этом location'е.
Поэтому разрешаются только обращения по полному имени.
intnzy
08.10.2015 21:02Несколько оффтопик, но меня ужасно интересует — в чем необходимость именно такой связки nginx-win? Именно не для тестирования а реальные use cases.
sebres
08.10.2015 21:13Именно такой, это какой?
Если это намек в сторону IIS, appache или любимое подставить, то у меня будет встречный вопрос «Когда оне будут такие же шустрые, динамичные и гибкие (во всех смыслах) как nginx?»
Т.е. для реального use case с преф-ом и девочками, а не поиграться и не для портала на 10 человек аля sharepoint?
Я беру nginx не потому, что он есть под linux — а потому-что он nginx. И не беру IIS, потому что он IIS.intnzy
08.10.2015 22:40Про nginx более-менее понятно. Непонятно 2 часть. Почему тогда Windows? Или nginx сервает ASP?
Evengard
09.10.2015 01:58Сейчас кстати ASP.NET сервается хорошо и из под Линукса. У самого такой сетап.
sebres
09.10.2015 13:53Потому что не сам себе режиссер. Например, в корпоративном секторе от виндовс часто тупо никуда не деться.
Или nginx сервает ASP?
Что во фразе «и не беру IIS» было не понятно…
maksqwe
08.10.2015 21:07«Видимо Максим когда делал эту проверку не знал (ваш покорный слуга кстати тоже), что Windows возвращает длинное имя файла рекурсивно, т.е. для каждого подкаталога (и как видим эта информация не кэшируется в OS).»
Кстати, а где вы это узнали? Msdn, судя по всему, молчит по этому поводу.
Или методом «Для пробы закомментируем эту проверку...»?
maksqwe
08.10.2015 22:48+2Прочитал статью не заглянув туда. Действительно, не очевидное поведение системы, хотя логичное в данном случае.
И вы натолкнули меня на мысль проверить кое-что.
Являюсь пользователем Qt как для домашних так и для рабочих проектов уже около 4 лет, иногда даже принимают мои патчи.
Данный системный вызов(«GetLongPathName») используется так же в основном модуле библиотеки при возврата пути к папки в которой система по-умолчанию хранит временные файлы, упущу некоторый код, в общем это:
QString QFileSystemEngine::tempPath()
{
QString ret;
wchar_t tempPath[MAX_PATH];
const DWORD len = GetTempPath(MAX_PATH, tempPath);
if (len) { // GetTempPath() can return short names, expand.
wchar_t longTempPath[MAX_PATH];
const DWORD longLen = GetLongPathName(tempPath, longTempPath, MAX_PATH);
ret = longLen && longLen < MAX_PATH?
QString::fromWCharArray(longTempPath, longLen):
QString::fromWCharArray(tempPath, len);
}
…
}
То есть при каждом вызове данной функции будет вызываться GetLongPathName().
Потом почитал в каких случаях GetTempPath() возвращает «короткий» путь, оказывается в XP и то иногда нет(?) путь действительно возвращается «короткий», почему так делалось можно почитать в относительно старой статьи в достаточно известном блоге — http://blogs.msdn.com/b/oldnewthing/archive/2004/12/24/331750.aspx
В Windows > XP этот путь возвращается полный. Отчего при каждом вызове данного метода вызываем не нужный достаточно медленный системный вызов.
Потому добавив код:
…
wchar_t * pwc = wcschr(tempPath, L'~');
if (pwc) { // GetTempPath() can return short names, expand.
wchar_t longTempPath[MAX_PATH];
const DWORD longLen = GetLongPathName(tempPath, longTempPath, MAX_PATH);
ret = longLen && longLen < MAX_PATH?
QString::fromWCharArray(longTempPath, longLen):
QString::fromWCharArray(tempPath, len);
}
else {
ret = QString::fromWCharArray(tempPath, len);
}
…
И погоняв несколько бенчмарков выяснилось что в среднем прирост скорости до 5 раз.
На выходных оформлю патчик и кину на кодревью в Qt для этого случая и в пару других мест в коде.
P.S. Теги не пашут, нет кармы чтобы нормально код оформить.Maccimo
09.10.2015 17:09Нельзя так делать.
Ответ почему — в том же блоге, что и заметка про TEMP: http://blogs.msdn.com/b/oldnewthing/archive/2004/04/14/113052.aspxsebres
09.10.2015 17:14Ну да Novell, Windows NT etc. все еще рулят…
Maccimo
09.10.2015 17:27То, что баг трудновоспроизводим в современном окружении, вряд ли может быть оправданием для его игнорирования.
От того, что современная версия Windows называется «Windows 10», последняя не перестаёт быть наследницей Windows NT.maksqwe
09.10.2015 17:39Спасибо за замечание. Очень жаль что так получается, надо почитать поразбиратсья с этим.
maksqwe
09.10.2015 20:18Прочитал эту заметку и, честно говоря, не понял почему так делать не нужно, особенно в случае когда берется значение этих системных переменных:
— The path specified by the TMP environment variable.
— The path specified by the TEMP environment variable.
— The path specified by the USERPROFILE environment variable.
— The Windows directory.
«Windows NT adds the hexadecimal hash overflow technique.» Что это за техника я тоже не понял и не нашел что-либо о ней.
Разве что действительно в редких случаях сети netware будут выдавать не полное имя файла… Судя по их сайту они вообще с 2005 года портируют свою система на Linux-based ядро.
Зато в мсдне написано как формируются 8.3 формат имени:
https://support.microsoft.com/en-us/kb/142982
и там еще внизу написано:
Applies to
Microsoft Windows Millennium Edition
Microsoft Windows 98 Standard Edition
Microsoft Windows 95
Microsoft Windows NT Server 4.0 Standard Edition
Microsoft Windows NT Workstation 4.0 Developer Edition
Так что считаю данное изменение имеет значение, особенно когда прирост в скорости не абстрактная величина, граничащая со случайностью, в 1.1 — 1.2 раза, а вполне себе в разы. Пока не сможете рассказать что имелось ввиду под «Windows NT adds the hexadecimal hash overflow technique.»maksqwe
09.10.2015 20:38Уже сам нашел. https://usn.pw/blog/gen/2015/06/09/filenames/
The file extension is truncated to 3 characters, and (if longer than 8 characters) the file name is truncated to 6 characters followed by ~1. SomeStuff.aspx turns into SOMEST~1.ASP.
If these would cause a collision, ~2 is used instead, followed by ~3 and ~4.
Instead of going to ~5, the file name is truncated down to 2 characters, with the replaced replaced by a hexadecimal checksum of the long filename — SomeStuff.aspx turns into SOBC84~1.ASP, where BC84 is the result of the (previously-)undocumented checksum function.
То есть когда очень много одинаковых имен файлов в одной папке система будет резать часть имени и добавлять свой хеш чтобы вместиться в формат 8.3 с помощью недокументированной внутренней системной функции. В той статье как раз ее и пытаются найти, точнее нашли и разобрались как работает. Но все равно тильда будет.maksqwe
09.10.2015 20:45Потому то и майкрософтовцы сами и советуют на виндо-серверах отключать генерацию 8.3 формата.
https://technet.microsoft.com/en-us/library/ff633453%28v=ws.10%29.aspx
Issue
In addition to the normal file names, the server is creating short, eight-character file names with a three-character file extension (8.3 file names) for all files.
Impact
Creating short file names in addition to the normal, long file names can significantly decrease file server performance.
Странно что по умолчанию не выключено. Хотя это критично лишь к файл-серверам.
Maccimo
09.10.2015 17:21Проверка на тильду — костыль и потенциальные грабли, не стоит так делать.
Её наличие в коротком пути в общем случае никто не гарантирует: http://blogs.msdn.com/b/oldnewthing/archive/2004/04/14/113052.aspx
Лучше, как советовали в комментариях выше, попробовать отключить генерацию 8.3: https://support.microsoft.com/en-us/kb/121007sebres
09.10.2015 18:39+1Лучше, как советовали в комментариях выше, попробовать отключить генерацию 8.3
Во первых, отключить простите где? На хосте корпоративного клиента? С удовольствием посмотрю на то как вы там админов уговаривать будете…
Во вторых, «Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: 8.3 aliasing cannot be disabled for specified volumes until Windows 7 and Windows Server 2008 R2.» Взято из Naming Files, Paths, and Namespaces (Windows)
В третих, одно другому совершенно не мешает.
Проверка на тильду — костыль и потенциальные грабли, не стоит так делать.
Вы вероятно имеете ввиду «Not all file systems follow the tilde substitution convention». Этой фразе 20-ть лет в обед исполняется. Ну и отключайте это для тех obscure-obsolete FS.
Кроме того перебор коротких путей без «tilde substitution convention» не имеет большого смысла.
А защита внутренних location (пример Игоря выше про php-скрипт) достигается совершенно другим способом: не нужно иметь глобальных location позволяющих вернуть любой файл системы. Это моветон.
UdarEC
Очень интересная особенность оказалась.