Эта статья была впервые опубликована в моем личном блогеБлог Сая
Пожалуйста, укажите источник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 для автоматического развертывания