<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>www.rusinov.ie (Записи о python)</title><link>https://www.rusinov.ie/</link><description></description><atom:link href="/ru/tags/python.xml" rel="self" type="application/rss+xml"></atom:link><language>ru</language><copyright>Contents © 2026 &lt;a href="mailto:vladimir.rusinov@gmail.com"&gt;Vladimir Rusinov&lt;/a&gt; </copyright><lastBuildDate>Fri, 06 Feb 2026 07:19:27 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Введение в nginx, часть 2: Другие возможности</title><link>/ru/posts/2009/nginx-2/</link><dc:creator>Vladimir Rusinov</dc:creator><description>&lt;p&gt;Данная статья была опубликована в электронном приложением к журналу "&lt;a href="http://www.samag.ru/"&gt;Системный администратор&lt;/a&gt;"- "&lt;a href="http://osa.samag.ru/"&gt;Open Source #042&lt;/a&gt;"&lt;/p&gt;
&lt;p&gt;&lt;a href="http://blog-ru.greenmice.info/2013/04/nginx-1.html"&gt;В первой части&lt;/a&gt; статьи я рассказал о базовых и наиболее часто применяемых возможностях nginx. Однако это малая часть того, что можно сделать с nginx. Во второй части своей статьи я расскажу о некоторых более продвинутых возможностях, которые используются в крупных и высоконагруженых проектах.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h3&gt;Failover и балансировка&lt;/h3&gt;Крупные проекты редко состоят из одного сервера приложений. Часто их два или больше, и возникает задача балансировки клиентов по этим серверам, а также выполнения failover — необходимо чтобы выход из строя одного из серверов не был заметен для клиентов.&lt;br&gt;Простейший способ рещить эту задачу — dns round-robin, т.е. назначение доменному имени нескольких ip-адресов. Но это решение имеет ряд недостатков, и гораздо лучше выглядит решение балансировки запросов по бакендам на фронтенде nginx. В конфигурационном файле выглядит это примерно так:&lt;br&gt;&lt;br&gt;&lt;pre class="brush: c"&gt;# объявляем upstream — список бакендов&lt;br&gt;upstream  backend  {&lt;br&gt;  # перечисляем dns-имена или ip-адреса серверов и их «вес»&lt;br&gt;  server   web1                 weight=5;&lt;br&gt;  server   1.2.3.4:8080         weight=5;&lt;br&gt;  # а так можно подключаться к бакенду через unix-сокет&lt;br&gt;  server   unix:/tmp/backend3   weight=1;&lt;br&gt;}&lt;br&gt;# конфигурация виртуального сервера&lt;br&gt;server {&lt;br&gt;  listen &amp;lt;...&amp;gt;;&lt;br&gt;  server_name myserver.com;&lt;br&gt;  # отправляем все запросы из локейшена / в апстрим&lt;br&gt;  location / {&lt;br&gt;    proxy_pass  http://backend;&lt;br&gt;  }&lt;br&gt;}&lt;br&gt;&lt;/pre&gt;&lt;br&gt;Запросы, приходящие к nginx, распределяются по бакендам соответственно указаному весу. Кроме того, можно сделать так, чтобы запросы с одних и тех же IP-адресов отправлялись на одни и те же серверы (для этого в upstream нужно указать директиву ip_hash). Так можно решить проблему с сессиями, но все же лучше найти какой-нибудь способ их репликации или (что еще лучше) использовать &lt;a href="http://ru.wikipedia.org/wiki/REST"&gt;RESTful-подход&lt;/a&gt;.&lt;br&gt;В случае, если один из серверов откажется принимать соединения или соединение к нему отвалится по таймауту, он на некоторое время будет исключен из upstream.
&lt;h3 id="nginx"&gt;Оптимизация nginx&lt;/h3&gt;
&lt;h4 id="1"&gt;1. Увеличение количества и объема буферов&lt;/h4&gt;
&lt;p&gt;Для хранения принятых запросов и еще не отданных ответов nginx использует буферы в памяти, а если запрос или ответ не помещается в них, nginx записывает его во временный файл (и пишет при этом предупреждение в log-файл). Поэтому необходимо установить такие размеры, чтобы в большинстве случаев не требовалось обращаться к временному файлу, а с другой стороны — чтобы буферы не использовали слишком много памяти.&lt;br&gt;&lt;br&gt;Для этого используются следующие параметры:&lt;br&gt;&lt;b&gt;client_body_buffer_size&lt;/b&gt; (по умолчанию: 8k/16k — в зависимости от архитектуры) — задает размер буфера для чтения тела запроса клиента. Обычно стандартного значения хватает, его требуется повышать, только если ваше приложение устанавливает огромные cookies.&lt;br&gt;&lt;b&gt;proxy_buffer_size&lt;/b&gt; (по умолчанию: 4k/8k) — задает размер буфера, в который будет читаться первая часть ответа, получаемого от проксируемого сервера. В этой части ответа находится, как правило, небольшой заголовок ответа. Стандартного значения обычно хватает.&lt;br&gt;&lt;b&gt;proxy_buffers&lt;/b&gt; (по умолчанию: 8 4k/8k) — задает число и размер буферов для одного соединения, в которые будет читаться ответ, получаемый от проксируемого сервера. Установите этот параметр так, чтобы большинство ответов от бэкенда помещалось в буферы.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;h4&gt;2. Механизмы обработки соединений&lt;/h4&gt;&lt;br&gt;Есть одна тонкость, касающаяся механизма обработки соединений, а именно — способ получения информации о событиях на сокетах. Существуют следующие методы:&lt;br&gt;&lt;br&gt;&lt;ul&gt;&lt;li&gt;select — стандартный метод. На большой нагрузке сильно нагружает процессор.&lt;/li&gt;&lt;li&gt;poll — стандартный метод. Также сильно нагружает процессор.&lt;/li&gt;&lt;li&gt;kqueue — эффективный метод, используемый в операционных системах FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0 и Mac OS X. На 2-процессорных машинах под управлением Mac OS X использование kqueue может привести к kernel panic.&lt;/li&gt;&lt;li&gt;epoll — эффективный метод, используемый в Linux 2.6+. В некоторых старых дистрибутивах есть патчи для поддержки epoll ядром 2.4.&lt;/li&gt;&lt;li&gt;rtsig — real time signals, эффективный метод, используемый в Linux 2.2.19+. При больших количествах одновременных соединений (более 1024) с ним могут быть проблемы (их можно обойти, но на мой взгляд, лучше с этим не связываться).&lt;/li&gt;&lt;li&gt;/dev/poll — эффективный метод, используемый в Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+ и Tru64 UNIX 5.1A+.&lt;/li&gt;&lt;/ul&gt;&lt;br&gt;При компиляции nginx автоматически выбирается максимально эффективный найденый метод, однако скрипту configure можно насильно указать какой метод использовать. Если вы решили сделать это, то лучше использовать такие методы:&lt;br&gt;Linux 2.6: epoll;&lt;br&gt;FreeBSD: kqueue;&lt;br&gt;Solaris, HP/UX и другие: /dev/poll;&lt;br&gt;Linux 2.4 и 2.2: rtsig, не рекомендуется при больших нагрузках.&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;h4&gt;&lt;br&gt;Включение gzip позволяет сжимать ответ, отправляемый клиенту, что положительно сказывается на удовлетворенности пользователя, но требует больше времени CPU. Gzip включается директивой gzip (on|off). Кроме того, стоит обратить на следующие важные директивы модуля gzip:&lt;br&gt;&lt;br&gt;&lt;b&gt;gzip_comp_level&lt;/b&gt; 1..9 — устанавливает уровень сжатия. Опытным путем выявлено, что оптимальные значения лежат в промежутке от 3 до 5, большие значения дают маленький выигрыш, но создают существенно большую нагрузку на процессор, меньшие — дают слишком маленький коэффициент сжатия.&lt;br&gt;&lt;b&gt;gzip_min_length&lt;/b&gt; (по умолчанию, 0) — минимальный размер ответа, который будет сжиматься. Имеет смысл поставить этот параметр в 1024, чтобы слишком малеьнике файлы не сжимались (т.к. эффективность этого будет мала).&lt;br&gt;&lt;b&gt;gzip_types&lt;/b&gt; mime-тип [mime-тип ...] - разрешает сжатие ответа методом gzip для указанных MIME-типов в дополнение к "text/html". "text/html" сжимается всегда. Имеет смысл добавить такие mime-типы как text/css, text/javascript и подобные. Разумеется, сжимать gif, jpg и прочие уже компрессированые форматы не имеет смысла.&lt;br&gt;&lt;br&gt;Кроме того, существует модуль gzip_static, который позволяет раздавать уже сжатые статические файлы. В конфирурационном файле это выглядит так:&lt;br&gt;&lt;br&gt;&lt;pre class="brush: c"&gt;location /files/ {&lt;br&gt;  gzip on;&lt;br&gt;  gzip_min_length 1024;&lt;br&gt;  gzip_types text/css text/javascript;&lt;br&gt;  gzip_comp_level 5;&lt;br&gt;  gzip_static on;&lt;br&gt;}&lt;br&gt;&lt;/pre&gt;&lt;br&gt;При использовании такой конфигурации в случае запроса «/files/test.html» nginx будет проверять наличие «/files/test.html.gz», и, если этот файл существует и дата его последнего изменения больше, чем дата последнего изменения файла test.html, будет отдан уже сжатый файл, что сохранит ресурсы процессора, которые потребовались бы для сжатия оригинального файла.&lt;br&gt;&lt;br&gt;&lt;/h4&gt;&lt;h4&gt;Оптимизация приложений&lt;/h4&gt;&lt;br&gt;Существует очень полезный трюк, который позволяет указать разработчикам приложений, какие страницы нужно оптимизировать в первую очередь. Для этого потребуется в конфиге nginx указать новый формат лога:&lt;br&gt;&lt;br&gt;&lt;pre class="brush: c"&gt;log_format  my_combined  '$remote_addr - $remote_user [$time_local] '&lt;br&gt;    '"$request" $status $body_bytes_sent '&lt;br&gt;    '"$http_referer" "$http_user_agent" '&lt;br&gt;    '$upstream_response_time "$host"'&lt;br&gt;&lt;br&gt;access_log /var/log/nginx/access_log my_combined;&lt;br&gt;&lt;/pre&gt;&lt;br&gt;Переменная $upstream_response_time содержит время ответа бэкенда, поэтому в лог попадает время обработки каждого запроса бэкендом. Далее понадобятся два скрипта:&lt;br&gt;&lt;br&gt;1. /usr/local/bin/url_stats_report.sh:&lt;br&gt;&lt;br&gt;&lt;pre class="brush: bash"&gt;#!/bin/sh&lt;br&gt;&lt;br&gt;echo "=== Requests which took most of the time ===" &amp;gt; /tmp/report.txt&lt;br&gt;echo "overall time - number of requests - average time - url" &amp;gt;&amp;gt; /tmp/report.txt&lt;br&gt;&lt;br&gt;cat /var/log/nginx/*access.log | /usr/local/bin/url_stats.py &amp;gt;&amp;gt; /tmp/report.txt&lt;br&gt;cat /tmp/report.txt | mail -s "url performance report" root&lt;br&gt;&lt;/pre&gt;&lt;br&gt;2. /usr/local/bin/url_stats.py:&lt;br&gt;&lt;br&gt;&lt;pre class="brush: python"&gt;#!/usr/bin/env python&lt;br&gt;&lt;br&gt;import sys&lt;br&gt;&lt;br&gt;urls = {}&lt;br&gt;&lt;br&gt;try:&lt;br&gt;    while 1:&lt;br&gt;        line = raw_input()&lt;br&gt;        line_arr = line.split(" ")&lt;br&gt;        try:&lt;br&gt;            host = line_arr[-1]&lt;br&gt;            host = host[1:]&lt;br&gt;            host = host[:-1]&lt;br&gt;            url = line_arr[6]&lt;br&gt;            t = float(line_arr[-2])&lt;br&gt;            #print host, url, t&lt;br&gt;&lt;br&gt;            try:&lt;br&gt;                urls[host + url] = (urls[host + url][0] + t, urls[host + url][1] + 1)&lt;br&gt;            except KeyError, e:&lt;br&gt;                urls[host + url] = (t, 1)&lt;br&gt;        except ValueError, e:&lt;br&gt;            pass&lt;br&gt;&lt;br&gt;&lt;br&gt;except EOFError, e:&lt;br&gt;   pass&lt;br&gt;&lt;br&gt;def sort_by_value(d):&lt;br&gt;    """ Returns the keys of dictionary d sorted by their values """&lt;br&gt;    items=d.items()&lt;br&gt;    backitems=[ [v[1],v[0]] for v in items]&lt;br&gt;    backitems.sort(reverse=True)&lt;br&gt;    return [backitems[i][1] for i in range(0,len(backitems))]&lt;br&gt;&lt;br&gt;if (len(sys.argv) &amp;gt; 1):&lt;br&gt;    f = open(sys.argv[1], 'r')&lt;br&gt;    for k in f.readlines():&lt;br&gt;        k = k.strip()&lt;br&gt;        try:&lt;br&gt;             print urls[k][0], urls[k][1], urls[k][0] / urls[k][1], k&lt;br&gt;        except:&lt;br&gt;             print 0, 0, k&lt;br&gt;else:&lt;br&gt;    i = 0&lt;br&gt;    for k in sort_by_value(urls):&lt;br&gt;        print urls[k][0], urls[k][1], urls[k][0] / urls[k][1],  k&lt;br&gt;        i += 1&lt;br&gt;        if i &amp;gt; 100: break&lt;br&gt;&lt;/pre&gt;&lt;br&gt;Они не идеальны, но задачу выполняют: запуская /usr/local/bin/url_stats_report.sh (например, в postrotate утилиты logrotate), вы получаете наглядную картину, какие запросы занимают большую часть времени бэкенда.&lt;br&gt;&lt;br&gt;&lt;h3&gt;Кеширование&lt;/h3&gt;Незадолго до выхода этой статьи, Игорь Сысоев выпустил версию nginx 0.7.44 с экспериментальной поддержкой кеширования. Из-за большого количества багов, за которкое время было выпущено несколько версий, и в настоящее время последняя версия — 0.7.50 уже достаточно хорошо (хотя и не идеально) работает с кешированием.&lt;br&gt;Однако это все еще экспериментальная функция, но я решил рассказать о ней, т.к. ее одень давно ждали многое администраторы, да и лично для меня она очень полезна.&lt;br&gt;Сейчас nginx умеет кешировать на диске ответы от http и fastcgi запросов на бакенды, указывать ключ для кеширования, учитывать заголовки "X-Accel-Expires", "Expires" и "Cache-Control" и вручную устанавливать максимальное время жизни объекта в кеше.&lt;br&gt;Обслуживанием кеша (очиста старых файлов, наблюдение за размером и т.п.) занимается специальный процесс cache manager. Положительной особенностью реализации является то, что при старте nginx cache manager начинает проверку кеша в фоне, благодаря чему nginx не делает то что называется «дает сквида», т.е. он не висит несколько минут проверяя кеш перед стартом.&lt;br&gt;Я намеренно не указываю пример конфигурации, т.к. во-первых директивы могут еще поменяться, а во-вторых нужно глубокое понимание механизма кеширования, что требует вдумчивого чтения документации (http://sysoev.ru/nginx/docs/http/ngx_http_proxy_module.html#proxy_cache) и архивов рассылки nginx-ru.&lt;br&gt;&lt;br&gt;&lt;h3&gt;За кадром&lt;/h3&gt;&lt;br&gt;В статье освещена лишь та часть возможностей nginx, которыми я пользуюсь чаще всего. За пределами рассказа остались такие вопросы, как поддержка SSI, работа с memcached, экспериментальный встроенный Perl и сторонние модули, реализующие дополнительную функциональность.</description><category>nginx</category><category>python</category><category>syseng</category><guid>/ru/posts/2009/nginx-2/</guid><pubDate>Sat, 23 May 2009 21:20:00 GMT</pubDate></item></channel></rss>