Pull to refresh

Ajax-машина

Reading time 11 min
Views 4K
Знали ли разработчики LiveScript из Netscape о том, какое влияние окажет их язык, позже переименованный в JavaScript, на лицо интернета — веб-сайты, или, может быть, они твердо на это рассчитывали, но факт остается фактом — JavaScript на сегодняшний день является одним из самых важных и распространенных языков в мире.

В то время, как на стороне сервера трудится разношерстная компания языков в лице php, perl, python, ruby, .net, java и многих других, на стороне клиента JavaScript существует буквально в гордом одиночестве. Фактически, из маленького дополнения к html, JavaScript превратился в полноправного члена банды «html+css+js», который, зачастую, уже командует остальными, сверкая золотым браслетом с надписью «ajax» и делая недовольное лицо $( ) при случае.

Данный материал не является попыткой объять необъятное, скорее, это попытка хорошенько необъятное полапать.

О чем статья и для кого она предназначена


В этой статье мною была предпринята попытка описать комплексный способ построения Rich Internet Applications с помощью JavaScript — паттерн «AJAX-машина». Целевая аудитория статьи — разработчики RIA и сочувствующие им программисты и менеджеры. На сегодняшний день потребность в Rich Internet Applications возникает в самых разных областях веб-разработки: браузерные игры, персональные менеджеры, системамы товаро- и документооборота, системы управления бизнес-процессами — т.е. любое, веб-приложение, требующее сложной логики на строне клиента. В RIA-виде зачастую переживают реинкранацию даже традиционно настольные приложения: почтовые клиенты, IM, всевозможные редакторы и даже операционные системы!

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

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

Итак, с позволения читателя, приступим!

Нужен ли JavaScript для веба?



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

Очевиден тот факт, что js по умолчанию считается поддерживаемым на компьютере, если там присутствует полноценный графический браузер. На сегодняшний момент — это огромное число компьютеров. Однако есть ли у js шансы на признание со стороны крупных компаний, как у языка зрелого и, в полной мере, достаточного для написания RIA? Предлагаю читателю несколько аргументов:

Кому нужен JavaScript?



Adobe AIR
Adobe AIR logo AIR (Adobe Integrated Runtime) — это среда для запуска приложений, позволяющая использовать HTML/CSS/JavaScript, Adobe Flash и Adobe Flex для переноса веб-программ (RIA) на настольные ПК. AIR-приложения могут обмениваться информацией с интернетом, использовать некоторые особенности ОС, на которой запускаются (к примеру работать с треем/доком), а так же хранить данные локально, с помощью БД SQLite. По сути же, AIR представляет собой браузер на основе движка WebKit, со специфичными возможностями, доступными через JavaScript-API. Поддерживаемые платформы: Microsoft Windows NT, Mac OS X, альфа-версия для GNU/Linux.
iPhone
iPhone iPhone — семейство мобильных телефонов, работающих на модифицированной ОС MacOS X. Первоначально фирма создатель — Apple — не позволяла сторонним разработчикам писать нативные приложения для iPhone, предлагая создавать оптимизированные веб-приложения(html+css+js). Лишь спустя некоторое время вышел iPhone SDK, позволивший писать полноценные «десктопные» программы, однако возможность создавать веб-приложения никуда не исчезла, и по сей день актуальна.
Google Gears
Google Gears logo Google Gears — открытая разработка от Google, позволяющая использование веб-приложений в режиме оффлайн. Поддерживаются все популярные браузеры:, Firefox, Internet Explorer, Safari (сомнения возникают с Оперой, но они обещали) и, само-собой, Chrome. Gears представляет собой специальный плагин, который заставляет браузер работать с локальным кэшем страниц (на основе SQLite), периодически синхронизируя кэш с онлайн источником. Мостик между Gears и клиентским кодом перекинут с помощью JavaScript.
Mozilla Prizm
Mozilla Prizm logo Mozilla Prizm — разработка компании Mozilla, использующая движок Firefox. Если коротко, то Призма, в основе, позволяет делать две вещи — создавать на рабочем столе ярлыки веб-сайтов и открывать их в независимом окружении (отдельный процесс браузера, со скрытыми системными меню и т.д.), как будто это программа на вашем локальном ПК. В связке с Google Gears способна составить конкуренцию AIR от Adobe, или даже наоборот, дать фору, потому что у FF+GG получается кроссплатформенное решение — 100% поддержка Linux, MacOS и Windows — да еще и с открытым кодом.
Google V8
Google Chrome logo V8 представляет собой высокопроизводительный интерпретатор JavaScript, реализующий стандарт ECMA-262. Работает на Windows XP, Vista, Mac OS X 10.5 (Leopard) и Linux-системах на процессорах IA-32 и ARM. Hint! V8 реализован на C++ и может использоваться как отдельный интерпретатор или как встроенный в любое приложение на C++. Всем известно, что V8 встроен в Chrome по умолчанию. Заявляется, что интерпретатор очень быстрый благодаря трансляции кода в инструкции процессора «на лету» (JIT).
TraceMonkey
FireFox logo TraceMonkey — Новый быстрый движок JavaScript для Firefox, так же основанный на принципе JIT. Работает на процессорах x86, x86-64, и ARM — к слову, как и V8, он может полноценно работать и на некоторых типах мобильных устройств. По всем признакам, появится в FF к концу года, хотя попробовать в деле его можно прямо сейчас.


Что умеет JavaScript?



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

Функция может принимать произвольное количество аргументов
function add(){
  var sum = 0;
  
  for(var i = 0; i < arguments.length; i++){
     sum+= arguments[i];
  }

  return sum;
}

add(1,1,1,1,1); // вернет 5
add(2, 3);   // вернет 5


Функцию можно вызвать и без аргументов совсем
function sayMyName(name){
  name = name ||'неизвестно';
  return 'Мое имя: ' + name;
}

sayMyName('Дэвид'); // вернет 'Мое имя: Дэвид'
sayMyName();    // вернет 'Мое имя: неизвестно'


Функция, как объект первого класса
function add(a, b){
  return a + b;
}

function sub(a, b){
  return a — b;
}

function doAction(action){
   return action(2, 1);
}

doAction(add); // вернет 3
doAction(sub); // вернет 1


Замыкания
function someAction(){
  var firstAction = function(){
    return 'Hello,';
  }

  var secondAction = function(){
    return firstAction() + ' Habr!';
  }

  return secondAction;
}

var act = someAction();
act(); // вернет 'Hello, Habr!'


ООП с нюансами Спасибо Iskin
function Food(type){
  this.type = type;
}

var foo = new Food('orange soda');
var bar = new Food('cheeses');

foo.type; // вернет 'orangeSoda'
bar.type; // вернет 'cheeses'


Динамическое изменение объектов
function Food(type){
  this.type = type;
}

var foo = new Food('orange soda');
var bar = new Food('cheeses');

Food.prototype.say = function(){
  return 'ORLY! This is ' + this.type + '!';
}

foo.say(); // вернет ORLY! This is orange soda!
bar.say(); // вернет ORLY! This is cheeses!


Для языка существует большое количество качественных свободных библиотек, поддерживаемых множеством людей по всему миру, из самых популярных можно выделить: JQuery, Prototype, Mootools, ExtJs, Yahoo UI. Любая из этих библиотек прямо сейчас готова стать основой для написания пользовательского интерфейса RIA. Извиняюсь перед читателем за отсутствие описания, к сожалению, это большая тема для отдельной статьи, поэтому привожу только ссылки.

Надеюсь, эти аргументы достаточно убедительны для того, что бы считать js признанным, полноценным и удобным языком, подходящим для написания RI приложений. А теперь, если дорогой читатель не уснул, я перехожу описанию того, ради чего и затевал статью.

