PHP + SockJS + Redis: Unicast

1

Мой стек приложений:

На моем сервере запущен сервер Redis. Бэкэнд PHP взаимодействует с библиотекой Predis с сервером Redis. Он будет публиковать сообщения. Эти сообщения будут получены моим клиентом Redis (node.js) и перенаправлены на подключенные клиенты websocket (с помощью SockJS).

Моя проблема:

Он работает хорошо. По крайней мере, для широковещательных сообщений. Теперь я пришел к тому, что мне нужно отправить одноадресное сообщение, и я застрял... Как подключить пользователя на стороне сервера (отправителя сообщений) с подключенным клиентом веб-узла?

Фрагменты кода:

PHP

$redis = new Client();
$redis->publish('updates', Random::getUniqueString());

Клиент Redis на сервере node.js

redis.subscribe('updates');

redis.on('message', function(channel, data) {
    for (var id in sockets) {
        if (sockets.hasOwnProperty(id)) {
            sockets[id].write(data);
        }
    }
});

Клиент SockJS

mySocketFactory.setHandler('message', function(event) {
    console.log(event.data);
});

Как я и сказал. Хорошо работает, но id используемый для подключения к сокету, не известен бэкэнд PHP.

Изменение: Одна идея, которую я имел в виду, - это использовать куки.

Теги:
websocket
redis

1 ответ

0

Я нашел способ решить свою проблему. Когда соединение сокета установлено, я отправил запрос на мой PHP-сервер и попросил идентификатор пользователя. Это сохраняется на сервере node.js. Когда сообщения поступают, есть чек, если они предназначены для конкретного пользователя и обрабатывают их только для них.

Итак, что я точно храню на моем узловом сервере?

var sockets = {}; // {connection_id: socket_connection}

var connIdToUser = {}; // {connection_id: user_id}

var connIdsForUser = {}; // {user_id: [connection_id_1, connection_id_2 ,...]}


socketServer.on('connection', function(conn) {
    sockets[conn.id] = conn;

    var options = {
        host: os.hostname(),
        port: 80,
        path: '/user/id',
        method: 'GET'
    };
    var req = http.request(options, function(res) {
        res.setEncoding('utf8');
        res.on('data', function (chunk) {
            var userId = JSON.parse(chunk).id;
            connIdToUser[conn.id] = userId;

            if (!connIdsForUser.hasOwnProperty(userId)) {
                connIdsForUser[userId] = [];
            }
            connIdsForUser[userId].push(conn.id);

            console.log('connection id ' + conn.id + ' related to user id ' + userId);
        });
    });
    req.end();

    conn.on('close', function() {
        console.log('connection lost ' + conn.id);

        // remove connection id from stack for user
        var connections = connIdsForUser[connIdToUser[conn.id]];
        var index = connections.indexOf(conn.id);
        if (index > -1) {
            connections.splice(index, 1);
        }

        // remove connection at all
        delete sockets[conn.id];
        // remove relation between connection id and user
        delete connIdToUser[conn.id];
    });
});

Причина сохранения отношения между идентификатором пользователя дважды ID подключения - это другой вариант использования, который мне нужен либо для отправки сообщения, либо для удаления соединения для события закрытия. В противном случае мне пришлось бы использовать вложенный цикл. Как вы можете видеть, удаление сокета довольно просто. Хотя удаление соединения из стека подключений пользователя немного сложнее.

Продолжайте отправку сообщения. Здесь я определил структуру сообщения, которое я получаю с сервера Redis:

{
  targets: [], // array of unit ids (can be empty)
  data: <mixed> // the real data
}

Отправка данных в сокеты выглядит так:

redis.on('message', function(channel, message) {
    message = JSON.parse(message);

    // unicast/multicast
    if (message.targets.length > 0) {
        message.targets.forEach(function(userId) {
            if (connIdsForUser[userId] !== undefined) {
                connIdsForUser[userId].forEach(function(connId) {
                    sockets[connId].write(message.data);
                });
            }
        });
    // broadcast
    } else {
        for (var id in sockets) {
            if (sockets.hasOwnProperty(id)) {
                sockets[id].write(message.data);
            }
        }
    }
});

Поскольку я храню стек подключения для каждого пользователя, довольно легко отправить данные во все сокеты, относящиеся к конкретному пользователю. Так что теперь я могу сделать unicast (массив с одним идентификатором пользователя), multicast (массив с несколькими идентификаторами пользователя) и широковещательный (пустой массив).

Это хорошо работает для моего использования.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню