Continuing a series of articles about the
Jii Framework . Today came the time of release of the comet, which I will discuss in this article.
Jii-comet is a scalable transport that is ready for high loads and bad Internet, realizing constant communication between the client and the server for instant data exchange.
')
Jii-comet provides a set of components and classes that make it easy to exchange messages between channels, subscribe to them, exchange data between servers, and so on. The module itself does not know how to deliver messages to the client and back, but it contains an abstraction so that any of the existing popular libraries (for example,
socket.io ,
sockjs ) can do this, as well as reliably and scalable.
For those who hears about this framework for the first time, I recommend reading
previous articles or
visiting the site . In short,
Jii is a framework, whose architecture and API is based on the PHP framework Yii 2.0, taking the best sides out of it and retaining the benefits of JavaScript.
Overview
First, a little help on what comets are (
wiki ):
Comet (in web development) - any model of a web application, in which a permanent HTTP connection allows the web server to send (push) data to the browser without an additional request from the browser.
Jii-comet features
Customer:
- Sending and receiving messages from channels
- Subscribe to channels
- Calling up actions on the server
- Balancing (random server selection)
- The ability to change the transport of messages
Server:
- Sending and receiving messages from channels
- Subscribe to messages from the channel
- Signing a channel connection
- Send messages to the connection
- The ability to change the message transport, hub and queue
- Scaling to multiple processes and servers
- Listen-only mode
- Starting actions from a queue
Installation
A comet is installed as a
component of the application , on the server and client respectively. On the server, the comets open and “listen” to data from the specified port, and the client, when the application is initialized, connects to this port and waits for information.
Consider an example of an application where a client subscribes to a
test channel on a server and sends a message to it.
Server:
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, 'on addConnection': function(event) { Jii.app.comet.subscribe(event.connection.id, 'test'); } } } } }, custom.comet || {} ));
Customer:
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', 'on open': function(event) { Jii.app.comet.send('test', {message: 'Hello World'}); }, 'on channel:test': function(event) { console.log('Income message: ' + event.params.message); } } } } }).start();
Next, let's look at server and client components.
Comet server
Jii-comet provides two classes for creating a server:
- Jii.comet.server.HubServer is a server configured only for listening to messages from connections, but it does not have the connections itself, but it can send data to a connection that is in a neighboring process. Those. It is impossible to connect directly to such a server (on the port), however, the message can be sent through other processes (to which clients are connected).
- Jii.comet.server.Server is a full-fledged server, inherited from the previous one. Stores and maintains direct connection with clients, can exchange data with the client.
This separation is necessary for flexible application scaling. For example, you can create several processes with the
Jii.comet.server.Server component, which will only maintain connection with clients, but not process business logic. And create multiple
Jii.comet.server.HubServer processes that will perform heavy calculations. This will make the comet channel more responsive: if all operations were on the same server, complex operations would slow down the process, and all subsequent requests (even light ones) would be delayed.
If you do not have large loads or you do not know what to choose, choose the
Jii.comet.server.Server component, as it contains all the functionality.
The configuration example for the server looks like this:
application: { components: { comet: { className: 'Jii.comet.server.Server', host: '0.0.0.0', port: 3100 },
A complete list of properties, methods and events is presented below.
Jii.comet.server.HubServer
Properties
- listenActions (boolean) - whether to accept requests to invoke actions from clients. By default, true .
- hub (object) - a hub, component, or its configuration that implements the Jii.comet.server.hub.HubInterface interface for exchanging messages between processes or servers. By default, the Jii.comet.server.hub.Redis component is created .
- queue (object) - a queue, a component or its configuration that implements the Jii.comet.server.queue.QueueInterface interface for accumulating a queue of actions (actions). By default, the Jii.comet.server.queue.Redis component is created .
Methods
- start () - opens the connection for the hub and queue components, starts listening to messages from the channels.
- stop () - breaks all connections of components and stops listening to information from the outside.
- sendToChannel (channel, data) - sends a message to the channel within the hub. Here channel is (string) the name of the channel, and data is (string | object | *) the data to send.
- sendToConnection (id, data) - like the previous method, sends data, but not to the channel, but directly to the connection. Here id (string) is the connection identifier taken from Jii.comet.server.Connection , and data is (string | object | *) the data to send.
- on (name, handler, data, isAppend) and off (name, handler) - methods for subscribing and unsubscribing to events. The event list is described below, a description of the arguments can be found in the event section .
- hasChannelHandlers (name) - Returns true if the name (string) channel has subscribers to this process.
Developments
- channel - Event is triggered by any incoming message to any channel. The first handler argument will be passed an instance of the Jii.comet.ChannelEvent class with the channel (string) and message (string) parameters.
- channel:% my_channel_name% - Event is triggered by any incoming message to the channel specified after
For example, when you call
Jii.app.comet.on ('channel: test', ...) , you subscribe to messages in the
test channel. As above, an instance of the
Jii.comet.ChannelEvent class with the parameters
channel (string) and
message (string) will be passed to the event handler as the first argument.
message - The event runs on any incoming message in the hub. The first argument of the handler will be an instance of the
Jii.comet.server.MessageEvent class with the
message (string) parameter.
Jii.comet.server.Server
Inherits the above properties, methods, and events, and adds the following:
Properties
- host (string) - the host waiting for incoming connections from clients. The default is 0.0.0.0 .
- port (number) - port for incoming connections. The default is 4100 .
- transport (object) - a component or its configuration that implements the Jii.comet.server.transport.TransportInterface interface for exchanging messages directly with clients (browser).
Methods
- subscribe (connectionId, channel) and unsubscribe (connectionId, channel) - subscribes and unsubscribes the connection to the specified channel. Jii recommends that you subscribe to the channel on the server to avoid a message race. In the connectionId (string) methods, this is the connection identifier taken from Jii.comet.server.Connection , and channel (string) is the name of the channel.
Developments
- addConnection - The event occurs when a new client is joined. The first handler argument will be passed an instance of the Jii.comet.server.ConnectionEvent class with the connection parameter ( Jii.comet.server.Connection ).
- removeConnection - The event occurs when the connection to the client is lost. Similar to the previous one, the first argument of the handler will be an instance of the Jii.comet.server.ConnectionEvent class.
Jii.comet.server.Connection
Contains client connection information
Properties
- id (string) - connection identifier. Used to send messages directly to the connection.
- request ( Jii.comet.server.Request ) - connection information: headers, ip address, connection port, and so on.
- originalConnection (object) - an internal instance of the connection received from the transport. Specific to each transport.
Comet client
The client is a little simpler: there is one
Jii.comet.client.Client component that connects to one of the servers and communicates.
application: { components: { comet: { className: 'Jii.comet.client.Client', serverUrl: 'http://localhost:4401/comet', autoOpen: true },
A complete list of properties, methods and events is presented below.
Jii.comet.client.Client
Properties
- transport (object) - a component or its configuration that implements the Jii.comet.client.transport.TransportInterface interface for exchanging messages with the server.
- plugins (object) - a set of components or their configuration, each of which implements the Jii.comet.client.plugin.PluginInterface interface to extend the capabilities of the client’s comets.
- workersCount (number | null) - the maximum number of server workers. This setting is used for load balancing on the client. By default, null is disabled.
- autoOpen (boolean) - if true , the client will automatically connect to the server when the comets application is initialized. By default, true .
Developments
- open - Event is triggered upon successful opening of the connection.
- close - The event is triggered when the connection is closed.
- beforeSend - The event is triggered by any outgoing message. The first argument of the handler will be an instance of the Jii.comet.client.MessageEvent class with the message parameter. You can change the sent message by changing the message parameter, the data from the message parameter will be sent to the server.
- channel - The event is triggered by any incoming message to any subscribed channel. The first handler argument will be passed an instance of the Jii.comet.ChannelEvent class with the channel (string) and message (string) parameters.
- channel:% my_channel_name% - The event is triggered by any incoming message to the subscribed channel specified after :. For example, when you call Jii.app.comet.on ('channel: test', ...) , you subscribe to messages in the test channel. As above, an instance of the Jii.comet.ChannelEvent class with the parameters channel (string) and message (string) will be passed to the event handler as the first argument.
- message - The event is triggered by any incoming message. The first argument of the handler will be an instance of the Jii.comet.server.MessageEvent class with the message (string) parameter.
- beforeRequest - The event runs before the client tries to send a request to the server to perform an action. The first argument of the handler will be an instance of the Jii.comet.client.RequestEvent class with the route (string) and params (object) parameters. You can change the request parameters in this event, the data from the params parameter will be sent to the server.
- request - The event is triggered when the server responds to the action requested previously. The first argument of the handler will be an instance of the Jii.comet.client.RequestEvent class with the route (string) and params (object) parameters.
Plugins
Plugins allow you to extend the capabilities of client comets. By default, the plugin is installed to automatically reconnect
Jii.comet.client.plugin.AutoReconnect . The plugin is very easy to create and connect. The interface of the
Jii.comet.client.plugin.PluginInterface plugin
does not require the implementation of methods, but only provides access to the comet client via the
comet parameter through which you can subscribe to comet events. The plugin is installed and configured through the configuration, by adding it to the
plugins section:
application: { components: { comet: { className: 'Jii.comet.client.Client', // ... plugins: { autoReconnect: { enable: false }, myPlugin: { className: 'app.components.MyCometPlugin' } } }, // ... } }
How it works
At first glance, jii-comet is a wrapper over the library of comets-transport (sockjs, socket.io - to choose from), but this is not so. From the transport library, only the message delivery functionality to the client and back is taken. The implementation of channels, subscriptions, scaling and additional chips made it in jii-comet. Architecturally, "under the hood" jii-comet is divided into the following components:
Server:
- Transport ( Jii.comet.server.transport.TransportInterface ) is an abstraction for exchanging messages between a client and a server.
- Hub ( Jii.comet.server.hub.HubInterface ) is an abstraction for exchanging messages between processes and servers.
- The queue ( Jii.comet.server.queue.QueueInterface ) is an abstraction for accumulating action calls.
- Connection ( Jii.comet.server.Connection ) - an instance of this class contains information about the connection and is available as a component of the context .
Customer:
- Transport ( Jii.comet.client.transport.TransportInterface ) is an abstraction for exchanging messages between a client and a server.
- The plugin ( Jii.comet.client.plugin.PluginInterface ) is an abstraction for extending the capabilities of the client's comets. By default, the plugin is installed to automatically reconnect Jii.comet.client.plugin.AutoReconnect .
All of these abstractions can be configured and redefined via application
configuration and context.
Examples
- several clients
- several server processes only for maintaining connections ( Jii.comet.server.Server and listenActions = false )
- several server processes for maintaining connections and handling actions ( Jii.comet.server.Server and listenActions = true )
- several server processes for processing actions only ( Jii.comet.server.HubServer and listenActions = true )
Example: The client sends a message to the channel.
- The client sends a message to the test channel.
- The server process supporting the connection accepts this message and sends it to the hub ( Jii.comet.server.hub.HubInterface ).
- The hub sends this message to all server processes that are subscribed to this channel (or their connections are signed).
- Server processes receive a message and send it to connections that are subscribed to it.
Example: Client triggers server action

- The client sends a request to perform the site / test action.
- The server process supporting the connection accepts it and drops it into the action queue ( Jii.comet.server.queue.QueueInterface ), as well as through the hub ( Jii.comet.server.hub.HubInterface ) sends a notification to all servers that process the actions ( listenActions = true ) that the queue is refreshed.
- One of the server processes with listenActions = true takes the request out of the queue (using the lpop method in the implementation of Redis) and starts the action itself.
- After performing the action, the server process sends the result of the action (response) back to the client. Since this server process does not have a direct connection with the client, it sends it to the hub ( Jii.comet.server.hub.HubInterface ).
- The hub sends a message to a server process subscribed to messages for this connection.
- The server process supporting this connection receives the message and sends it directly to the client.
In the third paragraph, a request from the queue could pull out any server process with
listenActions =
true . This works on the principle of "who had time" and, as a rule, the least loaded server will have time. Thus, balancing of action calls between servers and their processes is performed.
Internal communication channel
Jii-comet has an abstraction that allows you to use any library as an internal channel of communication between the client and the server. At the moment, an adapter has been made for the
sockjs library, in the future it will also appear for
socket.io .
In the configuration, the transport is declared in the
transport section. By default, the class
Jii.comet.server.transport.Sockjs in the north and
Jii.comet.client.transport.Sockjs on the client are used.
Vehicles can be configured and redefined via configuration:
comet: { className: 'Jii.comet.client.Client', transport: { className: 'Jii.comet.client.transport.Sockjs', transports: [ 'websocket', 'xhr-streaming', 'xdr-streaming', 'jsonp-polling' ] } }
The end
Framework Site -
jiiframework.ru
GitHub -
github.com/jiisoft
Send your suggestions and suggestions to affka@affka.ru
Like the idea of ​​the framework? Put a star on the githaba !