Sunday, November 18, 2012

Разработка real-time ASP.NET приложений с помощью SignalR

SignalR, на мой взгляд – одна из самых впечатляющих библиотек в ASP.NET, появившихся за последние пару лет. Я бы сказал, настоящее событие, своеобразная веха. И пока команда корпит над готовящимся релизом первой версии, самое время сделать ее небольшое описание. Однако прежде чем начать, стоит немного остановиться на том, зачем вообще кто-то придумывал эту библиотеку и какую проблему она решает.

Real-time веб приложения

Наверняка многие пользовались Facebook, Twitter или другими социальными сервисами и замечали, что когда приходят обновления новостей или статусов, страница обновляется в реальном времени и вы постоянно видите последнюю актуальную информацию. В Google Docs можно совместно редактировать документы, и все обновления синхронизированы и появляются у вас сразу же, как только их сделает другой пользователь. Это примеры сервисов, которые можно назвать real-time веб-приложениями. Основной их отличительной способностью является то, что они “ломают” привычную нам всем модель работы в вебе – запрос-ответ, благодаря чему пользователи видят обновление данных сразу же, как только они появляются на сервере.

Если не знать, насколько далеко вперед шагнули технологии за последние несколько лет, то можно предположить, что все подобные приложения реализованы при помощи периодического опрашивания сервера обычными Ajax-запросами, т.н. polling. Выглядит похоже, но это не так, в чем можно легко убедиться, просмотрев логи запросов из браузера на сервер в Firebug, Fiddler или Chrome developer tools.

Способы реализации

Итак, какие же есть способы реализации подобной функциональности, их достоинства и недостатки.

Техника Описание Преимущества Недостатки
Polling Постоянный опрос сервера Ajax-запросами + простота реализации
+ поддержка во всех современных браузерах
- задержка в результатах
- при уменьшении задержки существенно увеличивается нагрузка на сервер
Long Polling Ajax-запросы, идущие один за другим, но каждый запрос держится открытым в течение нескольких минут + сниженная нагрузка на сервер по сравнению с обычным Polling
+ уменьшенный трафик
+ поддержка во всех современных браузерах
- больше одновременно открытых соединений, т.к. каждый запрос живет дольше
Server-Sent Events Новый стандарт HTML5, работающий поверх HTTP. Позволяет создавать долгоживущее соединение с сервером, чтобы сервер мог отправлять данные на клиент + нет необходимости постоянно пересоединяться с сервером
+ нет изменений на стороне сервера, поэтому работает на всех современных веб-серверах
- не поддерживается в IE (даже в IE10)
- работает только в направлении сервер –> клиент (на сервер можно отправлять обычные Ajax запросы)
WebSockets Новый протокол (ws:// и wss://), работающий поверх TCP на одном уровне с HTTP. Позволяет создавать двустороннее долгоживущее соединение с клиентом + нет необходимости постоянно пересоединяться с сервером
+ работает в двустороннем режиме
- поддерживается не во всех веб-серверах (IIS8)
- поддерживается не во всех браузерах (в IE7-9, Android)

Для большей информации о Server-Sent Events и WebSockets советую посмотреть статью на HTML5 Rocks: http://www.html5rocks.com/en/tutorials/eventsource/basics/

Если посмотреть на достоинства и недостатки, то можно увидеть, что самый эффективный вариант – это WebSockets, но он не поддерживается во всех браузерах и будет поддерживаться лишь на IIS8 и выше. Server-Sent Events работает на более старых версиях IIS, но не поддерживается в IE, поэтому тоже подойдет далеко не всегда. Long Polling работает везде, но при этом далеко не так эффективен.

Идеальным решением было бы совмещение этих техник в разных случаях, но это сложно реализовать и поддерживать. Однако есть хорошая новость – это решение уже реализовано в библиотеке SignalR.

Введение в SignalR

SignalR – это библиотека для создания многопользовательских real-time ASP.NET (и не только) приложений. Она состоит из набора серверных и клиентских библиотек, и представляет собой абстракцию над целым набором транспортов. Все это добро – open source, лежащий на GitHub: https://github.com/SignalR, поэтому вы всегда можете пойти и посмотреть, что там внутри, какие есть баги и т.д. Кроме того, в разделе Wiki есть много информации о SignalR и примеров: https://github.com/SignalR/SignalR/wiki

SignalR был придуман и реализован двумя разработчиками Microsoft: Damian Edwards и David Fowler. В своих твиттерах они часто пишут полезную информацию о SignalR и сообщают о новостях. Также David ведет блог, в котором описывает все изменения в новых версиях. Если вы решите использовать библиотеку, то советую подписаться.

На момент написания этой статьи SignalR находится в предрелизном состоянии (версия 1.0 alpha2). Еще планируются один или несколько RC, после чего продукт будет выпущен в RTM.

Транспорты

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

image

То есть если сервер и клиент поддерживают WebSockets, то будет установлено WebSockets-соединение и все будут счастливы. Если же нет – то далее будет проверка, поддерживает ли клиент SSE, и если да – будет установлено это соединение. В случае IE сразу же будет испробован подход Forever Frame (невидимо висящий iframe устанавливает соединение и получает JS-инструкции с сервера) – это IE-хак, т.к. даже 10-я версия IE не поддерживает SSE. Молодцы, нечего сказать.

И в конце-концов, если клиент не поддерживает ни один из этих способов, или произошла ошибка, то SignalR откатится до самого надежного способа – Long Polling, который работает практически везде.

Архитектура

Архитектура SignalR очень проста. На сервере реализованы 2 вида API: низкоуровневый (PersistentConnection API) и высокоуровневый (Hub API), причем Hub опирается на PersistentConnection. Вы можете использовать любой из них, но в большинстве случаев вам будет достаточно возможностей, предоставляемых Hub API.

image

В альфе официально поддерживаются JavaScript (браузер), .NET 4 и WinRT клиенты. Windows Phone и Silverlight были в предыдущей версии, но в альфу не попали. Их обещают допилить ближе к релизу.

Также в альфу не попала библиотека для self hosting. Вместо нее обещают поддержку http://owin.org/.

Простое SignalR приложение

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

1) Открываем VS и создаем ASP.NET MVC приложение

