Анализ и оптимизация времени TTFB

Thank you for reading this post, don't forget to subscribe!

В широ­ком смыс­ле, TTFB — это мет­ри­ка, кото­рая пока­зы­ва­ет вре­мя до полу­че­ния пер­во­го бай­та (сете­во­го паке­та) веб-стра­ни­цы после отправ­ки запро­са со сто­ро­ны клиента.

Изме­ре­ние вклю­ча­ет запрос DNS, вре­мя под­клю­че­ния к сер­ве­ру и вре­мя ожи­да­ния обра­бо­тан­но­го запро­са (обра­бот­ка, пере­па­ков­ка, отправ­ка стра­ни­цы). Интересно,что тер­мин часто пута­ют c вре­ме­нем откли­ка сер­ве­ра — этот пока­за­тель дает воз­мож­ность оце­нить ско­рость реак­ции на HTTP-запрос при отсут­ствии сете­вой задерж­ки. На TTFB вли­я­ет почти все: сете­вые про­бле­мы и задерж­ки, объ­ем вхо­дя­ще­го тра­фи­ка, настрой­ки веб-сер­ве­ра, объ­ем и опти­ми­зи­ро­ван­ность кон­тен­та ( каче­ство гра­фи­ки, раз­мер css/js/html).

Оче­вид­но, что не на все выше­пе­ре­чис­лен­ные момен­ты лег­ко повли­ять. У вас вряд ли будет воз­мож­ность само­сто­я­тель­но улуч­шить каче­ство сети, а высо­кий тра­фик на веб-сайт может быть чем-то пло­хим толь­ко в слу­чае DDoS-атак. Един­ствен­ное, на что реаль­но повли­ять — бекенд, так что зай­мем­ся тюнин­гом Nginx.

Анализ TTFB

На про­сто­рах Все­мир­ной пау­ти­ны раз­ме­ще­но боль­шое коли­че­ство ресур­сов, посвя­щен­ных про­вер­ке ско­ро­сти загруз­ки веб-стра­ниц. Один из самых попу­ляр­ных и ува­жа­е­мых — WebPageTest.org.

Он предо­став­ля­ет более, чем исчер­пы­ва­ю­щую инфор­ма­цию о вре­ме­ни под­клю­че­ния, TTFB, вре­ме­ни ини­ци­а­ли­за­ции TLS/SSL (если исполь­зу­ет­ся), а так­же о загруз­ке отдель­ных эле­мен­тов веб-страницы.

Для про­вер­ки ско­ро­сти загруз­ки и целой стоп­ки допол­ни­тель­ных пара­мет­ров мож­но исполь­зо­вать бра­у­зер. И в Chrome, и в FireFox, и даже в Safari при­сут­ству­ют соот­вет­ству­ю­щие ути­ли­ты разработчика.

Оптимизация Nginx

Еще раз крат­ко прой­дем­ся по уже извест­ным пара­мет­рам и доба­вим несколь­ко новых, кото­рые непо­сред­ствен­но вли­я­ют на TTFB.

Соединения

Для нача­ла необ­хо­ди­мо задать коли­че­ство “работ­ни­ков” Nginx.

Каж­дый рабо­чий про­цесс спо­со­бен обра­ба­ты­вать мно­же­ство соеди­не­ний и при­вя­зы­ва­ет­ся к физи­че­ским ядрам про­цес­со­ра. Если вы точ­но зна­е­те, сколь­ко ядер в вашем сер­ве­ре, то мож­но задать их коли­че­ство само­сто­я­тель­но, или дове­рить­ся Nginx:

# Опре­де­ле­ние коли­че­ства рабо­чих процессов

Кро­ме это­го необ­хо­ди­мо задать коли­че­ство соединений:

# Опре­де­ле­ние коли­че­ства соеди­не­ний на один рабо­чий про­цесс, в пре­де­лах от 1024 до 4096

Запросы

Что­бы веб-сер­вер мог обра­ба­ты­вать мак­си­маль­ное коли­че­ство запро­сов, необ­хо­ди­мо задей­ство­вать выклю­чен­ную по умол­ча­нию дирек­ти­ву multi_accept:

# Рабо­чие про­цес­сы будут при­ни­мать все соединения

При­ме­ча­тель­но, что функ­ция будет полез­на толь­ко при усло­вии боль­шо­го коли­че­ства запро­сов одно­вре­мен­но. Если же запро­сов не так мно­го, име­ет смысл опти­ми­зи­ро­вать рабо­чие про­цес­сы, что­бы они не тру­ди­лись вхолостую:

# Рабо­чие про­цес­сы будут при­ни­мать соеди­не­ния по очереди

Улуч­ше­ние TTFB и вре­ме­ни откли­ка сер­ве­ра напря­мую зави­сит от дирек­тив tcp_nodelay и tcp_nopush:

# Акти­ва­ция дирек­тив tcp_nodelay и tcp_nopush

Если не слиш­ком вда­вать­ся в подроб­но­сти, то обе функ­ции поз­во­ля­ют отклю­чить неко­то­рые осо­бен­но­сти TCP, кото­рые были акту­аль­ны в 90х, когда Интер­нет толь­ко наби­рал обо­ро­ты, но не име­ют смыс­ла в совре­мен­ных усло­ви­ях. Пер­вая дирек­ти­ва отправ­ля­ет дан­ные, как толь­ко они будут доступ­ны (обхо­дит алго­ритм Нейг­ла). А вто­рая дает воз­мож­ность отправ­лять заго­ло­вок отве­та (веб-стра­ни­цы) и нача­ло фай­ла, ожи­дая запол­не­ния паке­та (то есть, вклю­ча­ет tcp_cork). Так что бра­у­зер смо­жет начать отоб­ра­же­ние веб-стра­ни­цы раньше.

На пер­вый взгляд, функ­ции про­ти­во­ре­чат друг дру­гу. Поэто­му дирек­ти­ва tcp_nopush долж­на исполь­зо­вать­ся вме­сте с sendfile. В этом слу­чае паке­ты будут запол­не­ны до отправ­ки, т.к. дирек­ти­ва рабо­та­ет намно­го быст­рее и опти­маль­нее, чем метод read+write. После того, как пакет запол­нен, Nginx авто­ма­ти­че­ски отклю­ча­ет tcp_nopush, а tcp_nodelay застав­ля­ет сокет отпра­вить дан­ные. Вклю­чить sendfile очень просто:

# Вклю­че­ние более эффек­тив­но­го, по срав­не­нию с read+write, мето­да отправ­ки файлов

Так что ком­би­на­ция всех трех дирек­тив сни­жа­ет нагруз­ку на сеть и уско­ря­ет отправ­ку файлов.

Буферы

Еще одна важ­ная опти­ми­за­ция затра­ги­ва­ет раз­мер буфе­ров — если они слиш­ком малень­кие, Nginx будет часто обра­щать­ся к дис­кам, слиш­ком боль­шие — быст­ро запол­нит­ся опе­ра­тив­ная память.

Для это­го потре­бу­ет­ся настро­ить четы­ре дирек­ти­вы. client_body_buffer_size и client_header_buffer_size зада­ют раз­мер буфе­ра для чте­ния тела и заго­лов­ка запро­са кли­ен­та соот­вет­ствен­но. client_max_body_size зада­ет мак­си­маль­ный раз­мер запро­са кли­ен­та, а large_client_header_buffers зада­ет мак­си­маль­ное чис­ло и раз­мер буфе­ров для чте­ния боль­ших заго­лов­ков запросов.

Опти­маль­ные пара­мет­ры буфе­ров будут выгля­деть так:

# Раз­мер буфе­ра 10 КБ на тело запро­са, 1 КБ на заго­ло­вок, 8 МБ на сам запрос и 2 буфе­ра для чте­ния боль­ших заголовков

Таймауты и keepalive

Пра­виль­ная настрой­ка вре­ме­ни ожи­да­ния и keepalive так­же может суще­ствен­но повы­сить отзыв­чи­вость сервера.

Дирек­ти­вы client_body_timeout и client_header_timeout зада­ют вре­мя ожи­да­ния на чте­ние тела и заго­лов­ка запроса:

# Уста­нов­ка вре­ме­ни ожи­да­ния, в секундах

При этом в слу­чае отсут­ствия отве­та от кли­ен­та при помо­щи reset_timedout_connection мож­но ука­зать Nginx отклю­чать такие соединения:

# Отклю­че­ние соеди­не­ний, пре­вы­сив­ших вре­мя ожидания

Дирек­ти­ва keepalive_timeout зада­ет вре­мя ожи­да­ния, преж­де чем пре­кра­тить соеди­не­ние, а keepalive_requests огра­ни­чи­ва­ет коли­че­ство keepalive-запро­сов от одно­го клиента:

# Уста­нов­ка вре­ме­ни ожи­да­ния 30 с и огра­ни­че­ния в 100 запро­сов на клиент

Ну а send_timeout зада­ет вре­мя ожи­да­ния при пере­да­че отве­та меж­ду дву­мя опе­ра­ци­я­ми записи:

# Nginx будет ждать отве­та 2 с

Кэширование

