Оцениваем возможности mod_python и mod_perl::Журнал СА 8.2007
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Наука и технологии
Подписка
Где купить
Авторам
Рекламодателям
Магазин
Архив номеров
Вакансии
Контакты
   

  Опросы

Какие курсы вы бы выбрали для себя?  

Очные
Онлайновые
Платные
Бесплатные
Я и так все знаю

 Читать далее...

1001 и 1 книга  
20.12.2019г.
Просмотров: 5101
Комментарии: 0
Dr.Web: всё под контролем

 Читать далее...

04.12.2019г.
Просмотров: 6343
Комментарии: 0
Особенности сертификаций по этичному хакингу

 Читать далее...

28.05.2019г.
Просмотров: 7599
Комментарии: 2
Анализ вредоносных программ

 Читать далее...

28.05.2019г.
Просмотров: 7922
Комментарии: 1
Микросервисы и контейнеры Docker

 Читать далее...

28.05.2019г.
Просмотров: 6978
Комментарии: 0
Django 2 в примерах

 Читать далее...

Друзья сайта  

Форум системных администраторов  

sysadmins.ru

 Оцениваем возможности mod_python и mod_perl

Архив номеров / 2007 / Выпуск №8 (57) / Оцениваем возможности mod_python и mod_perl

Рубрика: Программирование /  Веб-программирование

Алексей Мичурин

Оцениваем возможности mod_python и mod_perl

Модули сервера Apache mod_python и mod_perl позволяют встроить интерпретаторы языков Python и Perl непосредственно в сервер и значительно увеличить производительность веб-приложений. Perl – старый и хорошо зарекомендовавший себя игрок на этом рынке. Но в последние годы Python занял уверенные позиции в области веб-разработок и привлекает всё большее внимание.

Что это такое и о чём статья

Прежде всего я хотел бы оговориться. Во-первых, статья не является руководством по mod_perl или mod_python. Пожалуйста, не рассматривайте её с этой точки зрения. Статья является обзором возможностей этих средств, причём обзором с высоты птичьего полёта. Я буду всячески избегать углубления в детали (хотя полностью избежать деталей, конечно, не удастся). И во-вторых, я ни в коем случае не хочу разжигать религиозные войны поклонников тех или иных языков. Полагаю, что культурные люди используют во время еды и ложку, и вилку в зависимости от поданного блюда, а не спорят, какой из этих приборов лучше подходит для ковыряния в ухе.

Эта статья будет особо информативна для читателей, которые пользовались хотя бы одним из обсуждаемых средств или по крайней мере представляют, что такое mod_perl или mod_python, но я всё же скажу пару вводных слов об этих технологиях.

Веб-программирование служит одной цели: формированию HTML- или XML-документов и обмену ими по протоколу HTTP. Чаще всего речь идёт о формировании HTML-страниц, но не следует забывать о RSS, SOAP и других способах обмена данными по HTTP.

Чтобы сформировать динамический контент, сервер запускает некую программу, результат работы которой отдаётся посетителю веб-ресурса.

Эта программа может быть полноценной, самостоятельной программой, которая запускается по требованию сервера и «умеет» принимать данные от него и отдавать ему результат работы. Протокол этого обмена называется Common Gateway Interface, а такую программу называют cgi-приложением.

Такой подход очень прост в реализации, но крайне неэффективен (по сравнению с другими), так как при каждом запросе выполняется множество действий: загрузка программы в память, её интерпретация и так далее.

Полной противоположностью этого подхода (есть и промежуточные) является встраивание языка программирования непосредственно в сервер в виде модуля сервера. Тогда программа загружается и интерпретируется один раз при запуске сервера. На этом же этапе можно произвести подключение к базе данных (чтобы не выполнять подключения/отключения при каждом запросе), открыть необходимые файлы и выполнить другие предварительные действия. Таким образом, теперь запрос обрабатывается внутри сервера, что позволяет получить выигрыш в производительности в десятки и сотни раз.

Модули mod_python и mod_perl, как вы уже, наверное, догадались, и являются модулями сервера Apache для встраивания в сервер интерпретаторов Python и Perl соответственно.

Стабильность

Проект mod_perl стартовал в 1996 году, mod_python стартовал годом позже, и вначале разрабатывался как универсальное средство для встраивания интерпретатора Python в любой сервер. В 2000 году mod_python приобрёл современные очертания и полностью ориентировался на сервер Apache.

В настоящее время mod_perl получил большее распространение. Однако mod_python также официально признан разработчиками Apache (Apache Software Foundation) и является не менее зрелым средством разработки.

Производительность

Как таковые mod_perl и mod_python обеспечивают очень высокую скорость запуска кода. Поэтому производительность того или иного решения зависит от производительности самих языков и таланта программистов.

Для оценки производительности самих языков можно провести тест с выдачей «статического» документа. То есть документа, для формирования которого не используются никакие внешние источники данных (базы данных, файлы, параметры конфигурации сервера). По моим тестам выдача такого документа средствами mod_perl и mod_python требует одинакового времени, которое вдвое больше времени, необходимого для выдачи простого статического html-файла.

В «боевых условиях» производительность сервера начинает практически полностью лимитироваться производительностью базы данных или других средств, к которым сервер обращается. Поэтому выбор mod_perl или mod_python не так уж важен в погоне за производительностью.

Документация

Python и Perl имеют совершенно разную идеологию, что не могло не отразиться на характере документации. Документация на Perl более похожа на рассказы, её можно читать как книгу. Документация на Python – это классическая техническая документация, пунктуальная и сухая, увлекательной её никак не назовёшь.

Документация на mod_python выдержана в лучших традициях Python – исчерпывающая, последовательная, небогатая примерами и трюками.

Документацию на mod_perl можно смело разбить на две части. Первая – это документация на первую версию mod_perl (предназначенную для Apache 1.3). Она выдержана в традициях Perl, из неё можно узнать много о самом языке, в ней содержится много примеров, советов, комментариев, попадаются и витиеватые и остроумные фразы, способные вызывать улыбку. Вторая часть – документация на второй mod_perl (Apache 2.x). Она производит впечатление совершенно сырой, читать её, не прочитав первую часть, бесполезно.

Для администратора

Для администратора серверов на первом месте стоят вопросы безопасности и совместимости версий. Процесс установки mod_perl и mod_python одинаково прост и стандартен – configure/make/make install.

Пакеты с ПО в Perl и Python имеют стандартный способ установки (оба языка имеют средства управления пакетами). Perl давно имеет CPAN, Python недавно обзавёлся архивом PyPI и средством EasyInstall.

Безопасность

Подходы к обеспечению безопасности в mod_perl и mod_python различны.

Следует отметить, что весь код mod_perl исполняется одним и тем же интерпретатором. Поэтому mod_perl-модули, отвечающие за абсолютно разные ресурсы (даже если они обслуживают разные виртуальные хосты), в пределах одного сервера имеют полный доступ к данным друг друга. Вопросы их бесконфликтной работы остаются на совести программиста, а результаты конфликтов могут быть самыми неожиданными (учитывая, что в Perl существует ряд глобальных переменных, таких как $SIG). Устанавливая новые mod_perl-компоненты, следует иметь это в виду и всегда проводить тщательное предварительное тестирование.

Во второй версии mod_perl появилась возможность разделения интерпретаторов, но использовать её проблематично. Она основана на использовании нитей, но многие Perl-модули не рекомендуется использовать в этом режиме. К числу таких модулей относится, например, DBI, обеспечивающий доступ к базам данных.

Путь mod_python другой, он состоит в том, что при запуске сервера запускается main_interpreter-интерпретатор, который порождает новые интерпретаторы по мере поступления запросов (единожды рождённый интерпретатор функционирует до завершения работы сервера и обслуживает все запросы, адресованные именно этому интерпретатору).

Этот процесс полностью управляем. Можно его запретить и работать в режиме mod_perl, можно порождать отдельный интерпретатор на каждый виртуальный хост (поведение по умолчанию), можно добиваться самых экзотических эффектов: на разные директории разные интерпретаторы, на разные расширения разные интерпретаторы и так далее. Поэтому разные mod_python-модули можно устанавливать без всяких опасений, а если обнаружатся конфликты, то просто разделить интерпретаторы. Этот великолепный механизм встроен в архитектуру mod_python изначально.

Стабильность работы и требования к ресурсам

