Nodejs использует socket.io для реализации распределенной отправки сообщений «один к одному».

Node.js Redis задняя часть сервер

Эта статья была впервые опубликована в моем личном блогеБлог Сая
Пожалуйста, укажите источникalipot.top/blog/5 не 45...

В последнее время я использую nodejs для реализации функции уведомления о сообщениях, которая использует socket.io; поскольку я использовал socket.io впервые, большинство материалов онлайн-поиска являются учебными пособиями по трансляции в чате, и я не нашел конкретный метод реализации для отправки сообщений один к одному Здесь Поделитесь некоторыми ямами, на которые вы наступили во время обучения и использования, и окончательным методом реализации.

Реализуемая функция:
После обновления данных на стороне сервера соответствующим пользователям на стороне клиента отправляется уведомление; сервер записывается на nodejs и распределяется, одновременно открываются несколько экземпляров.

#1. Установите пакеты зависимостей

npm install socket.io

#Во-вторых, использование socket.io

1. Содержимое файла index.js

//index.js
var express = require('express')
var app = express()
var server = require('http').Server(app)
var io = require('socket.io')(server, {
    path: '/notice' //修改客户端请求的路径,默认为/socket.io
})
new (require('./notice')).init(io) //传入io

2. Содержимое файла note.js

var Notice = null
var socketMap = {} //用户对应socket.id

//初始化socket连接
exports.init = function(io) {
    //连接验证
    io.use(function(socket, next) {
        //console.log(socket.request.headers.cookie);
        var token = socket.request._query.token || ''
        if (validate(token)) {
            socket.request.headers.user = { userId: userId }
            return next()
        } else {
            return next(new Error('Authentication error'))
        }
    })
    Notice = io.of('/notice').on('connection', function(socket) {
        var user = socket.handshake.headers.user
        var user_id = user && user.userId
        if (user_id) {
            socketMap[user_id] = socket.id
        }

        socket.on('disconnect', function() {
            delete socketMap[user_id]
        })
    })
}
//其他模块调用,发送消息
exports.send = function(data) {
    var user_id = data.accountID
    var socket_id = socketMap[user_id]
    Notice.to(socket_id).emit('notice', data)
}

3. Файл клиента

<script src="/socket.io/socket.io.js"></script>
<script>
  var socket = io('http://localhost?token=token');
  socket.on('notice', function (data) {
    console.log(data);
  });
</script>

В случае междоменных запросовsocket.request.headers.cookieНевозможно получить действительный файл cookie, поэтому здесь токен передается напрямую от клиента для проверки. После прохождения проверки информация о пользователе сохраняется в заголовках.После успешного подключения клиента информация о пользователе извлекается, а идентификатор пользователя и соответствующий идентификатор подключения к сокету сохраняются в глобальномsocketMapпеременная, то внешний модуль отправляет сообщение, вызывая метод send.

Эта ситуация не является проблемой в случае одного экземпляра, но если сервер открывает несколько экземпляров, возникает проблема.

#3. Используйте nginx для распределенного развертывания

Сначала нужно установитьnginx;Записать файл конфигурации после завершения установки;

upstream socket_test {
        ip_hash;
        server 127.0.0.1:8013;
        server 127.0.0.1:8014;
}
server {
    listen 80;
    server_name socket.test.com;
    location / {
        proxy_pass              http://socket_test/;
        proxy_set_header        Upgrade $http_upgrade;
        proxy_set_header        Connection "upgrade";
        proxy_http_version      1.1;
        proxy_set_header        Host $host;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Запустите два экземпляра проекта, прослушивая порты 8013 и 8014 соответственно, затем запустите nginx.

sudo nginx

В ходе проверки будет обнаружено, что иногда сообщение может быть получено, а иногда оно не может быть получено. Причина в том, что когда клиент запрашивает, он будет бесконечно запрашивать один из двух экземпляров сервера.Если соединение является экземпляром запрошенного порта 8013, а второе отправленное сообщение является экземпляром порта 8014, оно будет в экземпляр 8014. Не удалось найти сокет для установления соединения.

#4. Распределенные решения

Официальный сайт socket.io предоставляет возможность использованияsocket.io-redis
Чтобы решить этот метод, socket.io-redis использует функцию подписки на сообщения и публикации redis.При отправке уведомления будет запущено событие onmessage, а затем будет вызвана широковещательная рассылка.

Затем добавьте socket.io-redis в файл index.js.

//index.js
var express = require('express')
var app = express()
var server = require('http').Server(app)
var io = require('socket.io')(server, {
    path: '/notice' //修改客户端请求的路径,默认为/socket.io
})
new (require('./notice')).init(io) //传入io
var redis = require('socket.io-redis')
io.adapter(redis({ host: settings.REDIS_HOST, port: settings.REDIS_PORT }))

Затем после перезапуска вы обнаружите, что он по-прежнему не работает, потому что иногда соответствующий user_id не может быть найден в переменной socketMap; поскольку он распределен, все переменные socketMap не используются совместно между двумя экземплярами, поэтому здесь используется redis, в Когда сокет установлен, socket_id, соответствующий user_id, сохраняется в redis, а socket_id считывается из redis при отправке сообщения.

Иногда у пользователя может быть несколько подключений к сокету одновременно, поэтому socket_id, соответствующий user_id, может быть массивом.При отправке сообщения его следует отправлять циклически.При разрыве соединения следует удалять только соответствующий socket_id, а другие установленные соединения должны быть сохранены.

Кроме того, сокет восстановит соединение при перезапуске сервера, поэтому все сохраненные данные в Redis должны быть очищены во время инициализации.

Веб-перехватчики GitHub для автоматического развертывания