SocketCluster — это среда JavaScript для создания приложений реального времени. Он обеспечивает масштабируемую и отказоустойчивую архитектуру для создания высокопроизводительных приложений реального времени.

Особенности кластера сокетов:

1 — обрабатывать неограниченное количество каналов

2 — Легкое развертывание

3 — Легко масштабировать

4 — Порядок сообщения гарантии

5 — Контролировать противодавление сообщений

Вы можете использовать клиентские библиотеки на языках C#, Golang, Javascript, Java и т. д.

Требования

  • Node.js v10 (может работать со старыми версиями, но вы не сможете использовать цикл for-await-of для потребления потоков). Скачать Node.js.

Дополнительные зависимости

вы можете увидеть большую часть информации в этой ссылке.

[Сервер] Создать сервер

Аналогичная логика уже должна быть внутри server.js при использовании шаблона по умолчанию:

// --- in server.js ---
const http = require('http');
const socketClusterServer = require('socketcluster-server');

let options = {
  // ...
};

let httpServer = http.createServer();
let agServer = socketClusterServer.attach(httpServer, options);

// port 8000
httpServer.listen(8000);

Аргумент httpServer должен быть экземпляром HTTP-сервера Node.js. Список возможных options, которые можно передать экземпляру сервера SocketCluster, можно найти здесь. Обратите внимание, что вы также можете использовать синтаксис ECMAScript import.

[Сервер] Прослушивание входящих сокетных соединений

Внутри server.js вы можете найти цикл for-await-of, который обрабатывает входящие подключения. Это должно выглядеть так:

// --- in server.js ---

// SocketCluster/WebSocket connection handling loop.
(async () => {
  for await (let {socket} of agServer.listener('connection')) {
    // Handle socket connection.
  }
})();

[Клиент] Подключиться к серверу

Внутри public/index.html клиент подключается к серверу следующим образом:

// --- in public/index.html ---

let socket = socketClusterClient.create();

^ Если соединение установлено успешно, это приведет к тому, что цикл connection на стороне сервера повторится один раз.

!! Вы можете передать объект options функции socketClusterClient.create(...).

[Сервер] Прослушивание входящих сообщений на сокете

Вы можете использовать socket.receiver(...) внутри цикла for-await-of для обработки сообщений в сокете:

// --- in server.js ---

// SocketCluster/WebSocket connection handling loop.
(async () => {
  for await (let {socket} of agServer.listener('connection')) {

    (async () => {
      // Set up a loop to handle remote transmitted events.
      for await (let data of socket.receiver('customRemoteEvent')) {
        // ...
      }
    })();

  }
})();

!! Вы также можете перебирать поток socket.receiver(receiverName) в клиентском сокете, используя тот же синтаксис.

[Клиент] передает сообщения через сокет

// --- in public/index.html ---

// ... After the socket is created.
socket.transmit('customRemoteEvent', 123);

^ Если сообщение достигает сервера, цикл customRemoteEvent на стороне сервера повторяется один раз; data будет 123.

!! Вы также можете использовать тот же синтаксис для передачи из серверного сокета. Передача никогда не может быть неудачной, поэтому вам не нужно заключать ее в блок try-catch.

[Сервер] Прослушивание входящих RPC на сокете

В отличие от сообщений, RPC ожидают ответа от другой стороны. Вы можете использовать socket.procedure(...) в цикле for-await-of для обработки RPC в сокете:

// --- in server.js ---

// SocketCluster/WebSocket connection handling loop.
(async () => {
  for await (let {socket} of agServer.listener('connection')) {

    (async () => {
      // Set up a loop to handle and respond to RPCs.
      for await (let request of socket.procedure('customProc')) {
        if (request.data && request.data.bad) {
          let badCustomError = new Error('Server failed to execute the procedure');
          badCustomError.name = 'BadCustomError';
          request.error(badCustomError);

          continue;
        }
        request.end('Success');
      }
    })();

  }
})();

!! Вы также можете перебирать поток socket.procedure(procedureName) в клиентском сокете, используя тот же синтаксис.

[Клиент] Вызывать RPC через сокет

// --- in public/index.html ---

// ... After the socket is created.
(async () => {
  let result = await socket.invoke('customProc', {foo: 'bar'});
  // result will be 'Success'
})();

!! Вы всегда должны добавлять блок try-catch вокруг вызова socket.invoke(...) для захвата асинхронных ошибок:

// --- in public/index.html ---

// ... After the socket is created.
(async () => {
  let result;
  try {
    result = await socket.invoke('customProc', {bad: true});
  } catch (error) {
    // error will throw.
    // error.name will be 'BadCustomError'.
  }
})();

!! Вы также можете использовать тот же синтаксис для серверных сокетов.

[Клиент] Подпишитесь на канал и следите за сообщениями

(async () => {
  let channel = socket.subscribe('foo');
  for await (let data of channel) {
    // ... Handle channel data.
  }
})();

[Клиент] Публиковать на канал, не дожидаясь подтверждения

// Publish data; do not wait for an acknowledgement from the server.
socket.transmitPublish('foo', 'This is some data');

!! Вы также можете вызвать transmitPublish для объекта AGChannel; в этом случае следует опустить первый аргумент, например:

let fooChannel = socket.channel('foo');
fooChannel.transmitPublish('This is some data');

[Клиент] Публикация на канале и ожидание подтверждения

(async () => {
  try {
    // Publish data; wait for an acknowledgement from the server.
    await socket.invokePublish('foo', 'This is some more data');
  } catch (error) {
    // ... Handle potential error if server does not acknowledge before timeout.
  }
})();

!! Вы также можете вызвать invokePublish для объекта AGChannel; в этом случае следует опустить первый аргумент, например:

(async () => {
  let fooChannel = socket.channel('foo');
  try {
    // Publish data; wait for an acknowledgement from the server.
    await fooChannel.invokePublish('This is some more data');
  } catch (error) {
    // ... Handle potential error if server does not acknowledge before timeout.
  }
})();

[Сервер] Публиковать на канал, не дожидаясь подтверждения

// Publish data; do not wait for an acknowledgement from the back end broker.
agServer.exchange.transmitPublish('foo', 'This is some data');

[Сервер] Публикация на канале и ожидание подтверждения

(async () => {
  try {
    // Publish data; wait for an acknowledgement from the back end broker (if it exists).
    await agServer.exchange.invokePublish('foo', 'This is some more data');
  } catch (error) {
    // ... Handle potential error if broker does not acknowledge before timeout.
  }
})();