Pull to refresh

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

Reading time5 min
Views5.2K
Для отправки данных с сервера на клиент, не важно будь это веб, мобайл или десктоп существует достаточное количество техник. Но проблема в том что они все разные и если нужно реализовать оповещение об одном и том же событии для клиентов на всех основных платформах, то придется дублировать этот самый код оповещения. Вот поэтому я хочу поделится с сообществом своей практикой по работе с одним продуктом под названием 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://cdnjs.cloudflare.com/ajax/libs/require.js/1.0.7/require.min.js"></script>
      <script src="https://code.jquery.com/jquery-2.1.0.min.js"></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 классе передать адрес машины где стоит сервер и таким образом возможно не имея локального сервака дебажить.

Напоследок

Судя по www.lightstreamer.com/doc адаптер можно написать на:
  • 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.
Tags:
Hubs:
Total votes 6: ↑4 and ↓2+2
Comments0

Articles