2) Инсталлируем пакет Microsoft.AspNet.SignalR через NuGet:

Install-Package Microsoft.AspNet.SignalR

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

После инсталляции вы увидите несколько изменений в проекте:

  1. В References добавятся библиотеки SignalR
  2. В Scripts добавятся библиотеки jQuery.SignalR
  3. В папке App_Start появится класс RegisterHubs, который занимается регистрацией рута ~/signalr/hubs

3) Создаем класс MoveHub в папке Controllers (может быть и другая папка, например, Hubs, но хабы – тоже своеобразные контроллеры):

using Microsoft.AspNet.SignalR.Hubs;

namespace SignalR_test.Controllers
{
	public class MoveHub : Hub
	{
		public void MoveShape(int x, int y)
		{
			Clients.All.shapeMoved(Context.ConnectionId, x, y);
		}
	}
}

Обращение Clients.All обозначает, что мы хотим отправить сообщение всем клиентам, которые подписались на события, происходящие в хабе. Метод MoveShape будет вызываться с клиента и вызывать метод shapeMoved во всех браузерах, открывших страницу с параметрами x, y (положение квадрата) и Context.ConnectionId (уникальный идентификатор клиента, отправившего запрос на сервер). Объект Clients.All (и другие) – dynamic, поэтому мы можем вызывать любые методы с любыми параметрами без опасения ошибок компиляции. В то же время это обозначает и то, что нам нужно быть предельно внимательными с именами методов и параметров, включая регистр – никакой проверки компиляции здесь нет.

4) Добавляем метод контроллера, который будет отображать нашу страничку Shape.cshtml:

public class HomeController : Controller
{
	...
	
	public ActionResult Shape()
	{
		return View();
	}
}

Здесь все просто, комментировать нечего.

5) Добавляем Shape.cshtml view, в котором ссылаемся на несколько JS файлов.






Сначала подключаем jQuery и jQuery.UI (для реализации draggable), затем библиотеку jQuery.SignalR, и в конце – обращение к JS-файлу с хабами. Это то место, где происходит вся магия SignalR. Файл генерируется на лету инфраструктурой библиотеки на основании всех хабов, которые есть в вашем коде. Если заглянуть в этот файл из браузера, то можно увидеть, как в нем регистрируются объекты и методы, использующиеся впоследствии из JavaScript-кода.

6) Кладем во view кусок JS-кода. В настоящем приложении JS-код лучше вынести в отдельный файл, но для наших целей и такого кода достаточно.


В JavaScript мы делаем следующее:

  1. Регистрируем метод shapeMoved, который будет “вызываться” из серверного кода. В методе двигаем фигуру, если мы сами не являемся источником запроса
  2. Стартуем соединение с сервером
  3. По успешному подключению регистрируем обработчик события draggable у фигуры, который будет вызывать серверный метод moveShape, находящийся в нашем хабе

7) Запускаем приложение, открываем страницу в двух разных браузерах (или на двух разных машинах), и смотрим, как при движении фигуры в одном из браузеров, она автоматически двигается в другом.

Группы

Очень полезной возможностью SignalR является поддержка групп. Группы используются для объединения подключений и последующего их группового использования. Например, когда у вас есть разные объекты (комнаты чатов, аукционы и т.д.) и необходимо, чтобы оповещения получали лишь те пользователи, которые просматривают именно этот объект.

С группами очень просто работать:

// Add connection to group "foo"
Groups.Add(Context.ConnectionId, "foo");

// Call send on everyone in group "foo"
Clients.Group("foo").send(message);

// Call send on everyone else but the caller in group "foo"
Clients.OthersInGroup("foo").send(message);

// Call send on everyone in "foo" excluding the specified connection ids
Clients.Group("foo", Context.ConnectionId).send(message);

Другие возможности SignalR

В целом, SignalR реализует следующие сценарии:

  1. клиент вызывает метод на сервере
  2. сервер вызывает метод на клиенте/клиентах
  3. передача состояния с клиента на сервер и обратно
  4. поддержка передачи сложных объектов (JSON сериализация)
  5. определение соединения, отсоединения и пересоединения клиентов
  6. обращение к клиентам извне хаба при помощи специального интерфейса (то есть любой код на сервере может оповестить клиенты о событии)
  7. асинхронные сценарии

В официальной документации можно посмотреть более подробное описание всех API и их возможности, как на сервере, так и на клиентах:

Веб-ферма

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

Разработчики позаботились об этом и на данном этапе предлагают 3 решения:

  1. Поддержка Windows Azure Service Bus (идеально подходит для развертывания в Azure)
  2. Поддержка Service Bus for Windows Server
  3. Поддержка Redis

Если у вас не Azure, то остается два последних варианта. Из них лично я бы посоветовал Redis, т.к. он очень прост в установке и конфигурировании (его почти нет), в то время как Service Bus for Windows Server потребует от вас недюженных усилий.

Redis – это key-value storage, изначально разработанный для Linux, но под Windows существует несколько портов. Официально поддерживающийся порт и его настройка описаны в документации SignalR. Неофициальный, но по всей видимости более взрослый порт можно найти здесь: https://github.com/dmajkic/redis. Решение, как запустить его как Windows Service, описано здесь: https://github.com/kcherenkov/redis-windows-service.

Со временем обещают поддержку и других шин.

Полезные ссылки

Если вы хотите посмотреть пример приложения на SignalR, то вы можете скачать пример приложения, показывающего обновляющиеся биржевые котировки с GitHub (https://github.com/SignalR/SignalR-StockTicker), либо зайти на http://jabbr.net и попробовать вживую пример, реализующий чат. Кстати, на Jabbr в комнате, посвященной SignalR довольно часто бывает David Fowler, и у него можно спросить любой вопрос, который вас интересует.

Спасибо за внимание и успешной разработки!

9 comments:

  1. Все круто, хотелось бы побольше про веб-фермы узнать, был ли опыт нормального использования? Так у меня были проблемы с балансером, в частности haproxy.

    ReplyDelete
    Replies
    1. Да, у нас все крутится на ферме, используем Redis в качестве хранилища.
      Какие проблемы с балансером были?

      Delete
    2. This comment has been removed by the author.

      Delete
    3. Проблемы как мне кажется были в том что я:
      а) использовал лонг-полинг и haproxy (возможно как-то не так его настроил)
      б) использовал довольно раннюю версию, до выхода 0.5й, в которой они вроде как многое для webfarm-ов пофиксили.

      А какой у вас балансер?

      PS
      До решения проблемы так и не докопался, появились более приоритетные задачи.

      Delete
    4. Да, они сделали поддержку веб ферм только в 0.5 вроде бы, поэтому и не получалось.
      На продакшене какая-то железяка стоит (точно не скажу), на тесте - софтварный над IIS.
      Думаю, теперь у вас получится решить проблему без особых напрягов :)

      Delete
  2. Консоли от корпорации Microsoft не сразу завоевали всемирную известность и доверие игроков. 1-ая консоль под названием Xbox, вышедшая в далеком 2001 году, значительно уступала PlayStation 2 по количеству проданных приставок. Однако все поменялось с выходом Xbox 360 - консоли седьмого поколения, которая стала по-настоящему "народной" для жителей России и стран СНГ - http://ru-xbox.ru/load/1/igry_xbox_360_kinect/11. Интернет-сайт Ru-Xbox.Ru является популярным ресурсом среди поклонников приставки, поскольку он предлагает игры для Xbox 360, которые поддерживают все существующие версии прошивок - совсем бесплатно! Для чего играть на оригинальном железе, в случае если есть эмуляторы? Для Xbox 360 игры выходили длительное время и представлены как посредственными проектами, так и хитами, многие из которых даже сейчас остаются эксклюзивными для это консоли. Некие гости, желающие сыграть в игры для Xbox 360, смогут задать вопрос: зачем нужны игры для прошитых Xbox 360 freeboot либо различными версиями LT, если имеется эмулятор? Рабочий эмулятор Xbox 360 хоть и существует, однако он просит производительного ПК, для покупки которого будет нужно вложить существенную сумму. К тому же, разнообразные артефакты в виде исчезающих текстур, недостатка некоторых графических эффектов и освещения - могут изрядно испортить впечатления об игре и отбить желание для ее дальнейшего прохождения. Что предлагает этот портал? Наш сайт полностью приурочен к играм для приставки Xbox 360. У нас можно совершенно бесплатно и без регистрации скачать игры на Xbox 360 через торрент для следующих версий прошивок консоли: - FreeBoot; - LT 3.0; - LT 2.0; - LT 1.9. Каждая прошивка имеет свои особенности обхода интегрированной защиты. Потому, для запуска той либо прочей игры будет нужно загрузить специальную ее версию, которая вполне приспособлена под одну из четырех вышеперечисленных прошивок. На нашем интернет-сайте можно без труда подобрать желаемый проект под подходящую прошивку, поскольку возле каждой игры присутствует название версии (FreeBoot, LT 3.0/2.0/1.9), под которую она адаптирована. Пользователям данного ресурса доступна особая категория игр для 360-го, созданных для Kinect - специального дополнения, которое считывает все движения одного или нескольких игроков, и позволяет управлять с помощью их компьютерными персонажами. Большой выбор ПО Кроме возможности скачать игры на Xbox 360 Freeboot либо LT различных версий, тут вы можете подобрать программное обеспечение для консоли от Майкрософт: - всевозможные версии Dashboard, которые позволяют кастомизировать интерфейс консоли под свои нужды, сделав его более удобным и современным; - браузеры; - просмотрщики файлов; - сохранения для игр; - темы для консоли; - программы, для конвертации образов и записи их на диск. Кроме перечисленного выше игры на Xbox 360 Freeboot вы можете запускать не с дисковых, а с USB и других носителей, используя программу x360key, которую вы можете достать на нашем портале. Гостям доступно огромное количество полезных статей, а помимо этого форум, где можно пообщаться с единомышленниками либо попросить совета у более опытных владельцев консоли.

    ReplyDelete
  3. Your car might be stolen if you don't keep this in mind!

    Imagine that your car was taken! When you visit the police, they inquire about a specific "VIN search"

    A VIN decoder is what?

    Similar to a passport, the "VIN decoder" allows you to find out the date of the car's birth and the identity of its "parent" (manufacturing facility). You can also figure out:

    1.Type of engine

    2.Model of a car

    3.The DMV and the limitations it imposes

    4.Number of drivers in this vehicle

    The location of the car will be visible to you, and keeping in mind the code ensures your safety. The code can be checked in the database online. The VIN is situated on various parts of the car to make it harder for thieves to steal, such as the first person sitting on the floor, the frame (often in trucks and SUVs), the spar, and other areas.

    What happens if the VIN is harmed on purpose?

    There are numerous circumstances that can result in VIN damage, but failing to have one will have unpleasant repercussions because it is illegal to intentionally harm a VIN in order to avoid going to jail or the police. You could receive a fine of up to 80,000 rubles and spend two years in prison. You might be stopped on the road by a teacher.

    Conclusion.

    The VIN decoder may help to save your car from theft. But where can you check the car reality? This is why we exist– VIN decoders!

    ReplyDelete