Cyy’s Blog alibt.top/ Blog /5b45ba…

Recently, nodeJS is used to implement a message notification function, which uses socket. IO; Due to the first use of socket. IO, the search information on the Internet is mostly chat room broadcast tutorial, did not find one to one to send a message specific implementation method, here to share their learning to use the process of stepping on some pits and the final implementation method.

Functions to be realized: After data updates on the server end, notifications are sent to the corresponding client users; The server is written in NodeJS and uses distribution, with multiple instances enabled at the same time.

#1. Install dependency packages

npm install socket.io
Copy the code

#Socket. IO

1. Contents of the index.js file

//index.js var express = require('express') var app = express() var server = require('http').Server(app) var io = // Require ('socket. IO ')(server, {path: '/notice') new (require('./notice')).init(IO) // Incoming IOCopy the code

2. Contents of notice.js file

Var Notice = null var socketMap = {} // user corresponding socket.id // initialization of socket connections. init = function(IO) {// connection authentication 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) }Copy the code

3. Client files

<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>Copy the code

. In the case of cross-domain request socket. The request headers. The cookie is unable to get a cookie to effective, so here come directly from the client token for validation. After the authentication succeeds, the user information is stored in headers. After the client connection succeeds, the user information is retrieved and the user ID and the corresponding SOCKET connection ID are stored in the global socketMap variable. Then the external module sends the message by calling the SEND method.

This is fine in the case of a single instance, but is problematic if the server has multiple instances open.

#Third, use Nginx to achieve distributed deployment

First, you need to install nginx; Write configuration files after installation;

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; }}Copy the code

Start two project instances, listening on ports 8013 and 8014 respectively; Then start nginx

sudo nginx
Copy the code

Tests show that messages are sometimes received and sometimes not. The reason is that when the client requests, it may request either of the two instances of the server. If the connection is made to the instance of port 8013 and the message is sent to the instance of port 8014, the socket for establishing the connection cannot be found in the instance of port 8014.

#Fourth, distributed solution

IO: socket. IO: socket. IO: socket. IO: socket. IO: socket. IO: socket. IO: socket. IO: socket. IO Socket. IO -redis uses the message subscription and publishing functions of Redis. When a notification is sent, onMessage event is triggered and broadcast broadcast is called.

Next add socket.io-redis to the index.js file

//index.js var express = require('express') var app = express() var server = require('http').Server(app) var io = require('socket.io')(server, { path: '/notice' // Change the path requested by the client, IO /socket}) new (require('./notice').init(IO) // host: settings.REDIS_HOST, port: settings.REDIS_PORT }))Copy the code

When you restart the socketMap variable, you still can’t find the corresponding user_id. Since socketMap variables are distributed, they are not shared between the two instances. Therefore, redis is used to store the socket_id corresponding to user_id into redis during socket establishment. Read the socket_id from Redis when sending the message.

Sometimes a user may have multiple socket connections at the same time, so the socket_id corresponding to the user_id can be an array. When sending a message, only the corresponding socket_id should be deleted when the connection is disconnected, and other established connections should be retained.

In addition, the socket will be re-established when the server restarts, so all data stored in redis should be emptied during initialization.

GitHub Webhooks implement automatic deployment