Разработка простых Web-сайтов реального времени на платформе Meteor

Быстрая реализация приложений, управляемых данными, почти мгновенно реагирующих на действия пользователя

Повсеместное распространение Интернета привело к тому, что даже небольшие задержки в доставке данных могут раздражать пользователей. Они хотят получать информацию немедленно. К сожалению, Web-технология не полностью удовлетворяет этот спрос на доступ в режиме реального времени. Доступ к данным быстро стандартизуется в рамках нескольких классов объектно-реляционного отображения (Object-Relational Mapping - ORM), но для связи реального времени подобного решения нет. Эта статья посвящена Meteor, интересной новой JavaScript-среде, нацеленной на решение этой проблемы.

Дэвид Беруби, владелец, Berube Consulting

Фото автораДэвид Беруби (David Berube) ― консультант, докладчик и автор руководств Практикум по Rails Plugins, Практикум по составлению отчетов с помощью Ruby and Rails и Практикум по Ruby Gems. Он пишет статьи для целого ряда журналов, включая Dr. Dobb's Journal и Linux Pro Magazine. Занимается разработкой программного обеспечения и консалтингом для различных клиентов и отраслей.



31.05.2013

Что такое Meteor?

Meteor — это новая JavaScript-платформа, предназначенная для автоматизации и упрощения разработки Web-приложений, действующих в режиме реального времени. Она управляет связью реального времени, используя протокол Distributed Data Protocol (DDP), который поддерживается современными браузерами с помощью WebSockets, а браузерами более ранних версий ― с помощью механизма long polling Asynchronous JavaScript + XML (Ajax). В обоих случаях связь между браузером и сервером остается прозрачной.

Протокол DDP предназначен для работы с коллекциями документов JavaScript Serialized Object Notation (JSON), что позволяет легко создавать, обновлять, удалять, запрашивать и, конечно, просматривать документы JSON. Так как DDP ― это протокол с открытым исходным кодом, он должен работать с любым клиентом или хранилищем данных. По умолчанию он работает с MongoDB.

Фактически, Meteor обеспечивает две базы данных MongoDB: буферную базу данных со стороны клиента и базу данных MongoDB со стороны сервера. Когда пользователь вносит изменения в данные — например, нажав кнопку Сохранить, — код JavaScript, выполняемый в браузере, обновляет соответствующую запись в локальной базе данных MongoDB, а затем делает запрос DDP к серверу. Код обрабатывается немедленно, как будто операция выполнена успешно, потому что ответа сервера ждать не нужно. Тем временем данные на сервере обновляются в фоновом режиме. Если операция на сервере не удалась, или возвращается неожиданный результат, то код JavaScript на стороне клиента немедленно корректирует данные в соответствии с последним ответом сервера. Эта корректировка называется компенсацией задержки и обеспечивает дополнительное ощущение быстродействия у пользователя.

Даже система шаблонов Meteor явно нацелена на упрощение связи в режиме реального времени. На большинстве Web-платформ в код можно легко внедрять язык гипертекстовой разметки (HTML) — или разметки, эквивалентной HTML, такой как HTML Abstraction Markup Language (Haml). Это позволяет легко вставлять в страницы, отправляемые пользователю, динамические значения из базы данных. После этого система должна следить за изменениями в данных и обновлять разметку. Однако система шаблонов в Meteor регистрирует, к каким именно данным обращались через шаблон, и автоматически выполняет обратные вызовы, изменяя этот HTML-код при изменении соответствующих данных, что делает шаблоны реального времени простыми и быстрыми.


Пример: конкурс популярности ссылок

Функциональность шаблонов Meteor значительно облегчает написание широкого спектра приложений реального времени. Например, предположим, что нужно создать сайт, куда пользователи могут добавлять ссылки — то есть единые указатели ресурсов (URL) — и голосовать за них, так чтобы URL, победившие в конкурсе популярности, отображались в верхней части списка. С помощью Meteor легко написать подобное приложение реального времени, в котором пользователи будут видеть 65 голосов других пользователей, как только те их вносят.


Установка Meteor

Чтобы установить Meteor, введите код, показанный в листинге 1, с терминала Linux® или Mac OS® X. Microsoft® Windows® Meteor не поддерживает.

Листинг 1. Установка Meteor
curl https://install.meteor.com > install_meteor.sh
chmod u+x install_meteor.sh
./install_meteor.sh

Теперь можно создать новый проект.


Создание нового проекта

Команда meteor автоматизирует процесс создания нового проекта, обеспечивая все необходимое для работы Meteor. Введите команду, показанную в листинге 2, чтобы создать проект realtime_links.

Листинг 2. Создание проекта Meteor
meteor realtime_links
cd realtime_links

