среда, 15 июня 2011 г.

Деб-пакеты с модулями SQLite

Собрал некоторые расширения как деб-пакеты, см. репозиторий http://mobigroup.ru/debian/pool-squeeze/main/s/

В апстримовском расширении FTS3 поправлен баг, мешающий собирать его отдельным модулем. Версия из текущего trunk, умеет conflict-resolution для виртуальных таблиц (можно писать insert or replace ...). Также добавлена поддержка стеммеров Snowball.

Модуль ICU собран "как есть" апстримовский.

Добавлен модуль для подсчета хэша sha1 (используется быстрая реализация из BSD-систем).

Для перекомпиляции, в т.ч. под другие ОС, см. в архивах файлик debian/rules, где содержатся команды сборки.

Для удобства загрузку нужных расширений можно сделать с помощью файла ресурсов:
$ cat ~/.sqliterc 
.load /usr/lib/libsqlitetcl.so
.load /usr/lib/libsqliteicu.so
.load /usr/lib/libsqlitefts3.so
.load /usr/lib/libsqlitesha1.so

$ sqlite3
-- Loading resources from /home/veter/.sqliterc

суббота, 11 июня 2011 г.

Оптимизация взаимодействия веб-клиента и сервера - Практические задачи

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


Хотелось бы немного остановиться на вопросе запуска сервисов. Способов запуска сервисов в линукс немало - скрипты http://ru.wikipedia.org/wiki/Init:


init (сокращение от англ. initialization — инициализация) — программа в UNIX и Unix-подобных системах, которая запускает все остальные процессы.

утилита nohup:


nohup — UNIX-утилита, запускающая указанную команду с игнорированием сигналов потери связи (hangup); таким образом, команда будет продолжать выполняться в фоновом режиме и после того, как пользователь выйдет из системы.

и даже запуск в сеансе screen.


Для использования init нужны рутовые права или специальная настройка системы для запуска с правами пользователя посредством команды sudo. Два других способа не требуют ни настройки системы, ни каких-либо дополнительных прав, но после перезагрузки сервера придется запускать все вручную. Все названные методы имеют большой недостаток - если сервис по какой-либо причине завершился, перезапустить его потребуется вручную. Недостаток - это еще мягко сказано, скорее, это огромная концептуальная дыра (обычно администраторы пишут скрипты для проверки работающих сервисов и, при необходимости, их перезапуска; такие скрипты добавляются в cron). Если есть возможность, рекомендую установить суперсервер runit, который обеспечивает возможность с правами пользователя создавать сервисы и управлять ими, а кроме того, сам перезапустит сервис при необходимости. Подробнее смотрите или в англоязычной документации или в русскоязычной статье Использование runit для своих сервисов.


Файловый хостинг


Все необходимое для создания файлового хостинга рассмотрено в разделе Статический контент. Здесь лишь приведу пример конфигурационного файла для запуска с помощью супервизора runit:


run

#!/bin/sh

export HOST=127.0.0.1
export PORT=6100
export ROOT=~/wwwserver

cd $ROOT
exec tcpserver -c 5 -R -H $HOST $PORT env -i LANG="ru_RU.UTF-8" \
/usr/sbin/fnord-idx . 2>/dev/null 1>/dev/null

Также можно средствами runit включить логирование запросов, но об этом см. в документации. Если листинг директорий не нужен, можно заменить /usr/sbin/fnord-idx на /usr/sbin/fnord. Для поддержки CGI и листинга директорий используйте /usr/sbin/fnord-cgi-idx, а только для CGI - /usr/sbin/fnord-cgi.


Система контроля версий


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


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


Fossil представляет собой простую в использовании распределенную систему управления исходными кодами (DSCM) с поддержкой доступа и администрирования через HTTP CGI или встроенный HTTP сервер, имеет встроенную вики, встроенный браузер файлов, встроенную тикет-систему (tickets system) и другие возможности.

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


Скачать бинарную сборку для вашего дистрибутива можно по адресу http://www.fossil-scm.org/download.html В debian/ubuntu дистрибутивах пакет fossil также есть в официальном репозитории. Основы работы описаны на сайте проекта, а также доступна для скачивания книга Fossil User manual. Далее мы ограничимся лишь парой примеров для облегчения начала самостоятельной работы.


Получить список всех команд:

$ fossil help
Usage: fossil help COMMAND.
Available COMMANDs:
add            close          http           redo           sync         
all            co             info           remote-url     tag          
annotate       commit         leaves         rename         timeline     
artifact       configuration  ls             revert         ui           
branch         del            merge          rm             undo         
cgi            descendants    mv             scrub          unset        
changes        diff           new            search         update       
checkout       extras         open           server         user         
ci             finfo          pull           settings       version      
clean          gdiff          push           sha1sum        wiki         
clone          help           rebuild        status         zip          
This is fossil version [15cb835736] 2010-06-17 18:39:10 UTC

Получить описание отдельной команды:

$ fossil help add
Usage: fossil add FILE...

Make arrangements to add one or more files to the current checkout
at the next commit.

When adding files recursively, filenames that begin with "." are
excluded by default.  To include such files, add the "--dotfiles"
option to the command-line.

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