В настоящее время не известны нестабильности ни в mod_python, ни в mod_perl. Хотя следует отметить, что в mod_perl удалось полностью избавиться от утечек памяти только начиная со второй версии.

Что касается общей «прожорливости», она во многом зависит от программистов. Языки Perl и Python, обладая равноширокими возможностями, оба дают программистам возможность создавать циклические ссылки и вызывать утечки памяти. Стоит, однако, отметить, что сборщик мусора в Python выгодно отличается от аналогичного механизма в Perl.

Версии

Оба средства поддерживают и Apache 1.3 и Apache 2.x. Версии mod_perl так и нумеруются: первая (уже очень давно не развивается) – для первого Apache, вторая – для второго. В mod_python дела обстоят так же: после версии 2.7 был осуществлён переход на Apache 2.0.

Для программиста

Конечно, для программиста лучше тот язык, который он лучше знает, но возможно вы полиглот?.. или стремитесь таковым стать, что безусловно откроет перед вами новые горизонты.

Если вы знаете и Perl, и Python

По функциональности mod_perl и mod_python одинаковы. Даже структура классов и методов очень сходна (неудивительно, и тот и другой просто предоставляет интерфейс к одному и тому же серверу). Если вы знаете одно средство, то освоение другого займёт у вас пару дней.

Однако, при всём сходстве, mod_python более «дружелюбен». Он предоставляет некоторое количество удобных, достаточно высокоуровневых инструментов, что безусловно облегчает работу.

Модуль имеет добротную документацию. Компактную, но исчерпывающую. Кроме того, mod_python предоставляет набор средств, которые я вынес в раздел дополнительных функций. Здесь только добавлю, что mod_python имеет очень удобный и гибкий встроенный механизм поддержки сессий.

Различия языков

Если вы не знаете Perl и/или Python, то уверяю вас, они оба заслуживают изучения.

Оба языка поддерживают и процедурный и объектно-ориентированный стиль программирования (в Python имеются элементы и функционального программирования). Причём философия объектов в языках очень сходна (например, все методы виртуальны).

Python-программы обладают структурой, более близкой к C++. Так что, если вы планируете быстро сделать пилотный проект на скриптовом языке, а потом аккуратно переписать его на C++, то Python – оптимальный выбор.

Кроме того, Python поддерживает развитый механизм исключений, чего очень не хватает в Perl.

Однако в Python и не пахнет Perl-скороговоркой. Работа с регулярными выражениями в Python такая же громоздкая и неповоротливая, как в PHP и других языках (хотя такой подход позволяет получить существенный выигрыш в производительности, как было упомянуто выше). В Python нет даже операторов инкремента и декремента («++» и «--»).

Многих отталкивает в Python то, что блоки выделяются отступами (то есть выравнивания важны!), но это как раз не самое большое зло Python. Во-первых, это правило не такое строгое, как кажется на первый взгляд и из него есть много исключений, а во-вторых, это позволяет забыть про фигурные скобки и точки с запятой.

На мой взгляд, гораздо большим недостатком Python является отсутствие некоторых кратких операторов: упомянутых инкремента и декремента, тернарного оператора (cond?exp1:exp2), появившегося только в версии 2.5.

Но это компенсируется возможностью переопределить любой оператор и гибко настроить механизм преобразования типов, создавать итераторы, классы-помощники, использовать декораторы. Кроме того, в Python есть изюминки, которых нет в Perl. Например, множественные сравнения (1 < a < 2). Есть и своя скороговорка. Например, Perl-конструкция:

if (grep {$_ eq 'z'} @a) {

в Python реализуется на много элегантней:

if 'z' in a:

Библиотеки

Всем известен девиз Perl: «всегда есть не одно решение» («There is more than one way to do it»). Видимо этим (и давней историей) вызвано большое разнообразие библиотек, обладающих разными возможностями. В Python принцип противоположен: «Должен быть один, а лучше только один, очевидный путь решения» («There should be oneand preferably only oneobvious way to do it»). Поэтому функциональность библиотек не пересекается, но по возможностям Python не уступает Perl.

Для обработки запросов в Python имеется библиотека cgi (в mod_python её функциональность продулибрована в более производительной библиотеке util). Она изящно обрабатывает и POST-, и GET-запросы, принимает файлы, одним словом, делает всё необходимое.

Для работы с почтой есть прекрасные пакеты email, smtplib, mimetools, multifile, rfc822 (поддерживается не только RFC 822, но и более «продвинутый» формат RFC 2822).

Оба языка имеют собственные API для работы с базами данных: DBI в Perl и набор модулей в Python (например, MySQLdb), имеющих единый интерфейс DBAPI 2.0. В среде «гуру» есть мнение, что Python работает с базами данных быстрее, чем Perl, но мне не удалось выявить различия; видимо, и там, и там скорость лимитируется производительностью самой базы данных. Следует отметить, что в Python тип параметров, подставляемых в SQL-запрос, определяется жёстко, что позволяет избежать многих недоразумений. (В Perl/DBI тип также можно задать принудительно, но это намного более громоздко.)

Отладка и диагностика

Для отладки веб-приложений наиболее ценны следующие возможности:

  • Возможность получения дампов данных, имеющих сложную структуру. Для этого в Perl и в Python есть стандартные модули Data::Dumper и pprint, соответственно (кроме того, в Python «правильно» спроектированные объекты сами «знают», как вывести себя в виде строки). Следует отметить, что в Python есть ещё и возможность интроспекции, позволяющая получать списки атрибутов объекта, зависимости классов и прочую информацию о структуре программы, библиотек, модулей. Этот механизм тесно интегрирован с механизмом исключений, что придаёт всему конгломерату дополнительную мощь. Perl поддерживает некое подобие этой функциональности в классе UNIVERSAL.
  • Возможность получения стека вызовов. И в Perl и в Python для этого имеются инструменты, кроме того, Python выдаёт эту информацию по умолчанию, а mod_python отображает эти сообщения непосредственно в окне браузера, при включённой диагностике (включается в настройках сервера). Имеются и более «продвинутые» средства визуализации информации об ошибках (cgitb), а также развитые средства протоколирования (модуль logging поддерживает уровни значимости сообщений, способен направлять сообщения не только в файл, но и по электронной почте и другим протоколам, способен сам осуществлять ротацию файлов-протоколов и многое другое).
  • Возможность отслеживания и анализа исключений. В Python встроен развитый механизм обработки исключений, в Perl практически то же самое можно получить при умелом использовании eval и die с аргументом-объектом, но это довольно искусственный приём, который всё равно не позволяет достичь возможностей Python.

Кроме того, и mod_perl и mod_python имеют модули для анализа настроек сервера и интерпретатора. Необходимо просто подключить в качестве обработчика запросов готовый модуль, и при запросе вы будет получать веб-страницу с полным описанием настроек сервера, интерпретатора и загруженных модулей.

Здесь mod_python тоже приготовил приятные сюрпризы. Например, вы сразу же (тут же, в окне браузера) можете посмотреть error_log, причём именно того виртуального хоста, на котором работаете.

Дополнительные функции

И mod_perl, и mod_python может предоставлять программисту более высокоуровневые интерфейсы, позволяя ему не задумываться о внутреннем устройстве сервера Apache.

В mod_perl это два модуля Apache::Registry и Apache::PerlRun. Они позволяют «легко» запускать обычные CGI-скрипты под управлением mod_perl. Таким образом, вы можете с минимальными усилиями увеличить производительность уже существующей системы в десятки раз. (Строго говоря, не всякий cgi-скрипт можно запустить таким образом без модификации.)

Разработчики mod_python реализовали такую же возможность в модуле mod_python.cgihandler, но на этом они не остановились, добавив ещё две возможности.

Модуль mod_python.publisher автоматически вызывает функцию-обработчик с тем же именем, что запрошено в URL, и берёт на себя часть работы по обработке запроса и выдаче ответа, что уменьшает нагрузку на разработчика.

Модуль mod_python.psp позволяет вести разработку в PHP-стиле, встраивая Python-код в тело страниц.

К сожалению, за удобство приходится платить производительностью. В документации на mod_python приведены следующие цифры:

Standard CGI:               23 requests/s

Mod_python cgihandler:     385 requests/s

Mod_python publisher:      476 requests/s

Mod_python handler:       1203 requests/s

Надо сказать, что подобная PHP-образная функциональность доступна и при использовании mod_perl в сочетании с дополнительными средствами типа HTML::Mason, HTML::Embperl и другими, однако они не так глубоко интегрированы в mod_perl и дают более существенное снижение производительности.

Перспективы

Как видите, возможности mod_python и mod_perl весьма схожи. Ситуация классическая: более старое средство – mod_perl – привлекает к себе тем, что уже хорошо себя зарекомендовало, более новое средство – mod_python – развивалось с учётом ошибок предшественников и обнаруживает большую гибкость и защищённость. Последние годы стало трудно сказать, какой из подходов лучше, что осложнило выбор. Остроту моменту добавляет тот факт, что оба языка сейчас стоят на распутье.

Грядёт выход следующих версий – Perl6 и Python3000. Обе обещают кардинальную переработку языка и не стремятся поддерживать совместимость с предыдущими версиями. Однако планы разработчиков Python не так амбициозны, речь идёт об исправлениях промахов в дизайне. Perl6 – это, скорее, новый язык.

Разработка обоих языков стартовала в 2000 году, хотя это достаточно условная дата. Правильнее было бы сказать, что где-то на изломе века у разработчиков начали появляться и выкристаллизовываться мысли и планы по полной переработке своих творений.

Релиз Python3000 планируется на август этого года, сейчас идут последние приготовления.

Работы над Perl6 пока продолжаются. Есть действующая модель виртуальной машины Parrot. Есть компилятор в код виртуальной машины, написанный на языке Haskell. Разработка компилятора ведётся только с 2005 года, причём синтаксис будущего языка Perl6 изменяется и додумывается в процессе этой разработки. Пока конца этому процессу не видно.

Поэтому, с одной стороны, большинство разработчиков по традиции используют Perl. С другой стороны, наиболее передовые организации, такие как Google, NASA, IBM, начинают отдавать всё большее предпочтение Python.

Кроме того, Python начинает привлекать всё больше молодых людей и приобретать популярность и универсальность Java. Позволю себе небольшое отступление от темы веб-разработок: ещё в 2004 году Nokia начала поддержку Python-приложений на платформе Symbian Series 60, а в начале прошлого года эти разработки стали доступны OpenSource-сообществу (лицензии «Apache Version 2» и «Python License»). В дистрибутив входят модули для управления графикой, звуком, камерой, приёма/отправки/парсинга сообщений и многие другие. Всё это снабжено превосходной документацией. Таким образом, Python уже сейчас даёт возможность создавать полноценные приложения для мобильных устройств. Всё это подогревает интерес к Python и способствует его распространению и популяризации.

Возвращаясь к веб-разработкам, нельзя не сказать об очень молодой, но многообещающей технологии WSGI (Web Server Gateway Interface), которая уже несколько лет вызревает в сообществе Python-разработчиков. Это попытка стандартизации протокола взаимодействия сервера и приложения. Разработчик, использующий WSGI, не только получает в своё распоряжение множество удобных и мощных инструментов, но и полностью избавляется от вопросов интеграции приложения с сервером. Используется ли mod-подход, или CGI, или FastCGI, или SCGI (Simple Common Gateway Interface). Естественно, упрощается и жизнь администратора, ему больше не надо адаптировать веб-сервер под конкретное приложение, использующее некий протокол.

Скорее всего, пока Perl6 формируется, Python3000 стабилизируется, а Python-сообщество предложит ряд очень привлекательных решений для веб-разработчиков, что заметно потеснит Perl на этом поле. У Python есть на то все шансы.

Приложение

Замечания о производительности

Хотя разработчики Perl делают очень много для повышения производительности, но приходится признать, что во многих аспектах Python обходит Perl.

В Python есть множество механизмов, дающих преимущество, начиная с предварительной компиляции модулей в байт-код и заканчивая последовательным проведением идеи copy-on-write. Когда вы пишете на Python a=b, значение не копируется, даже если это скаляр.

Приведу один выразительный пример. Большинство веб-приложений интенсивно работают со строками. Python поддерживает регулярные выражения в той же мере, что Perl. Но по результатам тестов Фридла (Дж. Фридл «Регулярные выражения»; имеется русский перевод первого и второго издания книги) обнаруживает десятикратное(!) превосходство в производительности. Это происходит потому, что Python не использует переменных типа $1 и начисто лишён $&-чумы.

На заметку Perl-программисту: если хотя бы один модуль вашей программы использует переменные типа $&, то все регулярные выражения во всех модулях значительно замедляются.

Например, элементарное выражение m/a/ начинает работать в десятки раз медленнее (точная цифра зависит от длины строки). Надо заметить, что переменная $& используется во многих модулях самого общего назначения и даже в прагме diagnostics.

О циклических ссылках и сборке мусора

Циклическая ссылка возникает, когда объект ссылается сам на себя или входит в цепь взаимноссылающихся объектов. (Простейший пример на Perl «$x=\$x».) В этом случае, даже если переменная, содержащая ссылку на объект, уничтожена, и объект стал недоступен, счётчик ссылок остаётся не равным нулю и сборщик мусора не может освободить память, что приводит к утечкам.

В Perl любая конструкция, ссылающаяся на себя, будь то замыкание и сложная структура данных, вызывает подобные утечки и программист обязан сам разрывать циклы ссылок в деструкторах. Любая неосторожность влечёт неприятности.

В Python (начиная с версии 2.0) имеется асинхронный сборщик мусора, распознающий циклические ссылки и корректно уничтожающий недоступные переменные. Поэтому создать утечку памяти в Python очень не просто. Кроме того, в Python имеются штатные средства взаимодействия со сборщиком мусора (модуль gc), которые позволяют легко обнаруживать и уничтожать циклические ссылки.

Философия mod_perl и mod_python

Мне всегда представлялась странной философия mod_perl, где функция-хендлер (обработчик запроса) вызывается как метод класса. По своему устройству и способу вызова она является конструктором, но от этого «конструктора» не требуется ничего конструировать(!), он должен обработать запрос (параметры запроса передаются в объекте-аргументе) сгенерировать ответ и вернуть статус (фактически число, а не объект). На мой взгляд, такое устройство не логично и идёт в разрез с ОО-моделью программирования. Конечно, при желании можно извлечь некоторую пользу из возможности наследовать обработчик из единого базового класса, но это не устраняет противоречие, а лишь запутывает код, который имея ОО-вид должен выполнять процедурные задачи, причём теперь где-то в глубине, в базовом классе.

Такой подход был бы оправдан, если бы хендлер возвращал бы не статус, а объект с заданным интерфейсом. Скажем, тело ответа доступно как $replay->body, статус как $replay->status и так далее.

Другой вариант: метод-хендлер вызывается, как инициализатор объекта запроса. То есть класс хендлера наследуется от класса запроса и переопределяет некоторые виртуальные методы. Это было бы очень удобное и красивое использование преимуществ ОО-подхода. Кроме того, хендлеру не пришлось бы явно передавать аргументы, объект запроса был бы просто доступен как self.

Что ж, возможно разработчики mod_perl планировали реализовать что-то подобное, но почему-то этого не сделали.

В mod_python обработчик (хендлер) – это просто функция (внутри которой, конечно, можно сконструировать объект и работать с этим объектом). Она тоже принимает объект запроса (по своему устройству, брата-близнеца объекта из mod_perl), тоже генерирует ответ и тоже возвращает статус. Мне это представляется более логичным. Но другого решения в mod_python и не могло быть, в Python конструктор может вернуть только объект. Никаких undef или статусов конструктор вернуть не может (что логично). Ошибки же отслеживаются механизмом исключений. Это Python-way.

  1. Официальный сайт проекта mod_python – http://www.modpython.org.
  2. Официальный сайт проекта mod_perl – http://perl.apache.org.
  3. Официальный сайт Python – http://www.python.org.
  4. Официальный сайт Perl – http://www.perl.org.
  5. Архив модулей Python PyPI – http://www.python.org/pypi.
  6. Архив модулей Perl CPAN – http://cpan.org.
  7. Страница с mod_python на сервере разработчиков Apache – http://httpd.apache.org/modules.
  8. Официальный сайт проекта Jython – http://www.jython.org.
  9. Python для Nokia S60 – http://opensource.nokia.com/projects/pythonfors60.
  10. План развития WSGI (PEP-333) – http://www.python.org/dev/peps/pep-0333.

Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru