...

вторник, 22 апреля 2014 г.

[Из песочницы] Дуплексный асинхронный обмен данными для веба, мобайла и десктопа в одной реализации

Для отправки данных с сервера на клиент, не важно будь это веб, мобайл или десктоп существует достаточное количество техник. Но проблема в том что они все разные и если нужно реализовать оповещение об одном и том же событии для клиентов на всех основных платформах, то придется дублировать этот самый код оповещения. Вот поэтому я хочу поделится с сообществом своей практикой по работе с одним продуктом под названием LightStreamer.



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

Итак, LightStreamer это сервер работающий на Java и как следствие может быть запущен как на Windows так и Linux машинах + если разработка ведется на амазоне, то можно использовать готовый Amazon Machine Image с LightStreamer на борту download.lightstreamer.com.

Установочный пакет (фактически заархивированная папка) уже содержит демо лицензию, ограничения которой сводятся лишь к 20 одновременно подключенных клиентов, что, согласитесь, для разработки вполне достаточно, но если нет — можно заказать evaluation без ограничений на 60 дней. Вот список доступных лицензий.


Инструкции по установке изложены в /Lightstreamer/GETTING_STARTED.TXT который находится внутри архива. Все предельно просто (опишу для Windows):

— распаковываем, желательно в корень диска потому что архив содержит достаточно длинные пути к файлам;

— указываем TCP порты на котором будет висеть сервер в /Lightstreamer/conf/lightstreamer_conf.xml, по умолчанию 8080 и 8888;

— в /Lightstreamer/bin/windows/LS.bat в переменной JAVA_HOME указываем где установлен JDK (если не установлен — установите);

— и запускаем сервер /Lightstreamer/bin/windows/Start_LS_as_Application.bat.

Для теста открываем http://localhost:8080 (порт указываем тот же что был указан в /Lightstreamer/conf/lightstreamer_conf.xml) и должны увидеть страницу приветствия с ссылками на преинсталированые демки.

Так же есть возможность вместо HTTP использовать HTTPS и кластеризацию с лоадбалансером


Что ж, сервер установлен, но толку мало — нужно научить его обрабатывать наши запросы и давать ответы. Для этого существует механизм адаптеров (что то на подобии плагинов). То есть, нам нужно написать адаптер, который будет обрабатывать запросы с наших клиентов и дать знать самому серверу что такой адаптер существует что бы он направлял запросы на него.


Написание адаптера



В папке /Lightstreamer/DOCS-SDKs/ находятся SDK. Для написания адаптера на .NET нужно реализовать интерфейс Lightstreamer.Interfaces.Data.IDataProvider из /Lightstreamer/DOCS-SDKs/sdk_adapter_dotnet/lib/DotNetAdapter_N2.dll

using System;
using System.Collections;
using System.Threading;
using Lightstreamer.Interfaces.Data;
public class HelloWorldAdapter : IDataProvider
{
private IItemEventListener _listener;
private volatile bool go;
public void Init(IDictionary parameters, string configFile) { }
public bool IsSnapshotAvailable(string itemName) { return false; }
public void SetListener(IItemEventListener eventListener) { _listener = eventListener; }
public void Subscribe(string itemName)
{
if (itemName.Equals("greetings")) { new Thread(new ThreadStart(Run)).Start(); }
}
public void Unsubscribe(string itemName) { if (itemName.Equals("greetings")) { go = false; } }
public void Run()
{
go = true;
int c = 0;
Random rand = new Random();
while (go)
{
IDictionary eventData = new Hashtable();
eventData["message"] = c % 2 == 0 ? "Hello" : "World";
eventData["timestamp"] = DateTime.Now.ToString("s");
_listener.Update("greetings", eventData, false);
c++;
Thread.Sleep(1000 + rand.Next(2000));
}
}
}




Запуск адаптера



Для запуска нам понадобится консольная апликуха

using System;
using System.Net.Sockets;
using Lightstreamer.DotNet.Server;
public class DataAdapterLauncher
{
public static void Main(string[] args)
{
string host = "localhost";
int reqrepPort = 6661;
int notifPort = 6662;
try
{
DataProviderServer server = new DataProviderServer();
server.Adapter = new HelloWorldAdapter();
TcpClient reqrepSocket = new TcpClient(host, reqrepPort);
server.RequestStream = reqrepSocket.GetStream();
server.ReplyStream = reqrepSocket.GetStream();
TcpClient notifSocket = new TcpClient(host, notifPort);
server.NotifyStream = notifSocket.GetStream();
server.Start();
System.Console.WriteLine("Remote Adapter connected to Lightstreamer Server.");
System.Console.WriteLine("Ready to publish data...");
}
catch (Exception e)
{
System.Console.WriteLine("Could not connect to Lightstreamer Server.");
System.Console.WriteLine("Make sure Lightstreamer Server is started before this Adapter.");
System.Console.WriteLine(e);
}
}
}




Заливка адаптера на сервер



В папке /Lightstreamer/adapters/ создаём новую папку и копируем в неё /Lightstreamer/DOCS-SDKs/sdk_adapter_remoting_infrastructure/lib/ls-proxy-adapters.jar. Там же создаём новый файл /lib/adapters.xml с контентом

<?xml version="1.0"?>
<adapters_conf id="HelloWorld">
<metadata_provider>
<adapter_class>com.lightstreamer.adapters.metadata.LiteralBasedProvider</adapter_class>
</metadata_provider>
<data_provider>
<adapter_class>com.lightstreamer.adapters.remote.data.RobustNetworkedDataProvider</adapter_class>
<classloader>log-enabled</classloader>
<param name="request_reply_port">
6661</param>
<param name="notify_port">
6662</param>
</data_provider>
</adapters_conf>




Имя может бить любое — оно будет указываться при написании клиента и все. Как видите порты указываются такие же как при запуске адаптера.
Написание клиента



Для примера приведу простейший JavaScript клиент

<html>
<head>
<script src="http://ift.tt/1rd0IXd"></script>
<script src="http://ift.tt/PI1jmv"></script>
<script src="C:/Lightstreamer/DOCS-SDKs/sdk_client_javascript/lib/lightstreamer.js"></script>
<script>
require(["LightstreamerClient","Subscription","StaticGrid"],function(LightstreamerClient,Subscription,StaticGrid) {
var lsClient = new LightstreamerClient("http://localhost:8080", "HelloWorld");
lsClient.connect();
var lsSubscription = new Subscription("MERGE",['greetings'],['greetings', 'message', 'timestamp']);
lsSubscription.addListener({
onItemUpdate: function(updateObject) {
$("#message").text('Message=' + updateObject.getValue("message"));
$("#timestamp").text('TimeStamp=' + updateObject.getValue("timestamp"));
}
});
lsClient.subscribe(lsSubscription);
});
</script>
</head>
<body>
<span id="message"></span><br/>
<span id="timestamp">
</span>
</body>
</html>




Результат



И в итоге получили автоматически обновляемые данные на клиенте:

image

Что бы клиент мог получать уникальные данные, которые относятся именно к нему, например сообщения в чате, то вместо просто 'greetings' (второй параметр) нужно передать айдишку пользователя и на стороне адаптера по этой айдишке выгребать его сообщения.



new Subscription("MERGE",['userID123'],['greetings', 'message', 'timestamp'])




Так же можно передавать не только один параметр, а несколько, разделённых запятой, двоеточьем и тд. и соответственно на стороне адаптера всё это парсить.

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



Судя по http://ift.tt/1ot7arK адаптер можно написать на:


  • Java

  • .NET

  • Node js




А список платформ для клиентов побольше:


  • avaScript

  • Android

  • iOS

  • Windows Phone

  • WinRT

  • BlackBerry

  • Java ME

  • Flash

  • Flex

  • Silverlight

  • .NET

  • Java SE

  • OS X




P.S.

Сорсы адаптера для примера на github'e.

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий