» » » Jii: Масштабируемый комет сервер и клиент

 

Jii: Масштабируемый комет сервер и клиент

Автор: admin от 14-10-2015, 14:23, посмотрело: 632

Продолжаю серию статей про Jii Framework. Сегодня настал момент релиза комета, о котором я и расскажу в этой статье.

Jii: Масштабируемый комет сервер и клиент

Jii-comet — это масштабируемый, готовый к высоким нагрузкам и плохому интернету транспорт, реализующий постоянную связь между клиентом и сервером для мгновенного обмена данными.

Jii-comet предоставляет набор компонентов и классов, которые упрощают обмен сообщениями между каналами, подписки на них, обмена данными между серверами и так далее. Сам модуль не умеет доставлять сообщения на клиент и обратно, но в нем заложена абстракция, чтобы это можно было делать любой из существующих популярных библиотек (например, socket.io, sockjs), а так же чтобы это было надежно и масштабируемо.

Для тех, кто в первый раз слышит об этом фреймворке, рекомендую прочитать предыдущие статьи или посетить сайт. Если коротко, то
Jii — это фреймфорк, архитектура и API которого базируется на PHP фреймворке Yii 2.0, взяв из него лучшие стороны и сохраняя преимущества javascript.

Обзор


Для начала немного справки о том, что такое комет (wiki):
Comet (в веб-разработке) — любая модель работы веб-приложения, при которой постоянное HTTP-соединение позволяет веб-серверу отправлять (push) данные браузеру без дополнительного запроса со стороны браузера.

Возможности Jii-comet


Клиент:

  • Отправка и прием сообщений из каналов

  • Подписка на каналы

  • Вызов действий на сервере

  • Балансировка (случайный выбор сервера)

  • Возможность смены транспорта сообщений


Сервер:

  • Отправка и прием сообщений из каналов

  • Подписка на сообщения из канала

  • Подписывание соединения на канал

  • Отправка сообщений в соединение

  • Возможность смены транспорта сообщений, хаба и очереди

  • Масштабирование на несколько процессов и серверов

  • Режим listen-only (прослушивание каналов)

  • Запуск действий из очереди


Установка


Комет устанавливается как компонент приложения, на сервере и клиенте соответственно. На сервере комет открывает и «слушает» данные из указанного порта, а клиент при инициализации приложения подключается к этому порту и ждет информацию.

Рассмотрим пример приложения, где клиент подписывается на сервере на канал test и отправляет в него сообщение.
Сервер:

var Jii = require('jii');
require('jii-comet');

require('jii-workers')
    .application('comet', Jii.mergeConfigs(
        {
            application: {
                basePath: __dirname,
                components: {
                    comet: {
                        className: 'Jii.comet.server.Server',
                        port: 4401,

                        /**
                         *
                         * @param {Jii.comet.server.ConnectionEvent} event
                         */
                        'on addConnection': function(event) {
                            Jii.app.comet.subscribe(event.connection.id, 'test');
                        }
                    }
                }
            }
        },
        custom.comet || {}
    ));

Клиент:

require('jii/deps');
require('jii-comet/sockjs');

Jii.createWebApplication({
    application: {
        basePath: location.href,
        components: {
            comet: {
                className: 'Jii.comet.client.Client',
                serverUrl: 'http://localhost:4401/comet',

                /**
                 *
                 * @param {Jii.base.Event} event
                 */
                'on open': function(event) {
                    Jii.app.comet.send('test', {message: 'Hello World'});
                },

                /**
                 *
                 * @param {Jii.comet.ChannelEvent} event
                 */
                'on channel:test': function(event) {
                    console.log('Income message: ' + event.params.message);
                }
            }
        }
    }
}).start();

Далее делально рассмотрим серверные и клиентские компоненты.

Комет-сервер


Jii: Масштабируемый комет сервер и клиент

Jii-comet предоставляет два класса для создания сервера:

  • Jii.comet.server.HubServer — сервер, настроенный только для прослушивания сообщений от соединений, при этом самих подключений у него нет, но он может отправить данные в подключение, находящееся в соседнем процессе. Т.е. в такой сервер напрямую (на порт) невозможно подключиться, однако сообщение можно отправить через другие процессы (к которым подключены клиенты).

  • Jii.comet.server.Server — полноценный сервер, унаследован он предыдущего. Хранит и поддерживает прямое соединение с клиентами, может обмениваться данными с клиентом.


Такое разделение необходимо для гибкого масштабирования приложения. Например, вы можете создать несколько процессов с компонентом Jii.comet.server.Server, которые будут только поддерживать соединение с клиентами, но не обрабатывать бизнес логику. И создать несколько процессов Jii.comet.server.HubServer, которые будут выполнять тяжелые вычисления. Это позволит сделать комет-канал более отзывчивым: если бы все операции были бы на одном сервере, то сложные операции затормаживали бы процесс, и все последующие запросы (даже легкие) были бы с задержкой.

Если у вас нет больших нагрузок или вы не знаете, что выбрать, выбирайте компонент Jii.comet.server.Server, так как он содержит в себе весь функционал.

Пример конфигурации для сервера выглядит так:

application: {
    components: {
        comet: {
            className: 'Jii.comet.server.Server',
            host: '0.0.0.0',
            port: 3100
        },

        // ...
    }
}

Полный список свойств, методов и событий представлен ниже.

Jii.comet.server.HubServer


Свойства



  • listenActions (boolean) — принимать ли запросы на вызов действий от клиентов. По умолчанию, true.

  • hub (object) — хаб, компонент или его конфигурация, имплементирующий интерфейс Jii.comet.server.hub.HubInterface для обмена сообщениями между процессами или серверами. По умолчанию создается компонент Jii.comet.server.hub.Redis.

  • queue (object) — очередь, компонент или его конфигурация, имплементирующий интерфейс Jii.comet.server.queue.QueueInterface для накопления очереди вызовов действий (actions). По умолчанию создается компонент Jii.comet.server.queue.Redis.


Методы



  • start() — открывает соединение для компонентов hub и queue, начинает слушать сообщения из каналов.

  • stop() — разрывает все соединения компонентов и перестает слушать информацию из вне.

  • sendToChannel(channel, data) — отправляет сообщение в канал в рамках хаба. Здесь channel — (string) название канала, а data — (string|object|*) данные для отправки.

  • sendToConnection(id, data) — как и предыдущий метод, отправляет данные, но не в канал, а напрямую в соединение. Здесь id (string) — это идентификатор соединения, взятый из Jii.comet.server.Connection, а data — (string|object|*) данные для отправки.

  • on(name, handler, data, isAppend) и off(name, handler) — методы для подписки и отписки на события. Список событий описан ниже, описание аргументов можно найти в разделе событий.

  • hasChannelHandlers(name) — Возвращает true, если на канал name (string) есть подписчики в рамках данного процесса.


События



  • channel — Событие запускается при любом входящем сообщении в любой канал. Первым аргументом обработчика будет передан экземпляр класса Jii.comet.ChannelEvent с параметрами channel (string) и message (string).

  • channel:%my_channel_name% — Событие запускается при любом входящем сообщении в канал, указанный после

Например, при вызове Jii.app.comet.on('channel:test', ...) происходит подписка на сообщения в канал test. Как и выше, в обработчик события первым аргументом будет передан экземпляр класса Jii.comet.ChannelEvent с параметрами channel (string) и message (string).
message — Событие запускается при любом входящем сообщении в хаб. Первым аргументом обработчика будет передан экземпляр класса Jii.comet.server.MessageEvent с параметром message (string).

Jii.comet.server.Server


Наследует вышеперечисленные свойства, методы и события и добавляет следующие:

Свойства



  • host (string) — хост, ожидающий входящие соединения от клиентов. По-умолчанию, 0.0.0.0.

  • port (number) — порт для входящих соединений. По-умолчанию, 4100.

  • transport (object) — компонент или его конфигурация, имплементирующий интерфейс Jii.comet.server.transport.TransportInterface для обмена сообщениями непосредственно с клиентами (браузером).


Методы



  • subscribe(connectionId, channel) и unsubscribe(connectionId, channel) — подписывает и отписывает соединение на заданный канал. Jii рекомендует делать подписку на канал именно на сервере, чтобы избежать гонки сообщений. В методах connectionId (string) — это идентификатор соединения, взятый из Jii.comet.server.Connection, а channel (string) — имя канала.


События



  • addConnection — Событие возникает при присоединении нового клиента. Первым аргументом обработчика будет передан экземпляр класса Jii.comet.server.ConnectionEvent с параметром connection (Jii.comet.server.Connection).

  • removeConnection — Событие возникает при потере соединения с клиентом. Аналогично предыдущему, первым аргументом обработчика будет передан экземпляр класса Jii.comet.server.ConnectionEvent.


Jii.comet.server.Connection


Содержит информацию о соединении с клиентом

Свойства



  • id (string) — идентификатор соединения. Используется для отправки сообщений напрямую в соединение.

  • request (Jii.comet.server.Request) — информация о соединении: заголовки, ip адрес, порт подключения и так далее.

  • originalConnection (object) — внутренний экземпляр соединения, полученный от транспорта. Специфичен для каждого транспорта.


Комет-клиент


Jii: Масштабируемый комет сервер и клиент

На клиенте немного проще: есть один компонент Jii.comet.client.Client, который подключается к одному из серверов и обменивается данными.

application: {
    components: {
        comet: {
            className: 'Jii.comet.client.Client',
            serverUrl: 'http://localhost:4401/comet',
            autoOpen: true
        },

        // ...
    }
}

Полный список свойств, методов и событий представлен ниже.

Jii.comet.client.Client


Свойства



  • transport (object) — компонент или его конфигурация, имплементирующий интерфейс Jii.comet.client.transport.TransportInterface для обмена сообщениями с сервером.

  • plugins (object) — набор компонентов или их конфигурация, каждый из которых имплементирует интерфейс Jii.comet.client.plugin.PluginInterface для расширения возможностей комет клиента.

  • workersCount (number|null) — максимальное количество воркеров сервера. Эта настройка используется для балансировки нагрузки на клиенте. По-умолчанию, null — отключена.

  • autoOpen (boolean) — если указано true, то при инициализации приложения комет клиент автоматически будет подключаться к серверу. По-умолчанию, true.


События



  • open — Событие запускается при успешном открытии соединения.

  • close — Событие запускается при закрытии соединения.

  • beforeSend — Событие запускается при любом исходящем сообщении. Первым аргументом обработчика будет передан экземпляр класса Jii.comet.client.MessageEvent с параметром message. Вы можете изменить отправляемое сообщение, изменяя параметр message, на сервер отправятся данные из параметра message.

  • channel — Событие запускается при любом входящем сообщении в любой подписанный канал. Первым аргументом обработчика будет передан экземпляр класса Jii.comet.ChannelEvent с параметрами channel (string) и message (string).

  • channel:%my_channel_name% — Событие запускается при любом входящем сообщении в подписанный канал, указанный после :. Например, при вызове Jii.app.comet.on('channel:test', ...) происходит подписка на сообщения в канал test. Как и выше, в обработчик события первым аргументом будет передан экземпляр класса Jii.comet.ChannelEvent с параметрами channel (string) и message (string).

  • message — Событие запускается при любом входящем сообщении. Первым аргументом обработчика будет передан экземпляр класса Jii.comet.server.MessageEvent с параметром message (string).

  • beforeRequest — Событие запускается перед тем, как клиент пытается отправить на сервер запрос на выполнения действия. Первым аргументом обработчика будет передан экземпляр класса Jii.comet.client.RequestEvent с параметрами route (string) и params (object). Вы можете изменить параметры запроса в этом событии, на сервер отправятся данные из параметра params.

  • request — Событие запускается при ответе сервера на запрошенное ранее действие. Первым аргументом обработчика будет передан экземпляр класса Jii.comet.client.RequestEvent с параметрами route (string) и params (object).


Плагины


Плагины позволяют расширять возможности комет клиента. По-умолчанию, установлен плагин для автоматического восстановления соединения Jii.comet.client.plugin.AutoReconnect. Плагин очень прост в создании и подключении. Интерфейс плагина Jii.comet.client.plugin.PluginInterface не требует имплементации методов, а лишь предоставляет доступ к комет-клиенту через параметр comet, через который можно подписываться на события комета. Устанавливается и настраивается плагин через конфигурацию, путем добавления его в секцию plugins:

application: {
    components: {
        comet: {
            className: 'Jii.comet.client.Client',
            // ...
            plugins: {
                autoReconnect: {
                    enable: false
                },
                myPlugin: {
			        className: 'app.components.MyCometPlugin'
                }
            }
        },

        // ...
    }
}

Как это работает


Jii: Масштабируемый комет сервер и клиент

С первого взгляда, jii-comet — это обертка над библиотекой комет-транспорта (sockjs, socket.io — на выбор), однако это не так. От библиотеки транспорта берется только функционал доставки сообщения на клиент и обратно. Реализация каналов, подписок, масштабирования и дополнительных фишек сделано именно в jii-comet. Архитектурно «под капотом» jii-comet разбит на следующие составляющие:

Сервер:

  • Транспорт (Jii.comet.server.transport.TransportInterface) — абстракция для обмена сообщениями между клиентом и сервером.

  • Хаб (Jii.comet.server.hub.HubInterface) — абстракция для обмена сообщениями между процессами и серверами.

  • Очередь (Jii.comet.server.queue.QueueInterface) — абстракция для накопления вызовов действий.

  • Соединение (Jii.comet.server.Connection) — экземпляр данного класса содержит информацию о соединении и доступен как компонент контекста.


Клиент:

  • Транспорт (Jii.comet.client.transport.TransportInterface) — абстракция для обмена сообщениями между клиентом и сервером.

  • Плагин (Jii.comet.client.plugin.PluginInterface) — абстракция для расширения возможностей комет клиента. По-умолчанию, установлен плагин для автоматического восстановления соединения Jii.comet.client.plugin.AutoReconnect.


Все эти абстракции могут настраиваться и переопределяться через конфигурацию приложения и контекста.

Примеры



  • несколько клиентов

  • несколько серверных процессов только для поддержания соединений (Jii.comet.server.Server и listenActions = false)

  • несколько серверных процессов для поддержания соединений и обработки действий (Jii.comet.server.Server и listenActions = true)

  • несколько серверных процессов только для обработки действий (Jii.comet.server.HubServer и listenActions = true)


Пример: Клиент отправляет сообщение в канал



Jii: Масштабируемый комет сервер и клиент



  1. Клиент отправляет сообщение в канал test.

  2. Серверный процесс, поддерживающий соединение принимает это сообщение и отправляет в хаб (Jii.comet.server.hub.HubInterface).

  3. Хаб рассылает это сообщение всем серверным процессам, которые подписаны на данный канал (или подписаны их соединения).

  4. Серверные процессы получают сообщение и отправляют соединениям, которые на него подписаны.


Пример: Клиент вызывает действие на сервере



Jii: Масштабируемый комет сервер и клиент


  1. Клиент отправляет запрос на выполнение действия site/test.

  2. Серверный процесс, поддерживающий соединение принимает его и скидывает в очередь действий (Jii.comet.server.queue.QueueInterface), а так же через хаб (Jii.comet.server.hub.HubInterface) рассылает уведомление всем серверам, обрабатывающим действия (listenActions = true) о том, что очередь обновилась.

  3. Один из серверных процессов с listenActions = true вытаскивает запрос из очереди (методом lpop в имплементации Redis'а) и запускает у себя действие.

  4. После выполнения действия, серверный процесс отправляет результат действия (response) обратно клиенту. Поскольку данный серверный процесс не имеет прямого соединения с клиентом, он отправляет его в хаб (Jii.comet.server.hub.HubInterface).

  5. Хаб отправляет сообщение серверному процессу, подписанному на сообщения для этого соединения.

  6. Серверный процесс, поддерживающий это соединение получает сообщение и отправляет его непосредственно клиенту.


В третьем пункте запрос из очереди мог вытянуть любой серверный процесс с listenActions = true. Работает это по принципу «кто успел» и, как правило, успевать будет наименее нагруженный сервер. Таким образом, производится балансировка вызовов действий между серверами и их процессами.

Внутренний канал связи


Jii-comet имеет абстракцию, позволяющую использовать любую библиотеку в качестве внутреннего канала связи между клиентом и сервером. На данный момент сделан адаптер для библиотеки sockjs, в будущем появится и для socket.io.

В конфигурации транспорт объявляется в секции transport. По-умолчанию, используется класс Jii.comet.server.transport.Sockjs на севере и Jii.comet.client.transport.Sockjs на клиенте.
Транспорт можно конфигурировать и переопределять через конфигурацию:

comet: {
    className: 'Jii.comet.client.Client',
    transport: {
        className: 'Jii.comet.client.transport.Sockjs',
        transports: [
            'websocket',
            'xhr-streaming',
            'xdr-streaming',
            'jsonp-polling'
        ]
    }
}

The End


Сайт фреймворка — jiiframework.ru
GitHub — github.com/jiisoft
Пожелания и предложения отправляйте на affka@affka.ru

Нравится идея фреймворка? Ставь звезду на гитхабе!




Источник: Хабрахабр

Категория: Программирование » Веб-разработка

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Добавление комментария

Имя:*
E-Mail:
Комментарий:
Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
Введите два слова, показанных на изображении: *