socketIo 搭建長連接(2)

今天我們在這裏要說的是wehsocket和node開發長連接問題,我們在真正的項目中,可能要實現的功能不知是簡單的聊天功能,我們現在要整合Redis,rabbitMQ,等實現o2o的提醒功能:

首先,整合一次redis:

我們建立一個chat文件夾,在其中寫入一個package.json文件,用於生成我們的node類庫

{  
  "name": "zefun",  
  "version": "1.0.0",  
  "description": "",  
  "main": "index.js",  
  "dependencies": {  
    "apn": "^1.5.2",  
    "amqp": "^0.2.4",  
    "co": "^3.0.6",  
    "co-redis": "^1.1.1",  
    "log4js": "^0.6.28",  
    "express": "^4.13.3",  
    "redis": "^0.10.3",  
    "redis-sentinel": "^0.1.3",  
    "socket.io": "^1.3.7"  
  },  
  "devDependencies": {},  
  "author": "",  
  "license": "ISC"  
}  
這時,我們通過運行node的命令進行安裝類庫,npm install

安裝結束後,我們會生成一個node_modules的文件,還要在本層的目錄創建一個logs的文件夾,因爲我們一會會使用一個日誌系統進行輸出,但是,node只有寫文件的權限,沒有輸出文件夾的權限

新建一個redisClient.js,作爲redis的客戶端連接

var PORT = 6379;  
var HOST = '120.25.254.164';  
var redis = require('redis');  
var chatClient = redis.createClient(PORT,HOST);  
var logger = require("./log").logger("redis");  
chatClient.on('error', function(err){  
    logger.error(err);  
});  
var chat_sadd = function(key, field) {  
    chatClient.sadd(key, field);  
    logger.info("redis sadd --> key : " + key + ", field : " + field);  
}  
var chat_srem = function(key, field) {  
    chatClient.srem(key, field);  
    logger.info("redis srem --> key : " + key + ", field : " + field);  
}  
var chat_smembers = function(key) {  
    return chatClient.smembers(key);  
}  
var chat_hset = function(key, userId, sockedId) {  
    chatClient.hset(key, userId, sockedId);  
    logger.info("redis hset --> key : " + key + ", field : " + userId + ", value :" + sockedId);  
}   
var chat_hget = function(key, userId) {  
    return chatClient.hget(key, userId);  
}   
  
exports.chat_sadd = chat_sadd;  
exports.chat_srem = chat_srem;  
exports.chat_smembers = chat_smembers;  
exports.chat_hset = chat_hset;  
exports.chat_hget = chat_hget;  


下面我們在主js中進行引用和傳值即可

var co = require('co');  
var wrapper = require('co-redis');  
var redis = wrapper(require("./redisClient")); 


我們看到上面我們使用了一個叫co的類庫,並使用wrapper對redis進行了包裝,只有經過包裝,在操作redis的時候,才能處理了node中異步的程序,將redis取值變成了同步的方式,即取到值後才能進行下面程序的執行
[html] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. co(function* () {  
  2. <span style="white-space:pre">    </span>var userSID = yield redis.chat_hget(USER_SOCKET_KEY, toUser);  
  3.     io.sockets.connected[userSID].emit('getMessage',{msg:msg});  
  4. })();  
對於存值不需要這麼同步
[html] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. redis.chat_sadd(redis_key,data.userId);  
redis就算是結束了,下面就開始rabbitMQ的搭建和操作:

此處我們將node作爲了rabbit的一個client進行監聽和處理程序,首先新建一個mqClient.js作爲隊列的客戶端進行監聽

[html] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. conn.queue('queue_chat_notify', { autoDelete: false, durable: true }, function(queue) {  
  2.     queue.subscribe(function (msg) {  
  3.       receiveNotify(msg.data);  
  4.     });  
  5.   });  
  6.   exports.createConnection = function(r1, r2) {  
  7.     receiveLogout = r1;  
  8.     receiveNotify = r2;  
  9.   }  
這是我們最核心的進行監聽的程序,是要subscribe這個方法,就可以進行監聽了,請注意receiveLogout這個方法,我們這個方式是需要在主js中作爲參數傳入的,當客戶端進行傳入一個方法作爲替代方法的時候,就可以進行讀取消息,但是如果沒有傳入,那麼該條數據丟失.下面是主js中的代碼
[html] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. var mq = require("./mqClient");  
  2. mq.createConnection(receiveNotify);  
  3. //rabbitmq監聽到通知的回調操作  
  4. var receiveNotify = function(msg) {  
  5.     logger.info("\r\n\r\n<-- receiveNotify begin -->");  
  6.     sendMsg(msg);  
  7. }  
這樣,在客戶端中判定登陸後,啓用該方法就可以讀取隊列中的消息,進行發送消息了,下面給出整個mqClient.js的全部內容
[html] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. var amqp = require("amqp");  
  2.   
  3. var exchName = "directExchange";  
  4.   
  5. var connOptions = {  
  6.       host: '120.25.254.164',  
  7.       port: 5672,  
  8.       login: 'zefun',  
  9.       password: 'zefun'  
  10.     };  
  11.   
  12. var exchOption = {  
  13.       type: 'direct',  
  14.       durable: true,  
  15.       autoDelete: false,  
  16.       confirm: false  
  17.     };  
  18.   
  19. var conn;  
  20. var receiveNotify  = null;  
  21. var logger = require('./log').logger("rabbitmq");  
  22. conn = amqp.createConnection(connOptions);  
  23.   
  24. conn.on('ready',function() {  
  25.   logger.info("rabbitmq is ready ... ");  
  26.   conn.queue('queue_chat_notify', { autoDelete: false, durable: true }, function(queue) {  
  27.     queue.subscribe(function (msg) {  
  28.       logger.info("queue_chat_notify consumer msg : " + msg);  
  29.       receiveNotify(msg.data);  
  30.     });  
  31.   });  
  32. });  
  33.   
  34. conn.on('close', function(){  
  35.   logger.error("rabbitmq is close");  
  36. });  
  37.   
  38. conn.on('error', function (error) {  
  39.   logger.error('Connection error : ' + error);  
  40. });  
  41.   
  42. exports.createConnection = function(r1) {  
  43.   logger.info("createConnection r1 : " + r1);  
  44.   receiveNotify = r1;  
  45. }  
  46. exports.publish = function(routeKey, message) {  
  47.   conn.publish(routeKey, message);  
  48.   
  49. };  
下面就是我們最後一步,看一下servier.js中的內容了
[html] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. var app = require('express')();  
  2. var http = require('http').Server(app);  
  3. var io = require('socket.io')(http);  
  4.   
  5. app.get('/', function(req, res){  
  6.     res.send('<h1>Welcome Realtime Server</h1>');  
  7. });  
  8.   
  9. http.listen(3000, function(){  
  10.     console.log('listening on *:3000');  
  11. });  
  12.   
  13. var mq = require("./mqClient");  
  14.   
  15. var co = require('co');  
  16. var wrapper = require('co-redis');  
  17. var redis = require("./redisClient");  
  18. var redisClient = wrapper(redis.chatClient);  
  19.   
  20. var logger = require("./log").logger("server");  
  21.   
  22. var STORE_USER_KEY = "store_to_chat_user_set_";  
  23. var USER_SOCKET_KEY = "chat_user_to_socket_hash";  
  24.   
  25. //rabbitmq監聽到通知的回調操作  
  26. var receiveNotify = function(msg) {  
  27.     logger.info("\r\n\r\n<-- receiveNotify begin -->");  
  28.     sendMsg(msg);  
  29. }  
  30.   
  31. mq.createConnection(receiveNotify);  
  32.   
  33. io.on('connection', function (socket) {  
  34.   
  35.   io.sockets.emit('connect',{hello:'connection success'});  
  36.   socket.on('sendMsg', function (from,toUser,msg,msgType) {  
  37.      co(function* () {  
  38.          var userSID = yield redisClient.hget(USER_SOCKET_KEY, toUser);  
  39.          io.sockets.connected[userSID].emit('getMessage',{msg:msg});  
  40.      })();   
  41.   });  
  42.   
  43.   socket.on('initUser',function(data){  
  44.      var redis_key = STORE_USER_KEY + data.storeId;  
  45.   
  46.      //redis插入數據   
  47.      redisClient.sadd(redis_key,data.userId);  
  48.      redisClient.hset(USER_SOCKET_KEY, data.userId, socket.id);  
  49.   
  50.      logger.info('initUser --->> 門店 :' + data.storeId + ', 用戶 : ' + data.userId);  
  51.   });  
  52.   
  53.   socket.on('disconnect', function () {  
  54.         
  55.   });  
  56. });  
  57.   
  58. var sendMsg = function(obj){  
  59.     co(function* () {  
  60.          var socketId = yield redisClient.hget(USER_SOCKET_KEY, obj.toUser);  
  61.          logger.info("socketId : " + socketId);  
  62.          io.sockets.connected[socketId].emit("getMessage", obj);  
  63.     })();   
  64. }  
下面,我們寫一個客戶端,其實就是一個jsp文件
[html] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. <%@ page language="java" contentType="text/html; charset=UTF-8"  
  2.     pageEncoding="UTF-8"%>  
  3. <%  
  4.     String chatPath = request.getContextPath();  
  5.             String chatPasePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()  
  6.                     + chatPath + "/";  
  7. %>  
  8. <script src="<%=chatPath%>/js/common/socket.io.js"></script>  
  9. <script type="text/javascript">  
  10. var userId = '${session_key_user_id}';  
  11. var storeId = '${session_key_store_id}';  
  12. var user = {"userId" : userId, "storeId" : storeId};  
  13. if(!isEmpty(userId)){  
  14.     var socket = io.connect('ws://localhost:3000');  
  15.     socket.on('connect', function(data) {  
  16.         //登錄聊天室  
  17.         socket.emit('initUser', user);  
  18.           
  19.         //接收消息  
  20.         socket.on('getMessage', function(data) {  
  21.             console.log("" + data);  
  22.             var fid = data.fid;  
  23.             //PC通知類處理  
  24.             if (fid == 2) {  
  25.                 var type = data.data.type;  
  26.                 //新預約  
  27.                 if (type == 2) {  
  28.                     //播放語音  
  29.                     textToVoice(0, data.data.msg);  
  30.                 }  
  31.             }  
  32.         });  
  33.     });  
  34. }  
  35. </script>  
可以進行觸發登陸,然後進行接收消息即可了.

記得在Linux上啓動的時候 , 使用 命令進行啓動,可作爲守護進程,這樣node就不會隨着終端的關閉而down掉。。。

到此我們的整套就寫完了,最後注意一下日誌,如果我們想將現在的類庫導入成爲package.json的話,在文件夾中使用npm init,自動生成json文件,就可以進行移植 

給出一個該項目的下載鏈接地址 http://download.csdn.NET/detail/u014201191/9303347

#特別注意:

在本案例中 io.sockets.connected[socket.id].emit("method",msg) 和 io.sockets.sockets[socket.id].emit("method",msg) 這兩個方法都不可用,可以換成: io.to[socket.id].emit("method",msg) 觸發傳送事件了,好像是TM因爲版本的問題。。。







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章