Трансформация и перевод на другие языки web-сайтов на лету при помощи Nginx





    В моем первом посте я описал применение Apache Traffic Server в качестве кеширующего reverse-proxy. В отзывах меня спрашивали почему не nginx? Поскольку в ATS все равно не нашлось удобного способа трансформировать контент сайта, то я решил изучить возможности Nginx. Для решения задачи пришлось углубится в дебри документации, и вот что получилось…

    Как и в прошлый раз мы будем трансформировать сайт example.com в example.ru. Не буду рассказывать о настройке и установке Nginx (об этом много статей), а лучше расскажу о конкретных полезных настройках.

    Глобальная конфигурация — nginx.conf
    worker_processes  4;
    
    # Поставьте тут столько процессов, сколько у в системе ядер
    # cat /proc/cpuinfo | grep processor | wc  - даст ответ
    
    http {
        resolver   127.0.0.1;
    
    # Это очень важная строчка. Без нее никак не удавалось запустить работу reverse proxy. Вместо 127.0.0.1 напишите адрес DNS сервера вашего провайдера.  Беда в том, что эта настройка ставится глобально, а все примеры по настройке в мануалах и статьях приводятся для локейшинов. Ошибку отлавливаем по слову resolver в логах.  Настройте DNS сразу и потратьте спасенное время на полезные дела!
        include    options.conf;
        include    mime.types;
    
    # Мы переводим сайт на английском языке, у которого, по-умолчанию, кодировка iso-8859-1. Писать будем по-русски и, чтобы не страдать перекодировками, выберем UTF-8.
        charset utf-8;
        override_charset on;
        source_charset iso-8859-1;
        charset_map  iso-8859-1 utf-8 { }
    
    # Команда  charset_map  - волшебная, без нее ничего не заработает! Предложенный в документации вариант charset_map  iso-8859-1  _ { }  у меня не заработал.
    
    # Настраиваем проксирование всех запросов. Зачем?  А чтоб было в хозяйстве!
        proxy_cache_path /usr/local/nginx/proxy_temp/ levels=1:2 keys_zone=cache-zone:10m inactive=10m max_size=1000M;
    
    # Настраиваем сохранение сайта
        proxy_store_access    user:rw  group:rw  all:r;
    
    # Разница между proxy_caсhe и proxy_store в том что первое создает оптимизированный служебный кэш, а второе создает точную локальную копию удаленного сайта.  Такая копия станет незаменимой вещью в процессе дальнейшего ручного перевода сайта.
    
    # Опускаем остальные конфигурационные команды и …
        include     example.conf;
    }
    

    Настройка виртуального сервера — example.conf
    server {
        listen          80;
        server_name     example.ru;
        access_log  logs/example.ru.access.log main;
        error_log   logs/example.ru.error.log;
        index       index.html;
        root        /usr/local/nginx/html/example.ru;
    
    # Ничего необычного за исключением:
        rewrite ^/(/broken_page.*) http://www.example.com/$1 permanent;
        
    # Это полезное правило позволяет “починить” линки, которые были сломаны проксификатором Nginx работающим в режиме reverse-proxy.  Мы теряем возможность перевода сломанных страниц, так как отсылаем пользователя на оригинальный сайт,  но зато освобождаемся от ошибки 404. Так лучше.  В моих тестах Nginx ломал  Pop-up окно с роликом на флеше.
    
    # Переходим к самому интересному!
    
    # Создаем локейшин /img/  Название локейшина должно совпадать с URI хранилища картинок оригинального сайта. Пояснения ниже.
        location ^~ /img/  {
    
    # Где же локальное хранилище? Ах вот где, прямо у нас под боком!
            root         /usr/local/nginx/html/example.ru;
    
    # Пишем волшебство!
            try_files $uri $uri/ @static;
        }
    # Смысл этой замечательно команды в том, что при запросе пользователя она проверяет есть ли в локальной копии сайта файл с указанными именем и если есть, то отдает файл, а если нет, то передает работу в @static, в котором живет прокси сервер. Таким элегантным способом мы можем накапливать картинки (страницы) с оригинального сайта, и, если нужно, по одной штуке переводить их на русский язык в редакторе. Весь процесс не создает ошибок, абсолютно прозрачен для пользователя и максимально удобен для переводчика-дизайнера.
    
    # Доделаем нашу статику:
        location @static {
            proxy_pass http://super-cdn.com$uri;
    # Стоит заметить что в годно сделанном сайте, есть 1-2 отдельных домена хранящие статические файлы. В нашем случае полный путь к ним оказался таким: http://super-cdn.com/img
    
            proxy_store /usr/local/nginx/html/${uri};
            expires max;
    # expires max -   Это чрезвычайно важная команда, которая превращает обычный кэш в постоянно хранилище файлов. Однажды попав картинка остается в хранилище пока вы не удалите ее руками или не перезапишите ее русифицированной версией.  Как только картинка перезаписывается, она мгновенно выдается всем новым запросам.  Все кэши ( на сервере, у юзера)  во всех местах обновляются как надо если перезапустить nginx -s reload. За этой завораживающей процедурой можно наблюдать прямо в консоли firebug после рефреша страницы в браузере.  Это действительно уникальная  возможность Nginx.
    
    # Отдельный лог не повредит при отладке!
            access_log   logs/2.access.log;
            error_log   logs/2.error.log debug;
        }
    
    # Теперь опишем корневую директорию нашего сервера.
        location / {
            include    example-transform.conf;
    
    # Включаем реверс прокси
            proxy_pass            http://www.example.com;
    
    # Всякоразные настройки
            proxy_redirect        off;
            proxy_cache           cache-zone;    # Не забыли про описание пула в самом начале?
            proxy_cache_min_uses  2;
            proxy_cache_valid  200 1h;
    
    # Если хотим чтобы владелец оригинального ресурса мог видеть наши посещения добавляем:
            proxy_set_header      Host            $host;
            proxy_set_header      X-Real-IP       $remote_addr;
            proxy_set_header      X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
    


    Мы помним, что картинки оригинального сайта находятся по адресу:
    http://super-cdn.com/img


    Однако запустив вышеописанную конфигурацию мы видим, что Nginx оставил все ссылки вида
    =http://super-cdn.com/img/*
    не проксифицированными. Понятно, что это из-за отличия домена. Так же понятно, что в такой ситуации никакой возможности подсунуть переведенные картинки у нас нет, потому как пользователь забирает их напрямую с CDN минуя нас. Значит требуется магия трансформации!

    Открываем example-transform.conf
    sub_filter 'http://super-cdn.com/img/’  'http://example.ru/img/' ;
    sub_filter_once off;
    


    По умолчанию модуль трансформации не откомпилирован! Нужен параметр:
     ./configure --with-http_sub_module
    


    Компилируем, перзапускаем… Бинго!!! Фанфары!!!
    Все работает, все летает, картинки можно подменять на ходу.

    Мы радостно добавляем первое правило замены содержимого страницы и … узнаем шокирующую информацию о том, что модуль http_sub_module позволяет выполнить всего одну замену!

    О Игорь Сысоев, почему ты скрыл этот чудовищный факт в страничке документации!
    sysoev.ru/nginx/docs/http/ngx_http_sub_module.html

    Ах если бы мы знали про такой финал в самом начале! Но… Стоп! Эмоции в сторону, потому что русские и китайцы — братья навек! Простой китайский паренек Weibin Yao уже решил нашу проблему и создал модуль substitutions4nginx, который доступен по адресу
    code.google.com/p/substitutions4nginx

    Устанавливаем модуль по инструкции, открываем наш конфигурацию трансформации и смело пишем:

    subs_filter 'http://super-cdn.com/img/’  'http://example.ru/img/' g;
    subs_filter '<title[^>]*>(.*?)</title>' '<title>НЛО прилетело сюда и подчинило этот сайт своей воле. Аминь!</title>' oir;
    

    Вместо послесловия
    1. Конфиг писался из рабочего, но по понятным причинам не проверялся вживую. Если возникнут сложности постараюсь помочь.

    2. Nginx работает в нестандартном малоисследованном режиме. Появляется вот такой баг.

    3. Модуль substitutions4nginx не обкатан, один баг нашелся сразу и был быстро исправлен. Видимо нужна толпа тестеров.

    4. В Apache Traffic Server и в Nginx я нашел одинаковый баг. При котором теги с линками типа a href содержащие перенос строки не прокисифцируются. Подозреваю что проблема тянется из библиотеки PCRE.

    З.Ы. Кто подскажет почему во втором блоке не работает подсветка source lang=«bash», тому плюшку!

    З.З.Ы. Проблему с подсветкой определил yeah, парсер ломается на коде:
    ://
    Оставлю блок без подсветки, чтобы не вносить лишних ошибок в конфиг.

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 9

      0
      З.Ы. Кто подскажет почему во втором блоке не работает подсветка source lang=«bash», тому плюшку!

      Подсвечивает, просто подсветка скудная.
        0
        UPD. Действительно, подсвечивается только первый блок.
        0
        Браво. Отличное чтиво и прекрасный метод. Попробую его завтра в черном деле. Еще раз браво. долго ломал голову как же реализовать то подмену. Век живи, век учись, однако!
          +2
          З.Ы. Кто подскажет почему во втором блоке не работает подсветка source lang=«bash», тому плюшку!

          2 причины:
          1. Директива rewrite
          2. Директива proxy_pass (2 раза)

          Если убрать обе, то подсветка работает. Причем комментирование не помогает. Скорее всего это особенность парсера bash.
            +4
            Все, причина найдена — "http://". Этот фрагмент есть во всех 3-х кусках кода, которые влияют на отсутствие подсветки.
          • UFO just landed and posted this here
              0
              А ещё можно использовать ngx_http_perl_module, открывающий безграничные возможности.
                0
                Надо использовать средства, требующие меньше усилий при настройке.
                0
                4. В Apache Traffic Server и в Nginx я нашел одинаковый баг. При котором теги с линками типа a href содержащие перенос строки не прокисифцируются. Подозреваю что проблема тянется из библиотеки PCRE.


                Это не баг, это из-за разбиения контента на строчки, о котором говорит наличие заголовка

                Transfer-Encoding: chunked
                


                Т.е. контент идет не сплошным потоком, а разбитый на строчки, причем перед каждой строчкой шлется её размер.

                Only users with full accounts can post comments. Log in, please.