Благодаря идеологии распределенной работы все версии репозитория равноправны, так что утрата любого из репозиториев, включая репозиторий на основном сервере, проблемой не является - можно скопировать репозиторий любого разработчика на сервер и синхронизировать его с остальными разработчиками, восстановив, таким образом, всю информацию. Вы еще думаете о проблеме бэкапов и центральном сервере? Забудьте, мы живем в мире распределенной информации. Если бы я решил последовать текущей ИТ-моде, то совершенно справедливо назвал бы fossil "облачной" системой контроля версий.


Вики


Ранее мы уже рассматривали систему контроля версий со встроенной вики: Система контроля версий Рассмотрим подробнее, чем же эта система нам будет полезна в новом качестве.


Начну с примера - данная статья с помощью встроенной в fossil вики в текущий момент редактируется локально в браузере, а после будет синхронизирована с ее веб-версией на сайте http://book.mobigroup.ru. Также можно сохранить файл статьи на диск, отредактировать в любимом текстовом редакторе и загрузить новую версию в локальный или удаленный fossil-репозиторий. Все ресурсы к статье (картинки, скрипты) также управляются fossil и в любой момент можно получить нужную версию статьи с соответствующими ей картинками и прочими ресурсами. Итак, есть фоссил-репозиторий на сайте и его копия (может быть сделана любым способом, каким нам больше нравится копировать файл) или клон (выполненный через HTTP командой fossil clone) на локальной машине. Изменения можно вносить в оба репозитория, а после синхронизации они снова станут идентичными. Иначе говоря, кроме стандартного функционала веб-вики мы получаем еще и вики локальную, которой можно пользоваться при отсуствии доступа в интернет и даже при отсутствии браузера. Любой пользователь может создать свою локальную копию и вносить в нее изменения, притом, если у него есть права редактирования вики-страниц в основном репозитории, то после этот пользователь сможет выполнить синхронизацию.


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


В качестве примера рассмотрим, как же нам получить копию репозитория, в котором опубликавана эта статья, в папке /tmp/book на своем компьютере:

$ mkdir /tmp/book
$ cd /tmp/book
$ fossil clone http://book.mobigroup.ru/ book.fossil
                Bytes      Cards  Artifacts     Deltas
Send:              49          1          0          0
Received:       16969        368          0          0
Send:           10025        225          0          0
Received:      850820        255         81        119
Send:            7898        176          0          0
Received:     1344313        214         60        107
Total network traffic: 9996 bytes sent, 434133 bytes received
Rebuilding repository meta-data...
367 (100%)...
project-id: c038be6d00dcad09c6cb103418aee95fea066e28
server-id:  e817408496f556de59679d530e521e807d2f9045
admin-user: alexey (password is "d52atf")

Вот и все, теперь можно работать с локальной копией. Имя администратора alexey и пароль "d52atf", но авторизация по умолчанию требуется только для удаленного доступа, а локально на своем компьютере логин и пароль не потребуются.


Откроем полученный репозиторий:

fossil open book.fossil 
...
project-name: Статьи Mobigroup
repository:   /tmp/book/book.fossil
local-root:   /tmp/book/
project-code: c038be6d00dcad09c6cb103418aee95fea066e28
server-code:  e817408496f556de59679d530e521e807d2f9045
checkout:     e78a25c434cfbb6175f91a69ae9dd022498392b8 2010-07-29 14:22:15 UTC
parent:       942d9890eeb8c85394c509f486558d708d8c7d08 2010-07-27 07:30:22 UTC
tags:         trunk

Теперь можно из консоли работать с репозиторием или запустить веб-интерфейс в браузере:

$ fossil ui
В текущем сеансе браузера создано новое окно.

После выполнения этой команды запустится браузер или откроется новое окно в уже запущенном браузере, с адресом http://localhost:8080/ Веб-интерфейс достаточно удобный, так что не будем отсанавливаться подробнее на том, как создать или сохранить страницу вики или просмотреть и извлечь файлы из репозитория.

Оптимизация взаимодействия веб-клиента и сервера - Кэширующий прокси

Об этих "невидимках" многие разработчики забывают, ведь кэширующие прокси находятся где-то далеко от серверов проекта и нам не подчиняются. Зато с ними мы совершенно бесплатно получаем так называемую CDN (Content Delivery Network), о которой мечтают чуть ли не все веб-разработчики. Очевидно то, что для сохраненных в кэше прокси-серверов объектов запрос к веб-серверу не требуется и это может очень значительно снижать нагрузку на оборудование нашего веб-ресурса. Но это еще не все, ведь клиент получает HTTP-ответ от ближайшего к нему прокси, у которого нужный ресурс есть в кэше, таким образом, время отклика снижается многократно. Если же нужный объект закэширован непосредственно в браузере, результат окажется еще лучше. Также в любой момент можно установить кэширующий прокси на своем сервере (или серверах) и так масштабировать веб-проект. Кэширующие прокси стандартизированы и следует лишь настроить отдачу нужных HTTP-заголовков для управления кэшированием с помощью веб-сервера или реверс-прокси.


Обратите внимание на следующие HTTP-заголовки: Cache-Control, ETag, Expires, If-Modified-Since, Last-Modified. Чаще всего используются Cache-Control и Expires. Подробно рассматривать эти заголовки мы не будем, поскольку их настройка индивидуальна для разных проектов и в сети Интернет есть детальные руководства для всех популярных случаев.


Приведем пример, как добавить HTTP-заголовок с директивой кэширования на уровне реверс-прокси Haproxy:


rspadd Cache-Control:\ public,\ max-age=604800

Схожим образом можно и удалить HTTP-заголовок:


rspidel ^Cache-Control:.*

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


Узнать больше об использовании директив кэширования можно в статье PHP и Web. Кэширование.

Оптимизация взаимодействия веб-клиента и сервера - Настройка операционной системы

Для нагруженного сайта необходимость настройки сервера мало у кого вызывает удивление, да и для небольших веб-проектов стоит заранее предусмотреть опции защиты от DDOS-атак. По умолчанию Linux-системы имеют сетевые настройки, более подходящие для десктопов, нежели для серверов. Какие-либо общие рекомендации дать сложно, потому предлагаю читателям набор настроек, которые достаточно хороши для "среднего" сервера.


/etc/sysctl.conf

net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

net.ipv4.conf.default.rp_filter=1
net.ipv4.tcp_syncookies=1

net.ipv4.tcp_keepalive_time = 120
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 5

net.core.somaxconn = 1024
net.core.netdev_max_backlog = 4000
net.ipv4.tcp_max_syn_backlog = 4096
net.ipv4.tcp_max_tw_buckets = 360000
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1

net.core.rmem_default = 262144
net.core.wmem_default = 262144

net.core.rmem_max = 16777216
net.core.wmem_max = 16777216

net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

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

Оптимизация взаимодействия веб-клиента и сервера - Реверс-прокси

Реверс-прокси это все равно что дворецкий, встречающий гостей в аристократическом замке, и при плохом выборе наш проект поворачивается к посетителям отнюдь не лицом. Современные реверс-прокси также являются и балансировщиками нагрузки, позволяя распределять поступающие запросы по множеству серверов-обработчиков.


Среди "чистых" реверс-прокси, не обремененных сторонней функциональностью, стоит назвать Pound и HAProxy. Рассмотрим особенности их использования в реальных проектах. Начнем с Pound, как более простого:


Pound позволяет распределять нагрузку между множеством Web-серверов, а также имеет встроенную поддержку SSL.

Для небольших проектов Pound будет прекрасным выбором, но при росте проекта может потребоваться его замена. На процессоре уровня Pentium D примерно при 200-300 HTTPS или 600-800 HTTP-запросах в секунду Pound перестает справляться с нагрузкой. Разработчики Pound в ответ на подобные вопросы рекомендуют воспользоваться другими реверс-прокси, пригодными для больших проектов, и это же отражено в документации. Настройка Pound проста и не требует много времени, более того, достаточно прочитать главную страницу проекта, чтобы узнать о его настройке все или почти все. Для небольших и средних проектов стоит выбрать именно его. Плюсом является и встроенная поддержка HTTPS.


Для высоконагруженных проектов прекрасным выбором станет HAProxy:

HAProxy это TCP/HTTP обратный прокси, предназначеннный для высоконагруженных проектов. Поддерживаются сессии на основе HTTP куки, балансировка нагрузки, управление входящими и исходящими хидерами HTTP-запросов. Кроме того, есть возможности блокировки запросов и удобный веб-интерфейс для просмотра всей статистики и текущего состояния.


HAProxy один из самых быстрых реверс-прокси. В число проектов, где работает HAProxy, входит Amazon EC2. Настройка нетривиальна и требует внимательного изучения документации, зато настроить можно очень гибко. Начинать знакомство с этим прокси стоит с чтения официальной документации.

Оптимизация взаимодействия веб-клиента и сервера - Сервера приложений (платформы)

Многие динамические сайты для проектов с высокой нагрузкой используют специализированные веб-сервера со встроенным интерпретатором какого-либо скриптового языка и встроенными средствами оптимизации и масштабирования. Такие системы принято называть серверами приложений:


Сервер приложений (англ. application server) — сервер, исполняющий некоторые прикладные программы. Термин также относится и к программному обеспечению, установленному на таком сервере и обеспечивающему выполнение прикладного ПО.

Ниже мы предпримем небольшой экскурс в историю и познакомимся с первым или одним из первых серверов приложений, который успешно развивался все эти годы и сегодня является мощной и эффективной системой уровня предприятия. Ссылки в основном ведут на англоязычные ресурсы, потому что в рунете практически нет информации о веб-технологиях 1990-х, активно развивавшихся в США. На самом же деле ко времени появления форка довольно примитивного веб-сервера NCSA HTTPd 1.3 под именем Apache в 1995 году уже существовали мощнейшие веб-платформы. А основанная в 1995-м году Полом Грэмом и Робертом Моррисом компания Viaweb была первым SAS (Software-As-Service) провайдером! В 1998-м году эта компания была приобретена Yahoo и превратилась в Yahoo Store.


Для Tcl мощным сервером приложений является AOL Server с многопоточным интерпретатором Tcl и развитым высокоуровневым API, пулом подключений к БД, мощной системой кэширования кода и файлов, пулом процессов-обработчиков HTTP-соединений, поддержкой языка серверных сценариев AOLserver Dynamic Pages и другими возможностями. При этом конфигурационный файл AOL Server является тиклевым скриптом, так что в нем можно использовать переменные, получать параметры конфигурации из любых внешних источников и многое другое. AOL Server разрабатывался в созданной в 1993-м году компании NaviSoft, купленной в 1994 году корпорацией AOL, и в 1999 году AOLserver 3.0 выпущен под open-source лицензией AOLserver Public License, аналогичной Mozilla Public License. Подробнее можно прочитать в статье от 2002-го года Introducing AOLserver. Существует форк проекта AOL Server, который взял исходное название NaviServer, в нем добавлены некоторые интересные возможности.


Совместно с сервером приложений для разработки может использоваться и один из фрэймворков:

Каркас (англ. framework) — в информационных системах структура программной системы; программное обеспечение, облегчающее разработку и объединение разных компонентов большого программного проекта. В его состав могут входить вспомогательные программы, библиотеки кода, язык сценариев и прочее.


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


Для AOL Server известным фрэймворком является OpenACS, есть и другие. OpenACS является открытой системой, построенной на кодовой базе одной из первых enterprize систем управления веб-контентом ArsDigita Community System, или, сокращенно, ACS. Да-да, от легендарной ArsDigita Corporation, о которой с восхищением пишет Джоель Спольски в статье Весна в Кэмбридже. В OpenACS добавлена поддержка СУБД PostgreSQL, тогда как ACS работал только с СУБД Oracle. В 2001 году после краха доткомов ArsDigita находилась в сложном положении, и было принято решение создать Java EE версию ACS, а в 2002 году компания Red Hat купила ArsDigita и продолжила разработку платформы ACS на Java EE под именем Red Hat CCM, в то время как Tcl-версия была переименована в OpenACS и управляется OpenACS community. В настоящее время многие крупные сайты работают именно на AOL Server + OpenACS, в том числе учебный процесс и сайты многих университетов управляются известной системой для электронного образования www.dotlrn.org


Раздача статического контента с помощью сервера приложений не столь быстра, как у веб-серверов для статики - это плата за возможность реализации необходимой бизнес-логики. Если посмотреть сравнительный график веб-серверов из соответствующего раздела, то становится заметным отставание AOLServer.


Теперь подробнее рассмотрим сервер приложений AOL Server и начнем с его установки. В debian-based дистрибутивах установка выполняется с помощью пакетного менеджера apt, в других дистрибутивах есть свои менеджеры пакетов.

sudo aptitude install aolserver4

Подготовительные действия по инициализиции веб-приложения и загрузке вспомогательных библиотек теперь не требуется выполнять для каждого запроса заново - эти действия можно выполнить единожды в скрипте инициализации. В конфигурации по умолчанию в debian-системах это должен быть файл с произвольным именем и расширением tcl в директории /usr/lib/aolserver4/servers/main/modules/tcl/tcl_module/, мы назовем его init.tcl:


/usr/lib/aolserver4/servers/main/modules/tcl/tcl_module/init.tcl

package require sqlite3
Здесь мы загрузили библиотеку для работы с базами данных формата SQLite3.

Запускаем сервер с конфигурационным файлом по умолчанию (из debian-пакета):

$ sudo /etc/init.d/aolserver4 restart

При старте будет загружен созданный нами файл init.tcl и указанная в нем библиотека станет доступна всем скриптам.


Пример. Простая страница.


В динамических страницах AOL Server содержимое тэгов <% ... %> интерпретируется как Tcl код, а все прочее считается HTML разметкой. Так что от нас потребуется лишь в нужном месте HTML страницы вставить Tcl код:


/var/www/index.adp

<HTML><BODY>
<h1>Привет, мир! Сейчас <%=[clock format [clock seconds]]%></h1>
</BODY></HTML>"

Проверяем, что наша страничка корректно создается:

$ openload -t localhost/index.adp
URL: http://localhost:80/index.adp
Clients: 1
------------------------
Status: 200
---- Headers -----------
Expires: now
MIME-Version: 1.0
Date: Sat, 03 Jul 2010 05:56:16 GMT
Server: AOLserver/4.5.1
Content-Type: text/html; charset=UTF-8
Content-Length: 123
Connection: close
---- Body   123 bytes --
<HTML><BODY>
<h1>Привет, мир!</h1>
</BODY></HTML>
---- End ---------------
Можно это и в браузере посмотреть, зайдя на адрес локального сервера http://localhost

Пример. Работа с БД SQLite3.


Создадим тестовую базу:

echo "create table log(date REAL NOT NULL DEFAULT (julianday('now'))); \
CREATE INDEX log_date_idx ON log(date);" | sqlite3 /tmp/www.db
chmod a+rw /tmp/www.db 

Создадим скрипты для вставки новой записи:


/var/www/write.adp

<%
sqlite3 db /tmp/www.db
db timeout 2000
db eval {insert into log default values}
set rowid [db onecolumn {select last_insert_rowid()}]
ns_adp_puts "Inserted new record ROWID=$rowid"
db close
%>

и для чтения времени последней добавленной записи:


/var/www/read.adp

<%
sqlite3 db /tmp/www.db
db timeout 2000
set lasttime [db onecolumn {select datetime(max(date)) from log}]
ns_adp_puts $lasttime
db close
%>

Команда db timeout 2000 указывает, что в случае, когда в БД заблокирована (производится запись из другого потока), следует подождать освобождения БД, но не более 2-х секунд (т.е. 2000 миллисекунд), а не возвращать сразу же ошибку доступа к БД. В AOL Server есть пулы соединений, предоставляющие единый интерфейс доступа к разным СУБД, но здесь используем более простой способ, открывая новое соединение к БД на каждый запрос.


Как мы видим, нет ничего особенно сложного в разработке веб-проектов на основе AOL Server. Рассказать можно еще очень многое, но нам придется ограничиться лишь таким кратким обзором. Для тех, кто заинтересовался одним из мощнейших из существующих серверов приложений AOL Server, найдется множество статей, примеров и целых промышленных платформ, ссылки на некоторые из них приведены в начале этой статьи.

Оптимизация взаимодействия веб-клиента и сервера - Динамический контент

Что касается раздачи динамического контента ("динамики"), хорошо известным методом является использование CGI-технологии:


CGI (от англ. Common Gateway Interface — «общий интерфейс шлюза») — стандарт интерфейса, используемого для связи внешней программы с веб-сервером. Программу, которая работает по такому интерфейсу совместно с веб-сервером, принято называть шлюзом, хотя многие предпочитают названия «скрипт» (сценарий) или «CGI-программа».

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


Промежуточным решением между CGI и серверами приложений можно назвать технологию FastCGI, позволяющую убрать часть ограничений CGI, но имеющую и свои серьезные проблемы. По моему мнению, стоит или ограничиться CGI, если хватает производительности, или работать с серверами приложений.


Часто CGI-приложения запускают с помощью веб-сервера, но на самом деле, это совсем не обязательно. Веб-сервер для CGI-программ всего лишь определяет значения переменных окружения согласно полученным заголовкам HTTP, хотя и само приложение может прочитать эти заголовки и обойтись без помощи веб-сервера. Далее я приведу примеры CGI-приложений как запускаемых напрямую, так и с помощью веб-сервера fnord-cgi (это уже знакомый нам fnord, скомпилированный с опцией поддержки CGI).


Разрабатывать веб-приложения можно на самых разных скриптовых языках, но я предпочитаю пользоваться языком Tcl, который с успехом применяется как для больших проектов (в том числе веб-проектов), так и для несложных утилит. Кроме прочих соображений, Tcl надежен и предложенные мною примеры можно запускать на общедоступном сервере. Если вы захотите переписать примеры на любом другом языке, это сделать несложно, но берегитесь уязвимостей, особенно при вызове внешних программ. В дополнение к примерам на Tcl я продемонстрирую написание CGI-программы и на unix shell.


Пример. Листинг директории (unix shell)


Для получения листинга директории можно поступить следующим образом: вывод утилиты ls с помощью unix shell отформатировать в виде, понятном для веб-клиентов. Главное достоинство этого метода в том, что он очень быстр - настолько, что зачастую не уступает встроенному в веб-сервер генератору индекса директории. Шелл-скрипт может выглядеть следующим образом:


$ tcpserver localhost 8888 echo "HTTP/1.0 200 OK
Content-type: text/plain

`ls`"

$ openload -t localhost:8888
URL: http://localhost:8888/
Clients: 1
------------------------
Status: 200
---- Headers -----------
Content-type: text/plain
---- Body    10 bytes --
hello.cgi
---- End ---------------

Приведенные команды с tcpserver и openload нужно запускать в разных консолях, так, чтобы tcpserver был запущен первым и мог ответить на запрос от openload. И в результате мы видим простой листинг директории, который несложно отформатировать в удобном для нас виде.


Пример. Вывод текущего времени (Tcl)


hello.cgi

#!/usr/bin/tclsh

 
puts "HTTP/1.0 200 OK"
puts "Content-type: text/html; charset=utf-8\n"
puts "<HTML><BODY>"
puts "<h1>Привет, мир! Сейчас [clock format [clock seconds]]</h1>"
puts "</BODY></HTML>"

Запустим почти так же, как и ранее. Утилита env здесь "прячет" от скрипта все переменные окружения - я это делаю из соображений безопасности, но это не является общепринятой практикой.


$ tcpserver localhost 8888 env -i ./hello.cgi

$ openload -t localhost:8888
URL: http://localhost:8888/
Clients: 1
------------------------
Status: 200
---- Headers -----------
Content-type: text/html; charset=utf-8
---- Body   104 bytes --
<HTML><BODY>
<h1>Привет, мир! Сейчас Tue Jul 13 23:29:13 MSD 2010</h1>
</BODY></HTML>
---- End ---------------

Откройте адрес http://localhost:8888 в своем браузере и вы увидите страничку текстом "Привет, мир!" и текущей датой.


Пример. AJAX и СУБД SQLite3 (Tcl)


Проиллюстрируем создание AJAX-сервиса на примере небольшого приложения, отображающего записи из базы данных: http://mobigroup.ru/service/jsontest/. Код доступен по ссылке jsontest. Для отображения результатов используется компактный и быстрый кроссбраузерный шаблонизатор на javascript (см. js-функцию supplant).


Пример. Конвертор трэков GPS (Tcl)


Напишем конвертор трэков со спутникового навигатора для отображения их на Google Maps и в Google Earth. Создадим страничку, отображающую форму для отправки пользователем конвертируемого файла, и скрипт для преобразования формата трэка. Само преобразование выполнит утилита gpsbabel, а от скрипта потребуется принять и сохранить файл, вызвать gpsbabel с нужными аргументами и вернуть результат пользователю.


Для интерактивного приложения, взаимодействующего с пользователем, удобно использовать модуль ncgi из библиотеки tcllib, умеющий работать с HTTP-формами, cookie и загрузкой файлов. Образцы кода для работы можно найти по адресу ncgi.test. Модуль предполагает существование переменных окружения, список которых определяется стандартом для протокола CGI. Опять же, обратимся к минималистичному веб-серверу fnord-cgi для обработки CGI-скриптов.


HTML-страница для выбора файла исходного трэка:


index.html

<html>
<head> 
<meta content='text/html; charset=utf-8' http-equiv='content-type'></meta> 
</head> 
<body>
<h3>Конвертор трэков в формат KML</h3> 
<form enctype='multipart/form-data' method='POST' action='/track2kml.cgi'>
<input name='file' id='file' type='file' size='25''/>
<input name='upload' value='Преобразовать' id='upload' type='submit'/>
</form> 
</body> 
</html>

И CGI-скрипт пробразования трэка:


track2kml.cgi

package require ncgi
::ncgi::parse

puts "Content-type: text/html; charset=utf-8\n"

array set extensions {.gdb gdb .mps mapsource .plt ozi .gpx gpx .nmea nmea .txt nmea}
set filename [clock seconds].kml
set clientname [ncgi::importFile -client file]
# anti-XSS                                                                                                                   
set clientname [string map {& &amp; < &lt; > &gt; \" &quot;} $clientname] 
puts "
<html><body>"
if {[::ncgi::exists file]==1 && $clientname ne {} && 
  [info exists extensions([file ext $clientname])]==1} {
    set filetype $extensions([file ext $clientname])
} else {
    puts "<b>Укажите файл для конвертации в одном из форматов
[array names extensions]</b>"
    puts "<br><br><a href='javascript:history.go(-1)'>Назад</a>"
    exit
}
puts "<b>Загружен файл: $clientname</b><br>"
set inname [ncgi::importFile -server file]
set tmpname ${inname}.kml
catch {exec /usr/bin/gpsbabel -t -x simplify,error=0.002k -i $filetype \
  -o kml,points=0,trackdata=0,line_width=3,line_color=990000FF $inname $tmpname}
if {[file exists $tmpname]==1} {
  catch {file rename $tmpname $filename}
} else {
  puts "<b>Ошибка: не удалось выполнить конвертацию</b>"
}

if {[file exists $filename]==1} {
    puts "<a href='$filename'>Скачать файл KML</a><br>"
}
puts "<br><br><a href='javascript:history.go(-1)'>Назад</a>"
puts "</body></html>"

Запускаем сервис так:

tcpserver localhost 8888 env -i /usr/sbin/fnord-cgi .

Загружаем трэк, к примеру, с Garmin Oregon в формате gpx, а полученный трэк в формате kml можно открыть в Google Earth. Онлайн-версия этого сервиса также предоставляет ссылку для просмотра трэка на Google Maps и доступна по адресу http://mobigroup.ru/service/track2kml/, а ее код можно увидеть здесь track2kml. Более современным решением является создание AJAX-приложения, смотрите http://mobigroup.ru/service/track2kml-ajax/, и его код track2kml-ajax. В последнем случае мы полностью разделяем серверный код на Tcl и клиентский интерфейс на html/javascript, что очень облегчает дальнейшие поддержку и модификацию приложения, притом код серверной части заметно упрощается.

Оптимизация взаимодействия веб-клиента и сервера - Статический контент


Введение

Зачастую достаточно лишь позаботиться об интенсивном кэшировании статики на стороне клиента (включая сюда кэширующие прокси-серверы интернет-провайдеров), и количество запросов на статику оказывается относительно мало, так что оптимизация этих и так "легких" запросов становится бесполезной. Но что делать, если запросов все равно очень много? Это может быть вызвано как характером контента (например, фотогалерея), так и просто высокой посещаемостью, особенно когда много уникальных посетителей.

Легковесность

Минималистичные веб-сервера для раздачи статики совсем или почти совсем не требуют настройки, обеспечивая при этом высокую производительность. Одним из действительно простых и эффективных веб-серверов для отдачи статического контента является fnord:
Программа fnord представляет собой миниатюрный HTTP сервер(15k размером), запускаемый посредством TCP суперсервера tcpsvd, tcpserver или другого. fnord хорошо оптимизированный и быстрый, поддерживает передачу методом sendfile и постоянные соединения (keep-alive), CGI, виртуальные домены, и докачку (content-ranges). Дополнительно встроена возможность подмены содержимого на лету (html - html.gz, or gif - png), и генерация индекса директории.
Особенно интересен fnord тем, что работа с TCP делегирована так называемому TCP суперсерверу. В любой UNIX/Linux системе есть встроенный TCP суперсервер, но мы обратимся к помощи более производительного tcpserver:
tcpserver ожидает входящие соединения от TCP клиентов. Для каждого соединения запускается программа, указанная аргументом командной строки, при этом дескриптор 0 принимает данные из сети и дескриптор 1 передает данные в сеть. Также tcpserver устанавливает значения нескольких переменных окружения.
Как и некоторые другие экстремально компактные программы, tcpserver и fnord следуют так называемому UNIX-way и умеют выполнять лишь одну задачу, зато делают это хорошо. Демон tcpserver слушает указанный порт, и для каждого входящего соединения (на каждый HTTP-запрос) вызывает отдельный экземпляр fnord, который читает содержимое HTTP-запроса на stdin и возвращает результат (HTTP-заголовки и тело ответа) на stdout. Запускается веб-сервер, к примеру, по адресу localhost:8888 и для текущей директории следующим образом:
tcpserver localhost 8888 /usr/sbin/fnord .
В консоль будет выводиться лог всех соединений.

Быстродействие

И в самом деле, легковесный веб-сервер может работать без какой-либо настройки и без конфигурационного файла, но что же будет с производительностью? Для ответа на интересующий нас вопрос сравним веб-сервера по количеству обрабатываемых в секунду запросов (TPS) в зависимости от размера запрошенного файла при 50-ти конкурентных клиентских подключениях. Для каждого вебсервера мы ограничимся конфигурацией по умолчанию из дистрибутива debian squeeze, для работы на одном процессорном ядре это достаточно хороший выбор. Так как размер HTTP заголовков составляет сотни байт, то и тестирование передачи файлов размером менее килобайта практического смысла не имеет.Для тестирования я выбрал несколько популярных веб-серверов. Скрипт для тестирования можно взять по ссылке dload.tcl. Вот какие результаты были получены:
File sizeAolserver/4.5.1Apache2/2.2.15Fnord/1.10Lighttpd/1.4.26Nginx/0.7.65
1000812633682853885
5000724574674843959
10000719629671840866
50000556517608766853
100000386469557665742
500000163296333321305
100000079127135156147

или на графике


Потратив лишь минуту своего времени на запуск веб-сервера fnord, мы получили результат на уровне больших и сложно настраиваемых решений. Не будем углубляться в рассмотрение среднего времени ожидания обработки запроса, замечу лишь, что для связки tcpserver + fnord этот параметр на уровне результатов Nginx и Lighttpd (подробности смотрите в файлах dload.log в прикрепленных к статье ресурсах). Думаю, для многих такой результат оказался неожиданным. Оставляя "за бортом" философские вопросы, мы приходим к следующему выводу - оправдано использование или экстремально простого веб-сервера для статики (такого, как fnord и некоторые другие), или же следует проводить комплексное тестирование и всерьез заниматься настройкой, прежде чем выбирать одно из более сложных решений, таких как Nginx, Lighttpd, etc.

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

В целях защиты нашей системы крайне желательно, чтобы веб-сервер не имел возможности записи на диск (для защиты от перегрузок при DDOS и взлома через уязвимости в коде работы с файловой системой) и мог запускаться в chroot. При необходимости сохранения лога запросов следует его получать с stdout или в syslog. Перечисленным требованиям удовлетворяет fnord, причем запуск в chroot не требует никаких дополнительных действий. "Большие" веб-сервера придется дополнительно настраивать, чтобы они не пытались писать логи самостоятельно, а поместить их в chroot зачастую непросто.

Оптимизация взаимодействия веб-клиента и сервера - Введение

Всемирная паутина (www) начиналась с полностью статичных веб-ресурсов, так что пользователи могли лишь просматривать предложенные веб-странички и скачивать файлы. Со временем появлялись все более развитые средства взаимодействия с пользователями, начали использовать кукисы (cookie) для идентификации пользователей, предложили формы обратной связи и форумы. Сегодня многие веб-сайты обзавелись базами данных и активно применяют ajax-технологию для удобства посетителей, и для нас стал привычен термин "веб-приложения". Происходящее сравнимо с революционными изменениями, порожденными открытием книгопечатания, и даже более того - ведь теперь не только информация стала как никогда раньше доступной и глобальной, но и каждый ее потребитель легко и быстро может связаться с автором, включаясь таким образом в процесс создания новой информации. Все это здорово, но техническая часть взаимодействия между веб-приложениями и их пользователями производит впечатление чрезвычайно сложного процесса, требующего сложнейших программных решений. Но так ли это на самом деле? В предлагаемой вашему вниманию статье автор постарался показать, насколько просты принципы, лежащие в основе современного веба, и как легко можно стать полноправным участником построения всемирной сети. Печатная машинка делает процесс печати доступным каждому из нас, но большая типография производит впечатление сложного и непостижимого для неспециалиста технологического процесса. Да, технологически типография сложна, но суть ее - быть высокопроизводительной печатной машинкой. Вероятно, уже спустя 10 лет мы вовсе забудем о существовании типографий, поскольку дальнейшее развитие технологий сделает их ненужной ветвью эволюции информационного общества. Ровно так же и в области информационных технологий есть свои рабочие инструменты и есть огромные монстры, причем последние являются лишь данью нашему времени. Для сравнения мы будем упоминать и монстров, названия которых знакомы многим нашим читателям, но главное, постараемся показать основные идеи и те основные инструменты, которые позволяют их воплотить в жизнь. Автор уверен, что статья окажется полезной как начинающим разработчикам, так и профессионалам, поскольку обсуждения многих затрагиваемых здесь вопросов показывают, как мало разработчиков задумываются о том, что они делают, будучи вместо этого всецело поглощены мыслью о том, как это делать.

С чего же начинается построение веб-ресурса? Для создания и отправки созданного цифрового контента клиентам служит программное обеспечение, называемое веб-сервером. Существуют разнообразные веб-серверы, от маленьких и простых до огромных и сложных, к тому же требующих дополнительных внешних программ для своей работы. Выбор веб-сервера задача непростая, и во многих проектах используются разное программное обеспечение для передачи статических, заранее подготовленных файлов, так называемой "статики" и динамически создаваемых данных, или кратко "динамики". Также в некоторых случаях выделяются задачи потоковой трансляции мультимедиа-содержимого и некоторые другие, которые мы рассматривать не будем, поскольку такие вопросы являются узкоспециализированными и выходят за рамки текущей статьи (большинство сайтов работают с таким содержимым ровно также, как с обычной статикой). Ниже будет представлен обзор некоторых популярных решений и предложен миниатюрный и не требующий настройки веб-сервер для раздачи статики, а также технология управления генерацией динамического содержимого. Как будет показано, разница в быстродействии между самыми сложными и самыми простыми решениями часто невелика, в то время как разница между затрачиваемым на их изучение и настройку временем огромна.

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

Во многих случаях оптимально и достаточно ограничиться одним из миниатюрных веб-серверов для отдачи статики, технологии CGI для динамики и балансировщика нагрузки, если проект достаточно большой и один веб-сервер не справляется. Далее везде предполагается, что на сервере работает именно операционная система Linux (с ядром 2.6.26 и выше) или другая UNIX-система.

Хотя веб-сервер является необходимым компонентом, зачастую его недостаточно и для оптимизации производительности и времени отклика веб-ресурса применяются еще и так называемые реверс-прокси серверы (обратные прокси), которые представляют собой "единое окно" для доступа всех пользователей к одному или более "спрятанных" веб-серверов. Для рассматриваемой архитектуры существует калька с английского названия фронтэнд (front-end) для прокси-сервера (в общем случае - компонента, непосредственно взаимодействующего с пользователями), и бэкенд (back-end) для компонентов, непосредственно выполняющих обработку запросов, но невидимых для пользователей. Реверс-прокси лишь передают запросы на обработку далее, при этом они могут модифицировать как запрос, так и полученный результат (удалить небезопасные данные или, наоборот, добавить дополнительные данные). Преимущество от работы обратного прокси достигается за счет "умного" управления очередью пользовательских запросов и распределения этих запросов по бэкендам (в том числе, осуществляется балансировка запросов по нескольким одинаковым бэкендам, передача определенных типов запросов нужным бэкендам и обеспечение минимального времени ожидания запросов пользователей в очереди, блокирование сетевых атак многих типов). Для небольших проектов реверс-прокси часто работает на том же физическом сервере, что и бэкенды, и даже в этом случае выгоды от такого дополнительного компонента весьма ощутимы. Из-за того, что пользователь не видит разницы между обращением непосредственно к веб-серверу или к реверс-прокси, во всех случаях установленное на сервере программное обеспечение зачастую называют одинаково - веб-сервер.

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

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

В качестве критериев оценки мы будем показывать скорость обработки запросов, измеряемую в транзакциях в секунду (TPS - transaction per second). Измерения будут проводиться на моем нетбуке Intel(R) Atom(TM) CPU N450 1.66GHz. Почему именно нетбук? Ну, во-первых, именно на нем я пишу статью и мне это удобно :-) Основная же причина - показанные значения будут достаточно близки к тому, что мы получаем на бюджетных хостингах. Если же вы примените описанные далее методики на хорошем железе, то будете приятно удивлены. Я мог бы провести замеры на топовом "железе" и получить высокие цифры для украшения статьи, но это бы не привело ни к чему иному, как к разочарованию большинства читателей при практическом использовании представленного материала.

Во многих примерах показаны однопоточные обработчики, и вот почему. Специализированный балансировщик запросов выполнит задачу распараллеливания запросов почти всегда лучше, нежели встроенный в веб-сервер модуль балансировки, что, вдобавок, чрезвычайно упрощает логику самого обработчика запроса, делая его однопоточным. Занять все доступные ядра просто, запустив несколько однопоточных обработчиков на разных портах и настроив балансировку входящих запросов по этим портам в реверс-прокси, а масштабирование по числу процессоров/ядер становится наилучшим - линейным.

(C) Alexey Pechnikov aka MBG, mobigroup.ru