express redis socket 消息提醒方案:本地emit 轮循服务器获取redis 再推送
在做项目时,需要实现消息提醒,因为现在有多个项目,都需要实现。
为了实现多项目公用,和以后项目也可以使用。
单独开了个项目,起了个node 服务来实现消息提醒。
用express redis socket.io来实现的。
session 都存在redis里,所有的服务都一样。这样实现了,sessio共享
只要其他项目登录了,消息服务也就登录了。
因为要多个项目共用,所以会在项目中引用socket.io js 然后创建
var socket=io('http://localhost:8007');
链接到8007也就是消息服务器。
消息服务io connection 链接上后,通过socket.request.headers.sookie 来获取请求的cookie 找到connect.sid 得到sid。
再从redis上查找对应的用户信息。保存到链接socket上。然后发送emit open ,表示链接成功。
本地接收到后轮循向服务器发请message 请求来获取消息。
服务器查询redis获取消息,并emit 返回。
以下是服务端代码:
- /*star 2016-8-20
- socket 从请求中获取cookie 也就是token 再从redis里取到token对应的user 信息。
- 这后向socket页面emit(open) 表示当前登录用户链接socket成功。
- 本地轮循发起emit message 事件,获取用户的提示条数。
- 服务器根据 userid 从redis里读取提示条数并返回。
- ----此为node socket 消息服务器第一种解决方案
- */
- var express = require('express');
- var path = require('path');
- var cookieParser = require('cookie-parser');
- var bodyParser = require('body-parser');
- var session = require('express-session');
- var RedisStore = require('connect-redis')(session);
- var http = require('http');
- var redis_api=require('./redis_api.js')
- var port=8007;
- var app = express();
- // view engine setup
- app.use(bodyParser.json());
- app.use(bodyParser.urlencoded({ extended: false }));
- app.use(cookieParser());
- app.use(express.static(path.join(__dirname, 'public')));
- // 设置 Session
- app.use(session({
- store: new RedisStore({
- host: "127.0.0.1",
- port: 6379,
- db: 0
- //pass: 'yu'
- }),
- resave:false,
- saveUninitialized:false,
- secret: 'keyboard cat'
- }))
- app.use(function(req,res,next){
- console.log(req.url);
- next();
- })
- app.get('/socket',function(req,res){
- res.header("Access-Control-Allow-Origin", "*");
- //console.log(req.session.username)
- res.send(req.session)
- })
- var server = http.createServer(app);
- server.listen(port);
- server.on('error', onError);
- server.on('listening', onListening);
- var io=require('socket.io')(server);
- var users=[]
- io.on('connection',function(socket){
- console.log('open')
- //获取请求cookie
- var cookie_string=decodeURIComponent(socket.request.headers.cookie)
- //正则匹配 获取sid
- var s=/connect.sid=([^\.]+)/g.exec(cookie_string);
- var sid='';
- if(s && s.length>1){
- sid=s[1].split(':')[1];
- //console.log(sid);
- var user;
- redis_api.getsid(sid,function(err,res){
- if(!err){
- user=res;
- socket.emit('open',{sid:sid,user:user.user})
- socket.sid=sid;
- socket.user=user.user;
- //链接数测试
- /*var n=9000000;
- while(n--){
- users.push(socket);
- }*/
- }
- })
- }
- socket.on('message',function(res){
- console.log(res);
- switch(res.action){
- case "read":
- read();
- break;
- }
- })
- function read(){
- redis_api.getmessage(socket.user.id,function(err,reply){
- if(!err){
- socket.emit('message',{type:1,action:'message',data:reply,number:users.length})
- }
- })
- }
- socket.on('disconnect',function(){
- console.log('close')
- })
- })
- function onError(error) {
- if (error.syscall !== 'listen') {
- throw error;
- }
- var bind = typeof port === 'string'
- ? 'Pipe ' + port
- : 'Port ' + port;
- // handle specific listen errors with friendly messages
- switch (error.code) {
- case 'EACCES':
- console.error(bind + ' requires elevated privileges');
- break;
- case 'EADDRINUSE':
- console.error(bind + ' is already in use');
- break;
- default:
- throw error;
- }
- }
- /**
- * Event listener for HTTP server "listening" event.
- */
- function onListening() {
- console.log('localhost:'+port)
- }
- var redis = require('redis');
- var client = redis.createClient('6379', '127.0.0.1');
- exports.getsid=function(sid,call){
- //client.select('0',function(err){
- //if(!err){
- //console.log('select 0',err)
- client.get('sess:'+sid ,function(err,reply){
- console.log('------sess:'+sid)
- console.log('sess:',sid,err,reply)
- console.log('end ------sess:'+sid)
- if(err){
- call(err)
- }else{
- //console.log('sess:'+sid+'=',reply)
- call(0,JSON.parse(reply))
- }
- })
- //}
- //})
- }
- //根据userid 获取消息数
- exports.getmessage=function(userid,call){
- client.select('0',function(err){
- if(!err){
- client.get('message:'+userid,function(err,reply){
- if(!err){
- console.log('message:'+userid+'='+reply)
- call(0,JSON.parse(reply))
- }
- // 关闭链接
- //client.quit();
- })
- }
- })
- }
以下是页面代码:
- var socket=io('http://localhost:8007');
- socket.on('open',function(res){
- console.log('开了',res)
- time();
- //socket.emit('message',{id:1,type:1,action:'read'})
- })
- socket.on('message',function(res){
- console.log(res,new Date());
- })
- //以下为轮循发请emit 获取消息数
- function time(){
- setInterval(function(){
- socket.emit('message',{type:1,action:'read'})
- //socket.emit('message',{type:1,action:'read'})
- },3000)
- }
当然这种实现是有问题 的,并不是时时消息提醒。
而且当链接数大时不断轮循会给服务器和带宽带来很大压力。