Вклю­че­ние кэши­ро­ва­ния суще­ствен­но улуч­шит вре­мя откли­ка сервера.

Мето­ды более подроб­но выло­же­ны в мате­ри­а­ле о кэши­ро­ва­нии с Nginx, но в дан­ном слу­чае акту­аль­но вклю­че­ние cache-control. Nginx спо­со­бен отправ­лять запрос на кэши­ро­ва­ние ред­ко­из­ме­ня­е­мых дан­ных, кото­рые часто исполь­зу­ют­ся, на сто­роне кли­ен­та. Для это­го в сек­цию server нуж­но доба­вить строчку:

# Зада­ние фор­ма­тов фай­лов и дли­тель­но­сти хра­не­ния кэша

Так­же не поме­ша­ет зак­э­ши­ро­вать инфор­ма­цию о часто исполь­зу­е­мых файлах:

# Раз­ре­ша­ет кэши­ро­вать дескрип­то­ры 10 000 фай­лов в тече­ние 30 секунд

open_file_cache зада­ет мак­си­маль­ное коли­че­ство фай­лов, инфор­ма­ция о кото­рых будет хра­нить­ся, и вре­мя хра­не­ния. open_file_cache_valid зада­ет вре­мя, после кото­ро­го нуж­но про­ве­рить акту­аль­ность инфор­ма­ции, open_file_cache_min_uses опре­де­ля­ет мини­маль­ное коли­че­ство обра­ще­ний к фай­лу со сто­ро­ны кли­ен­тов, а open_file_cache_errors вклю­ча­ет кэши­ро­ва­ние оши­бок поис­ка файлов.

Логирование

Это еще одна функ­ция, кото­рая может незна­чи­тель­но сни­зить про­из­во­ди­тель­ность все­го сер­ве­ра и, соот­вет­ствен­но, вре­мя откли­ка и TTFB. Так что луч­шим реше­ни­ем будет отклю­чить основ­ной лог, а сохра­нять инфор­ма­цию толь­ко о кри­ти­че­ских ошибках:

# Отклю­че­ние основ­но­го логирования

Сжатие Gzip

Полез­ность Gzip слож­но пре­уве­ли­чить. Сжа­тие поз­во­ля­ет зна­чи­тель­но умень­шить тра­фик и раз­гру­зить канал. Но у него есть и обрат­ная сто­ро­на — для ком­прес­сии нуж­но вре­мя. Так что для улуч­ше­ния TTFB и вре­ме­ни откли­ка сер­ве­ра его при­дет­ся отключить.

На дан­ном эта­пе мы не можем реко­мен­до­вать отклю­че­ние Gzip, так как сжа­тие улуч­ша­ет Time To Last Byte, то есть, вре­мя, тре­бу­е­мое для пол­ной загруз­ки стра­ни­цы. А это в боль­шин­стве слу­ча­ев более важ­ный пара­метр. На улуч­ше­ние TTFB и вре­ме­ни откли­ка сер­ве­ра суще­ствен­но повли­я­ет мас­штаб­ное внед­ре­ние HTTP/2, кото­рый содер­жит встро­ен­ные мето­ды ком­прес­сии заго­лов­ков и муль­ти­плек­си­ро­ва­ние. Так что в буду­щем, воз­мож­но, отклю­че­ние Gzip будет не таким замет­ным, как сейчас.

Оптимизация PHP: FastCGI в Nginx

Все совре­мен­ные сай­ты исполь­зу­ют сер­вер­ные тех­но­ло­гии. PHP, к при­ме­ру, кото­рый так­же важ­но опти­ми­зи­ро­вать. Обыч­но PHP откры­ва­ет файл, про­ве­ря­ет и ком­пи­ли­ру­ет код, затем выпол­ня­ет. Таких фай­лов и про­цес­сов может быть мно­же­ство, поэто­му PHP уме­ет кэши­ро­вать резуль­тат для ред­ко­из­ме­ня­е­мых фай­лов при помо­щи моду­ля OPcache. А Nginx, под­клю­чен­ный к PHP при помо­щи моду­ля FastCGI, может сохра­нять резуль­тат выпол­не­ния скрип­та PHP для момен­таль­ной отправ­ки пользователю.

Самое важное

Опти­ми­за­ция ресур­сов и пра­виль­ные настрой­ки веб-сер­ве­ра — основ­ные вли­я­ю­щие на TTFB и вре­мя откли­ка сер­ве­ра фак­то­ры. Так­же не сто­ит забы­вать о регу­ляр­ных обнов­ле­ни­ях ПО до ста­биль­ных вер­сий, кото­рые несут опти­ми­за­ции и повы­ше­ния производительности.