AJAX-машина


Концепция паттерна: Всю логику храним на сервере, весь интерфейс формируем у клиента. Приложение, созданное по паттерну, реализует трёхзвенную архитектуру. Загрузка приложения (всего кода html, css и js происходит один раз, при первом обращении. Спасибо shiko_1st и jodaka



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

После загрузки каркасной dom-модели документа, JavaScript отрисовывает интерфейс; кроме того, js заведует коммуникациями с внешним миром, посредством ajax-запросов. В свою очередь, сервер занимается только выдачей статики и обработкой ajax-запросов от клиента. В идеале, сервер не должен уметь писать в поток вывода ничего, кроме JSON.

Предложенная архитектура изображена с помощью следующей схемы (изображена моими скромными силами, да простит меня читатель):
простая схема

На схеме наличествуют 3 уровня, соответственно:
  1. Зеленый или Клиентский уровень. На этом уровне приложение создает пользовательский интерфейс в браузере, обрабатывает действия пользователя и посылает запросы на сервер. Для этого уровня работают художники, дизайнеры и js-программисты. Так же, на зеленом уровне может быть и другой сервер, в случае, если вы открыли API для межсерверного взаимодействия.
  2. Красный или Уровень протокола. Красный цвет символизирует критичный участок в разработке AJAX-машины — хороший формат обмена данными позволит сделать клиент и сервер независимыми друг от друга, легко распределять нагрузку на несколько серверов, добавлять новые возможности, без переделок архитектуры. Красный уровень обеспечивается http(s) транспортом, обмен идет в виде JSON-пакетов. JSON выбран не зря: во-первых, он более компактный по размеру передаваемых данных, по сравнению с чистым XML, а во-вторых, это родной формат данных для js.
  3. Синий или Уровень сервера. Здесь трудятся «серверные» языки, так же, как и программисты на этих языках :) На схеме была предпринята попытка изобразить общий вид масштабируемого решения, с роутером запросов или (и) балансировщиком нагрузки и несколькими серверами. Роутер — это входная точка (Front Controller) сервера, так же, он должен отдавать статику. Синий уровень является самым гибким с точки зрения архитектуры, уровнем AJAX-машины. К примеру, здесь можно поставить один очень мощный сервер с FastCGI или же два сервера с php, разделив между ними функционал, или вообще, десяток одинаковых по функционалу серверов с Ruby. Для сервера можно выбрать любой язык или даже несколько разных языков, удовлетворяющих поставленным задачам, с учетом того, что от них потребуется выводить только ответы в виде JSON и динамическую графику, если в ней есть необходимость.


Аспекты


В лучших традициях велосипедостроения, все еще не потерявшему интерес читателю можно предложить хороший способ модульного деления логики — аспекты. Аспект — это логически неделимый процесс, происходящий с вашим приложением. Что бы прекратить заумные формулировки, приведу примеры аспектов: «Регистрация», «Новости», «Авторизация», «Сообщения». В идеальном случае, один аспект не должен ничего знать об остальных, взаимодействуя только с БД или файловой системой напрямую. Грамотно спроектированная структура аспектов и связей между ними позволяет разнести самые «тяжелые» по производительности аспекты на разные сервера, или(и) выделить одному аспекту несколько серверов с балансировкой нагрузки между ними.

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

Базы данных


Все современные СУБД позволяют писать и выполнять хранимые процедуры, и разработчику крупной системы противопоказано пренебрегать этой возможностью. Выработав четкие правила именования процедур и аргументов, можно смело перекладывать все тяжелые запросы на саму СУБД. Совершенно не рационально и крайне расточительно получать от сервера БД огромную выборку с тем, что бы, после дополнительной обработки, передать клиенту три значения. Хочу уверить уважаемого читателя, не знакомого с прелестями хранимых процедур в том, что:
  • во-первых, в написании таких процедур нет ничего сложного;
  • во-вторых, они дают, зачастую, гораздо большую свободу для манипуляций данными, чем диалект SQL для конкретной СУБД;
  • в-третьих, позволяют осуществлять черновую обработку и отсеивание данных на сервере БД, не повышая трафик между, собственно, сервером БД и сервером приложения;
  • в-четвертых, значительно проще вызвать одну процедуру, чем запрос на 400 строк с различным подстановками параметров (печальный случай из практики).


Сценарий работы Ajax-машины


JavaScript рисует для пользователя форму авторизации, пользователь вводит логин и пароль, на сервер отправляется JSON-пакет вида:
{
   action: 'authorization',
   status: 'request',
   options: {
      login: 'Vasiliy',
      pass: '********'
   }
}


Пакет следует через сеть к роутеру, который, как минимум, проверяет пакет на минимальный и максимальный допустимые размеры и на количество запросов от узла в единицу времени (что бы исключить перебор). В случае, если все хорошо, переправляет данные аспекту «Авторизация» (это может быть модуль, класс, программа, отдельный сервер и еще что-нибудь), к примеру, в очередь, в виде:
{
   action: 'authorization',
   status: 'in_queue',
   options: {
      login: 'Vasiliy',
      pass: '********'
   },
   date_in: '25082008-221500'
}


Аспект принимает данные, и устраивает свою проверку, на соответствие полей пакета эталонным. Если все верно, он авторизует пользователя Vasiliy с паролем ********, и отправляет следующий пакет клиенту:
{
   action: 'authorization',
   status: 'authorize',
   options: {
      user_name: 'Vasiliy Pomidorov',
      permission: 'super_user'
   },
   date_in: '25082008-221500',
   date_out: '25082008-221501'
}

Js на стороне клиента получает пакет, выводит какое-нибудь банальное сообщение в виде: «Добро пожаловать, Vasiliy Pomidorov» и разблокирует дополнительные действия для super_user'а.

Все этапы строго логируются. В случае некритичных проблем с пакетом, как-то: неверный пароль/логин, недоступность какого-то действия для данного пользователя и т.д., клиенту отправляется информационный пакет, оповещающий об ошибке:
{
   action: 'authorization',
   status: 'deny',
   options: {
      code: 99 //неверный логин или пароль
   },
   date_in: '25082008-221500',
   date_out: '25082008-221501'   
}

В случае критичной проблемы, к примеру: флуд пакетами, постоянные разнообразные ошибочные запросы от узла и т.д., следует принять более жесткие меры: вынести предупреждение или вовсе заблокировать авторизованного пользователя до выяснения обстоятельств, в крайних случаях применимо блокирование по ip и маске подсети (в случае DoS/DDoS атак).

Эпилог


Прежде всего, благодарю тебя, читатель, сумевшего пройти через ряды немного занудного, но, надеюсь, полезного текста — Спасибо!

Хотелось бы быть уверенным в том, что кому-то данный материал действительно поможет сформулировать свои мысли, других подтолкнет на путь разработчиков Rich Internet Applications, а может даже, повлияет на создателя гениального и ужасно уникального стратапа (тешу себя надеждой :)

Очень жаль, но за рамками статьи осталось много интересных материалов: тут и сравнение по пунктикам JavaScript и Adobe Flash, плюсы и минусы «больших» веб-фреймворков для создания RIA, лавирование между SOAP и REST, задачи и проблемы процесса создания системы скриптов для автоматического тестирования и разворачивания клиентского js-кода, централизованное использование микроформатов…
Много, ох как много интересных тем можно осветить, описать, откомментировать, отбросить за ненадобностью или принять на вооружение, отхоливарить, в конце-концов, на страницах Хабрахабра :) Дело за тобой, уважаемый читатель!
Tags:
Hubs:
+120
Comments 91
Comments Comments 91

Articles