кое-что о разработке веб-приложений для локального применения
Написание локальных веб-приложений может стать быстрым, легким и эффективным способом решения определенных задач во внутренней сети. Узнайте, почему в некоторых случаях веб-браузер предоставляет лучший интерфейс по сравнению с GUI-приложением, а простейшим и наиболее изящным решением может стать CGI-скрипт.
Совершенно очевидно, что подавляющее большинство посещаемых вами веб-сайтов доступно через Интернет, однако многие компании пришли к заключению, что и разработка приложений для внутренних сетей занимает немаловажное место. Эту идею можно развить дальше и разработать полнофункциональные веб-приложения, которые никогда не будут отправлять ни пакета данных через сетевой интерфейс. Опытные веб-разработчики иногда затрачивают большие усилия на изучение какого-нибудь GUI-инструментария, хотя то, что действительно нужно в их ситуации - это простой CGI-скрипт. Во всяком случае, веб-приложение, предназначенное для работы только в локальной сети, гораздо проще приложения для универсального применения. Вы легко сможете задать требования для браузера, а производительность сервера, скорее всего, не будет большой проблемой. Простые приложения, использующие стандартные виджеты CGI-форм и им подобные, можно написать за гораздо меньшее время, чем потребовалось бы на разработку самодостаточных приложений. Приложения, построенные вокруг обработки форм или данных, часто являются отличными кандидатами на реализацию в качестве обычных веб-сервисов.
Во многих случаях такое специальное приложение может обеспечить изящное и простое решение определенной узкой задачи. Однажды я написал утилиту для просмотра изображений, которая делала лишь одну вещь: просматривала директорию с изображениями с фотоаппарата и давала мне возможность сортировать картинки по категориям. Программа занимала лишь около двадцати строчек на Perl, а в результате работала гораздо быстрее, чем более универсальное решение, не говоря уже о времени, которое потребовалось бы на написание приложения с графическим интерфейсом, выполняющего эту же самую функцию.
итак, чем же нам поможет веб-браузер?
Можно задать совершенно резонный вопрос: ну и что? Что умеет веб-браузер, чего не умеют другие приложения? Ответ очевиден: ничего. Но тогда что же можно написать на высокоуровневых языках, чего нельзя выполнить в машинных кодах? Опять же, ничего. Преимущество при использовании веб- браузера в качестве интерфейса состоит в том, что все жесткое кодирование уже выполнено. Не нужно отслеживать события изменения размеров или развертывания окна или события меню. Все, что нужно сделать, это прочитать фрагмент данных с запросом и обработать его.
Потенциальная слабость разработки основанных на веб-технологиях приложений - недостаточные возможности по управлению их внешним видом. На самом деле, это не такой уж и недостаток. Если вы пытаетесь максимально ускорить разработку приложения, вам точно не стоит тратить много времени на возню с внешним видом. На какой бы платформе вы не работали, пользователям будут знакомы кнопки и текстовые виджеты веб-браузера. Это действительно весьма полезная возможность.
Веб-браузер делает еще одну очень полезную вещь: он предоставляет множество настроек и предпочтений, о которых вам не нужно беспокоиться. Пользователь может изменять размер шрифта во время работы. Аналогично, если сгенерированные выходные данные представляются в простой и аккуратной HTML-форме, их можно легко и быстро вывести на печать. Многие функции, которые в ином случае вам пришлось бы реализовывать самостоятельно (например, сохранение выходных данных в файл, вывод выходных данных на печать, изменение размеров окон), уже реализованы.
однопользовательская архитектура
Хотя может показаться, что однопользовательское окружение полностью устраняет многие проблемы, связанные с разработкой приложений, это не совсем так. Недавно я написал систему для работы с массивом документов как веб-приложение. Поскольку я спроектировал ее для одновременной работы максимум с одним пользователем, я подумал, что смогу обойтись без объемного кода, занимающегося блокировкой файлов, который в ином случае был бы необходим. Я ошибался. В интерфейсе использовались фреймы, и одна из программ проверки работоспособности, запущенных моим оконным менеджером, могла зависнуть намертво, если другая программа изменяла базу данных, пока та была запущена. Браузер часто начинал обрабатывать оба запроса одновременно.
При использовании локального сервера отпадают проблемы с пропускной способностью сети; даже то, что может вызвать проблемы при работе через локальную сеть, будет нормально функционировать на локальной машине, хотя большие файлы по-прежнему будут тормозить браузер или приводить к его зависанию.
сетевая безопасность
Если вы разрабатываете приложение, которое будет запускаться на локальном веб-сервере, задайтесь вопросом, что произойдет, когда кто-то другой будет обращаться к этому приложению по сети. В идеале, убедитесь, что локальное приложение доступно только через внутреннюю компьютерную сеть. Если сделать этого нельзя, потребуются специальные меры безопасности. Возможно, будет достаточно просто указать вашему приложению отвергать все соединения с любых адресов кроме 127.0.0.1, что гораздо безопаснее, чем использовать пароли.
Возможно, разумнее будет просто выбрать альтернативный номер порта, организовав для приложения выделенный сервер. С помощью командной строки UNIX® потребуется несколько минут, чтобы настроить личную копию Apache для работы через порт 8880, и в результате вы получите полный контроль над функциями и настройками сервера. Кроме того, серверная часть приложения будет выполняться с вашими обычными привилегиями, следовательно, не нужно делать важные файлы доступными для записи, что, несомненно, большой плюс.
управление данными
Для традиционного веб-приложения использование сервера баз данных в значительной степени упрощает разработку, так как механизм базы данных - в силу того, что это отдельный сервер - обеспечивает большую часть необходимой сериализации и блокировки, а обеспечить остальное довольно просто. Для однопользовательского приложения, которое будет запускаться на одном компьютере, такая схема избыточна и, возможно, реализовывать ее не стоит. Локальному приложению все это не нужно, зато в нем имеет смысл применить простой и легко перемещаемый файл данных. Куда бы я не путешествовал, я всегда синхронизировал файл данных моего приложения для работы с массивом документов между ноутбуком и десктопом. Это была несложная задача, поскольку я использовал файл Berkeley DB.
Формат Berkeley DB предлагает довольно простое решение, легко реализуемое на многих языках. Так как сервер баз данных не используется, это не лучший выбор для приложения, одновременно работающего с множеством пользователей; в целях безопасности необходимо заблокировать базу данных, выполнить нужные операции, а затем разблокировать ее. С другой стороны, поскольку сервера БД нет, такое решение подходит для программы, очень редко обслуживающей несколько пользователей одновременно.
Вот пример кода на Perl, подключающего базу данных Berkeley DB (с блокировкой):
my %db;
open(DBFILE, '+file.db');
flock(DBFILE, LOCK_EX);
$dbhandle = tie %db, 'DB_File', 'file.db', O_CREAT|O_RDWR, 0666, $DB_HASH;
После выполнения этого кода можно получить доступ к базе данных напрямую, как к стандартному хэшу Perl:
$db{user} = "me";
print "<p>User: $db{user}</p>\n";
Можно также использовать объект dbhandle для выполнения операций над базой данных:
$dbhandle->del("user");
После выполнения всех этих действий работа действительно закончена. То есть, когда ваша программа завершает работу, блокировка автоматически снимается. Блокировка с помощью flock() - всего лишь рекомендация. Она не помешает другим программам осуществлять запись в файл, если они ее не используют. Если ваша программа реализована в виде нескольких взаимосвязанных программ, добавьте код блокировки во все из них, или еще лучше, поместите его в модуль общего пользования. Таким образом, эта необязательная блокировка, тем не менее, обеспечивает необходимый результат: это надежная гарантия, что файлы данных будут изменяться только одной программой одновременно.
Некоторые приложения будут прекрасно работать с обычными файлами, например с CSV-файлами или просто текстовыми файлами с пустыми полями. Другим может потребоваться полнофункциональная SQL-база данных. Вы не обязаны применять решения "корпоративного класса" в небольших приложениях, которые должны быстро и легко выполнять поставленную задачу. Лучше сконцентрируйте усилия на устранении ошибок и создании удобных функций. При этом если вам нужна реляционная база данных - используйте ее.
виджеты интерфейса
Достаточно хорошие приложения получаются при помощи форм, содержащих множество кнопок. Если имеются метаданные или контекст, которые необходимо передавать от страницы к странице, используйте скрытые поля форм; проблемы безопасности, с которыми вы можете столкнуться (пользователи могут обходить их) в данном случае не так уж серьезны. Кнопки с изображениями можно использовать во многих целях, хотя в этом случае вам придется заняться разработкой рисунков. Если вы не хотите этого делать, создайте кнопку с альтернативным текстом, указав для изображения несуществующий URL:
print $q->image_button(-name => "sort",
-value => "$name",
-src => "/nonexistant",
-alt => $label);
Это может показаться нелепым по сравнению с более простой альтернативой использования гиперссылки, но в своей полной форме соответствующая ссылка href=... может быть довольно длинной, и ее будет невозможно вычислить заранее, если кнопка отправляет форму. Это очень грубый трюк, но он работает.
одна задача - одно приложение
Написав пару небольших веб-приложений для запуска на локальной машине, вы быстро заметите следующее: такие приложения часто создаются очень быстро, даже с учетом времени на отладку, а работают они значительно эффективней по сравнению с выполнением стандартных задач вручную. Выигрыш в эффективности, получаемый при упрощении долгих и скучных процедур, с лихвой окупает время на разработку такого приложения, тем более что этот процесс может занять лишь несколько минут.
В стандартной CGI-библиотеке Perl имеется множество полезных базовых рабочих инструментов. Поэкспериментировав с ней пару часов и привыкнув к ее чрезвычайно гибкому и богатому набору функций, вы сможете написать множество разнообразных приложений, не прилагая к этому больших усилий. С помощью несложной скриптовой системы можно удобно и эффективно использовать скрипты командной строки, решающие одну определенную задачу. Простые CGI-скрипты делают то же самое в отношении разнообразных программ с графическим интерфейсом. Посвятив им один день, вы быстро обнаружите, что можете с легкостью выполнять десятки задач, о программах для решения которых вы так мечтали.
масштабирование
К сожалению, типичная практика такова: сначала пишут небольшое простое приложение под конкретную задачу, предназначенное, как полагают, только для локального использования, а затем обнаруживается, что существует очевидная причина расширить это приложение для большей аудитории. Даже если приложение предназначено только для локального использования, пишите понятный код, оснащайте программу средствами контроля и сохраняйте отладочные выходные данные. Хороший код с удобной поддержкой отладки очень быстро окупается невысокими затратами на сопровождение. Если с самого начала в программе была предусмотрена возможность переработки для более широкой аудитории, выгода будет еще больше.
Значительную часть работы может составить рефакторинг, но это не так плохо, как вам, вероятно, кажется. По большей части вам придется заниматься масштабированием самой серверной базы данных или оптимизацией обработки файлов, чтобы обеспечить лучшую и более интеллектуальную блокировку. Если ваша программа занимается обработкой файлов, возможно, стоит написать небольшой сервер, выполняющий эти операции атомарно, обеспечивая таким образом сериализацию. Также для такого сервера будет разумно обрабатывать одного клиента за раз, поскольку отдельные скрипты выполняются быстро.
Главное, на что вам нужно обратить внимание - это программа, делающая предположения о внутреннем состоянии. Например, в программе-классификаторе документов совершенно очевидной представляется функция, отображающая для пользователя "следующий файл", а затем заносящая его в определенную категорию. Если работают два пользователя, необходимо отслеживать, какой файл обрабатывает каждый из них. Также нужен какой-то механизм обработки пограничных ситуаций. Например, если кто-нибудь откроет приложение, а затем в какой-то момент отойдет, то необходимо разрешить другому пользователю просмотреть этот файл. Иногда для этого потребуется существенная переработка всей серверной части, однако в целом будет довольно просто обеспечить стабильность на стороне клиента.
Питер Сибах
Совершенно очевидно, что подавляющее большинство посещаемых вами веб-сайтов доступно через Интернет, однако многие компании пришли к заключению, что и разработка приложений для внутренних сетей занимает немаловажное место. Эту идею можно развить дальше и разработать полнофункциональные веб-приложения, которые никогда не будут отправлять ни пакета данных через сетевой интерфейс. Опытные веб-разработчики иногда затрачивают большие усилия на изучение какого-нибудь GUI-инструментария, хотя то, что действительно нужно в их ситуации - это простой CGI-скрипт. Во всяком случае, веб-приложение, предназначенное для работы только в локальной сети, гораздо проще приложения для универсального применения. Вы легко сможете задать требования для браузера, а производительность сервера, скорее всего, не будет большой проблемой. Простые приложения, использующие стандартные виджеты CGI-форм и им подобные, можно написать за гораздо меньшее время, чем потребовалось бы на разработку самодостаточных приложений. Приложения, построенные вокруг обработки форм или данных, часто являются отличными кандидатами на реализацию в качестве обычных веб-сервисов.
Во многих случаях такое специальное приложение может обеспечить изящное и простое решение определенной узкой задачи. Однажды я написал утилиту для просмотра изображений, которая делала лишь одну вещь: просматривала директорию с изображениями с фотоаппарата и давала мне возможность сортировать картинки по категориям. Программа занимала лишь около двадцати строчек на Perl, а в результате работала гораздо быстрее, чем более универсальное решение, не говоря уже о времени, которое потребовалось бы на написание приложения с графическим интерфейсом, выполняющего эту же самую функцию.
итак, чем же нам поможет веб-браузер?
Можно задать совершенно резонный вопрос: ну и что? Что умеет веб-браузер, чего не умеют другие приложения? Ответ очевиден: ничего. Но тогда что же можно написать на высокоуровневых языках, чего нельзя выполнить в машинных кодах? Опять же, ничего. Преимущество при использовании веб- браузера в качестве интерфейса состоит в том, что все жесткое кодирование уже выполнено. Не нужно отслеживать события изменения размеров или развертывания окна или события меню. Все, что нужно сделать, это прочитать фрагмент данных с запросом и обработать его.
Потенциальная слабость разработки основанных на веб-технологиях приложений - недостаточные возможности по управлению их внешним видом. На самом деле, это не такой уж и недостаток. Если вы пытаетесь максимально ускорить разработку приложения, вам точно не стоит тратить много времени на возню с внешним видом. На какой бы платформе вы не работали, пользователям будут знакомы кнопки и текстовые виджеты веб-браузера. Это действительно весьма полезная возможность.
Веб-браузер делает еще одну очень полезную вещь: он предоставляет множество настроек и предпочтений, о которых вам не нужно беспокоиться. Пользователь может изменять размер шрифта во время работы. Аналогично, если сгенерированные выходные данные представляются в простой и аккуратной HTML-форме, их можно легко и быстро вывести на печать. Многие функции, которые в ином случае вам пришлось бы реализовывать самостоятельно (например, сохранение выходных данных в файл, вывод выходных данных на печать, изменение размеров окон), уже реализованы.
однопользовательская архитектура
Хотя может показаться, что однопользовательское окружение полностью устраняет многие проблемы, связанные с разработкой приложений, это не совсем так. Недавно я написал систему для работы с массивом документов как веб-приложение. Поскольку я спроектировал ее для одновременной работы максимум с одним пользователем, я подумал, что смогу обойтись без объемного кода, занимающегося блокировкой файлов, который в ином случае был бы необходим. Я ошибался. В интерфейсе использовались фреймы, и одна из программ проверки работоспособности, запущенных моим оконным менеджером, могла зависнуть намертво, если другая программа изменяла базу данных, пока та была запущена. Браузер часто начинал обрабатывать оба запроса одновременно.
При использовании локального сервера отпадают проблемы с пропускной способностью сети; даже то, что может вызвать проблемы при работе через локальную сеть, будет нормально функционировать на локальной машине, хотя большие файлы по-прежнему будут тормозить браузер или приводить к его зависанию.
сетевая безопасность
Если вы разрабатываете приложение, которое будет запускаться на локальном веб-сервере, задайтесь вопросом, что произойдет, когда кто-то другой будет обращаться к этому приложению по сети. В идеале, убедитесь, что локальное приложение доступно только через внутреннюю компьютерную сеть. Если сделать этого нельзя, потребуются специальные меры безопасности. Возможно, будет достаточно просто указать вашему приложению отвергать все соединения с любых адресов кроме 127.0.0.1, что гораздо безопаснее, чем использовать пароли.
Возможно, разумнее будет просто выбрать альтернативный номер порта, организовав для приложения выделенный сервер. С помощью командной строки UNIX® потребуется несколько минут, чтобы настроить личную копию Apache для работы через порт 8880, и в результате вы получите полный контроль над функциями и настройками сервера. Кроме того, серверная часть приложения будет выполняться с вашими обычными привилегиями, следовательно, не нужно делать важные файлы доступными для записи, что, несомненно, большой плюс.
управление данными
Для традиционного веб-приложения использование сервера баз данных в значительной степени упрощает разработку, так как механизм базы данных - в силу того, что это отдельный сервер - обеспечивает большую часть необходимой сериализации и блокировки, а обеспечить остальное довольно просто. Для однопользовательского приложения, которое будет запускаться на одном компьютере, такая схема избыточна и, возможно, реализовывать ее не стоит. Локальному приложению все это не нужно, зато в нем имеет смысл применить простой и легко перемещаемый файл данных. Куда бы я не путешествовал, я всегда синхронизировал файл данных моего приложения для работы с массивом документов между ноутбуком и десктопом. Это была несложная задача, поскольку я использовал файл Berkeley DB.
Формат Berkeley DB предлагает довольно простое решение, легко реализуемое на многих языках. Так как сервер баз данных не используется, это не лучший выбор для приложения, одновременно работающего с множеством пользователей; в целях безопасности необходимо заблокировать базу данных, выполнить нужные операции, а затем разблокировать ее. С другой стороны, поскольку сервера БД нет, такое решение подходит для программы, очень редко обслуживающей несколько пользователей одновременно.
Вот пример кода на Perl, подключающего базу данных Berkeley DB (с блокировкой):
my %db;
open(DBFILE, '+file.db');
flock(DBFILE, LOCK_EX);
$dbhandle = tie %db, 'DB_File', 'file.db', O_CREAT|O_RDWR, 0666, $DB_HASH;
После выполнения этого кода можно получить доступ к базе данных напрямую, как к стандартному хэшу Perl:
$db{user} = "me";
print "<p>User: $db{user}</p>\n";
Можно также использовать объект dbhandle для выполнения операций над базой данных:
$dbhandle->del("user");
После выполнения всех этих действий работа действительно закончена. То есть, когда ваша программа завершает работу, блокировка автоматически снимается. Блокировка с помощью flock() - всего лишь рекомендация. Она не помешает другим программам осуществлять запись в файл, если они ее не используют. Если ваша программа реализована в виде нескольких взаимосвязанных программ, добавьте код блокировки во все из них, или еще лучше, поместите его в модуль общего пользования. Таким образом, эта необязательная блокировка, тем не менее, обеспечивает необходимый результат: это надежная гарантия, что файлы данных будут изменяться только одной программой одновременно.
Некоторые приложения будут прекрасно работать с обычными файлами, например с CSV-файлами или просто текстовыми файлами с пустыми полями. Другим может потребоваться полнофункциональная SQL-база данных. Вы не обязаны применять решения "корпоративного класса" в небольших приложениях, которые должны быстро и легко выполнять поставленную задачу. Лучше сконцентрируйте усилия на устранении ошибок и создании удобных функций. При этом если вам нужна реляционная база данных - используйте ее.
виджеты интерфейса
Достаточно хорошие приложения получаются при помощи форм, содержащих множество кнопок. Если имеются метаданные или контекст, которые необходимо передавать от страницы к странице, используйте скрытые поля форм; проблемы безопасности, с которыми вы можете столкнуться (пользователи могут обходить их) в данном случае не так уж серьезны. Кнопки с изображениями можно использовать во многих целях, хотя в этом случае вам придется заняться разработкой рисунков. Если вы не хотите этого делать, создайте кнопку с альтернативным текстом, указав для изображения несуществующий URL:
print $q->image_button(-name => "sort",
-value => "$name",
-src => "/nonexistant",
-alt => $label);
Это может показаться нелепым по сравнению с более простой альтернативой использования гиперссылки, но в своей полной форме соответствующая ссылка href=... может быть довольно длинной, и ее будет невозможно вычислить заранее, если кнопка отправляет форму. Это очень грубый трюк, но он работает.
одна задача - одно приложение
Написав пару небольших веб-приложений для запуска на локальной машине, вы быстро заметите следующее: такие приложения часто создаются очень быстро, даже с учетом времени на отладку, а работают они значительно эффективней по сравнению с выполнением стандартных задач вручную. Выигрыш в эффективности, получаемый при упрощении долгих и скучных процедур, с лихвой окупает время на разработку такого приложения, тем более что этот процесс может занять лишь несколько минут.
В стандартной CGI-библиотеке Perl имеется множество полезных базовых рабочих инструментов. Поэкспериментировав с ней пару часов и привыкнув к ее чрезвычайно гибкому и богатому набору функций, вы сможете написать множество разнообразных приложений, не прилагая к этому больших усилий. С помощью несложной скриптовой системы можно удобно и эффективно использовать скрипты командной строки, решающие одну определенную задачу. Простые CGI-скрипты делают то же самое в отношении разнообразных программ с графическим интерфейсом. Посвятив им один день, вы быстро обнаружите, что можете с легкостью выполнять десятки задач, о программах для решения которых вы так мечтали.
масштабирование
К сожалению, типичная практика такова: сначала пишут небольшое простое приложение под конкретную задачу, предназначенное, как полагают, только для локального использования, а затем обнаруживается, что существует очевидная причина расширить это приложение для большей аудитории. Даже если приложение предназначено только для локального использования, пишите понятный код, оснащайте программу средствами контроля и сохраняйте отладочные выходные данные. Хороший код с удобной поддержкой отладки очень быстро окупается невысокими затратами на сопровождение. Если с самого начала в программе была предусмотрена возможность переработки для более широкой аудитории, выгода будет еще больше.
Значительную часть работы может составить рефакторинг, но это не так плохо, как вам, вероятно, кажется. По большей части вам придется заниматься масштабированием самой серверной базы данных или оптимизацией обработки файлов, чтобы обеспечить лучшую и более интеллектуальную блокировку. Если ваша программа занимается обработкой файлов, возможно, стоит написать небольшой сервер, выполняющий эти операции атомарно, обеспечивая таким образом сериализацию. Также для такого сервера будет разумно обрабатывать одного клиента за раз, поскольку отдельные скрипты выполняются быстро.
Главное, на что вам нужно обратить внимание - это программа, делающая предположения о внутреннем состоянии. Например, в программе-классификаторе документов совершенно очевидной представляется функция, отображающая для пользователя "следующий файл", а затем заносящая его в определенную категорию. Если работают два пользователя, необходимо отслеживать, какой файл обрабатывает каждый из них. Также нужен какой-то механизм обработки пограничных ситуаций. Например, если кто-нибудь откроет приложение, а затем в какой-то момент отойдет, то необходимо разрешить другому пользователю просмотреть этот файл. Иногда для этого потребуется существенная переработка всей серверной части, однако в целом будет довольно просто обеспечить стабильность на стороне клиента.
Питер Сибах
Сетевые решения. Статья была опубликована в номере 11 за 2007 год в рубрике программирование