Meteor создаст каталог, содержащий HTML-файл, JavaScript-файл и файл Cascading Style Sheets (CSS). Последний представляет собой стандартный CSS-файл, но первые два нуждаются в пояснении. Полные версии файлов realtime_links.html и realtime_links.js можно загрузить в разделе Загрузка.


Файл realtime_links.html

В листинге 3 приведены заголовок и фрагменты тела файла realtime_links.html.

Листинг 3. Заголовок и фрагменты тела файла realtime_links.html
<head>
<title>Realtime Links Demo</title>
</head>

<body>
  {{> header }}
  {{> link_list }}
  {{> add_new_link }}
</body>

Как видите, начало у HTML-шаблона простое. Не нужно беспокоиться о включении тегов BODY, деклараций DOCTYPE и даже JavaScript- и CSS-файлов. Meteor делает все это сам. За дополнительной информацией о коде CSS и JavaScript в составе Meteor обращайтесь по ссылке на Web-сайт Meteor, приведенной в разделе Ресурсы.

Синтаксис {{> означает «воспроизвести этот шаблон». realtime_links.html воспроизводит три шаблона:

  • header — это простой заголовок, отражающий количество ссылок в базе данных;
  • link_list отображает список ссылок и соответствующие им голоса;
  • add_new_link - это форма для добавления новых ссылок.

Шаблон header приведен в листинге 4.

Листинг 4. Шаблон header из realtime_links.html
<template name="header">
<h1>The Link Collection</h1>

	<p>We currently have {{collection_size}} links.</p>

</template>

Шаблон header просто отрисовывает тег h1 и краткое описание размера коллекции. Метод collection_size определен в JavaScript-файле realtime_links.js (подробнее см. в следующем разделе). Meteor автоматически отмечает, какие элементы данных интерполируются шаблоном. Так что при изменении размера коллекции шаблон header обновляется автоматически.

Обратите внимание, что используемый здесь синтаксис {{ ... } подобен <%= ... %> в Ruby on Rails или <?= ... ?> в PHP. Он может интерполировать произвольный код, что позволяет вставлять любое полезное динамическое выражение.

Шаблон link_list приведен в листинге 5.

Листинг 5. Шаблон link_list из realtime_links.html
<template name=
"link_list">

  <ul>

    {{#each links }}

      <li>  {{> link_detail }} </li>

    {{/each }}

  </ul>

</template>

Как видите, код в листинге 5 ― это список ссылок. Этот список создает метод links из файла JavaScript realtime_links.js. Для каждой ссылки отрисовывается шаблон link_detail. Обратите внимание, что не нужно передавать никакие аргументы, потому что циклы #each Handlebars делают текущий контекст каждой итерации текущим объектом. Другими словами, локальные методы шаблона link_detail корректно интерпретируются как методы каждого объекта ссылки. В листинге 6 демонстрируется шаблон link_detail, который управляет данными, отображаемыми для каждой отдельной ссылки.

Листинг 6. Шаблон link_detail из файла realtime_links.html
<template name="link_detail">
<div id="link-{{id}}">

    <h1>{{url}}</h1>

    <p><strong>Stats:</strong> up: {{thumbs_up}} down: {{thumbs_down}} 
net score: {{score}}</p>
<input type="button" value="Thumbs Up" 
 class="thumbs_up" url="{{url}}" />
    <input type="button" value="Thumbs Down" 
class="thumbs_down" url="{{url}}" />
</div>

</template>

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

В листинге 7 приведен шаблон add_new_link.

Листинг 7. Шаблон add_new_link из файла realtime_links.html
<template name="add_new_link">

  <div id="new_link_form">

    URL: <input id="url">

<input type="button" value="Click" id="add_url" />

  </div>

</template>

Это просто поле ввода текста и кнопка, образующие интерфейс для добавления в список новых URL.


Файл realtime_links.js

Код JavaScript из файла realtime_links.js управляет доступом к данным и событиями обратного вызова из вашей программы, как со стороны клиента, так и на сервере. Оператор if (Meteor.is_client) представляет собой клиентскую часть, а оператор if (Meteor.is_server) ― серверную. Meteor предоставляет возможность защитить уязвимый исходный код от вредоносных клиентов. За более подробной информации обращайтесь по ссылке на документацию Meteor, приведенной в разделе Ресурсы.

Листинг 8 демонстрирует вспомогательные функции заголовка и списка ссылок.

Листинг 8. Вспомогательные функции заголовка и списка ссылок
Template.header.collection_size = function () {
return Links.find({}).count();
	};
	Template.link_list.links = function () {
return Links.find({}, {sort : {score: -1} });
	};

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

Листинг 9 содержит два обратных вызова по событию к шаблону link_detail.

Листинг 9. Обратные вызовы по событию link_detail
	Template.link_detail.events = 
{

'click input.thumbs_up' : function () {
Meteor.call('vote', this.url, 'thumbs_up');
},

  'click input.thumbs_down' : function 
() {Meteor.call('vote', this.url, 'thumbs_down');}

	};

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

В листинге 10 показан обратный вызов по событию для формы, с помощью которой пользователи могут добавлять новые ссылки.

Листинг 10. Обратный вызов по событию формы добавления новой ссылки
  Template.add_new_link.events = {

    'click input#add_url' : 
dfunction () {

var new_url = $('#url').val();

      var url_row = Links.findOne( {url:new_url} );

      if(!url_row){

Links.insert( { url : new_url,
score: 0,
thumbs_up: 0,

thumbs_down: 0 });
      }
Meteor.call('vote', url, 'thumbs_up');

    }
  };

Во-первых, форма пытается найти существующий объект ссылки по запрашиваемому URL. Если находит, то запрос считается голосом, поданным за существующий объект ссылки. В противном случае создается новый объект ссылки и подается голос thumbs_up за этот новый объект.

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

Кроме того, понятно, что в настоящее время Meteor не реализует всех возможностей MongoDB. Например, Meteor не поддерживает функцию MongoDB upserts, которая вставляет новые данные или изменяет старые. Если бы Meteor поддерживал upserts, можно было бы написать функцию, показанную в листинге 11.

Листинг 11. Гипотетическое добавление новой функции обратного вызова по событию формы ввода ссылки с использованием upserts
  Template.add_new_link.events = {

    'click input#add_url' : function () {

      var new_url = $('#url').val();

       Links.update( { url : new_url}, 
                     { $set: {url : new_url}, 
                       $inc: { votes : 1 } } , true );
    }
  };

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

Код, приведенный в листинге 12, выполняется на сервере. Это единственный метод, который можно вызвать посредством кода со стороны клиента. Этот метод, vote, позволяет клиенту голосовать "за" или "против" определенного URL. Он использует оператор Mongo $inc для приращения соответствующего счетчика голосов. Он также увеличивает или уменьшает общую оценку. Метод Meteor.startup позволяет коду выполняться только при запуске сервера. Затем функция Meteor.methods определяет функции, которые можно вызывать на стороне клиента с использованием Meteor.call, как было показано в листинге 9.

Листинг 12. Код метода vote на стороне сервера
if (Meteor.is_server) {
    Meteor.startup(function () {
      Meteor.methods({
        vote: function (url, field){

new_obj = { $inc: { } };

 if(field =='thumbs_up'){
new_obj['$inc']['thumbs_up'] = 1;
 new_obj['$inc']['score'] = 1;
 }else{
new_obj['$inc']['thumbs_down'] = 1;
 new_obj['$inc']['score'] = -1;
                }

                Links.update( { url : url}, new_obj );

              }
      });
    });
}

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


Приложение в действии

Теперь можно запустить наше приложение Meteor, как показано в листинге 13, и увидеть его в действии.

Листинг 13. Запуск системы
meteor

После запуска Meteor работает через порт 3000. Откройте Web-браузер и перейдите по адресу: http://localhost:3000/.

Если ввести URL в поле Add a URL и нажать кнопку Add, вы должны увидеть список URL с количеством баллов one. Затем нажмите кнопку Thumbs Up или Thumbs Down, чтобы голосовать "за" или "против" URL. Это происходит в режиме реального времени, без обновления страницы. Если открыть новое окно Web-браузера, в нем можно делать то же самое, и любые изменения будут мгновенно отражаться в первом окне.

Добавив несколько URL, вы увидите, что вверху отображается тот, у которого наибольшее количество баллов — определяемых как разность между голосами "за" и "против". Если голосовать "за" или "против", URL будут перемещаться по списку вверх или вниз в зависимости от изменения рейтинга. Это происходит в режиме реального времени, и множество пользователей с разными браузерами будут видеть в процессе голосования одни и те же данные.


Заключение

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


Загрузка

ОписаниеИмяРазмер
Исходный код примеров для этой статьиrealtime_links.zip2 KБ

Ресурсы

Научиться

Получить продукты и технологии

  • Meteor: простая в эксплуатации и мощная платформа Web-приложений реального времени.
  • MongoDB: превосходная документно-ориентированная база данных.
  • CoffeScript: мощное расширение JavaScript с плагином Meteor, так что его можно использовать в своих приложениях Meteor.

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


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

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Web-архитектура, Open source
ArticleID=932161
ArticleTitle=Разработка простых Web-сайтов реального времени на платформе Meteor
publish-date=05312013