大家好,
第一次发帖,不要拍砖啊,哈哈。
目前正在设计消息系统这块,已经有了2种方案,都是比较不错的,但是现在纠结在消息持久化这块。
第一种比较传统:收到消息后直接存入数据库,标记未读,当用户刷新页面,或者跳转的时候,会拉取下未读消息,并显示在导航栏那里。
第二种比较前卫:用nodejs+socket.io+redis(sub/pub)做的,优点是不用刷新页面用户就可以直接收到提醒,而且引入scoket.io以后还能做很多事情,个人打算采用第二种。
但是现在纠结在消息的持久化方面,如果在web层做消息入库,那么我发现socket.io除了在用户不刷新页面的时候“弹一下”,真没什么用了。反正消息已经存进去了。
要么就是web只管往redis里面publish一个消息,由node这里统一入库,并通知。我第一次用nodejs,对性能没什么信心,主要是nodejs对mysql的操作方面。
想听听大家的意见和看法,谢谢!!
补充:刚才被一位qq好友一顿批评:
这你还用问吗?如果你采用第二种方案,当然是在nodejs做持久化了,你引入redis的pub/sub干什么用的啊!?不就是为了分布式吗?能从不同的地方发消息吗?难道我发一条消息还要先在你的数据库保存一下,再做redis的publish吗?如果是那样,你的nodejs可以扔了。。。。
还是想听听大家的意见。。。。。谢谢。。。。。
对于第二种方案经过了2天的实践,最终放弃了,还是选择了第一种简单的。首先说明:第二种方案是可行的
开始分享代码吧
我的系统对nodejs定位只是用到websocket这块,不提供http服务(ngxin提供),所以没用到任何框架express,sails什么的,再说我喜欢从最基本的代码来了解一个新东西,好了,先看下package.json:
string-template是用来输出消息的,别的大家应该很熟悉了。先看下app.js
test.js 的authorize方法,判断链接是否可信,我用的cookie做的验证
test.js 的connect方法,用户通过验证后的链接逻辑:
到这里用户可以正常拦截,链接,并join自己订阅的room了。下面就是怎么从redis的channel里面取到消息通知客户端了,我这里简答的做了一个新用户注册后的消息通知:
sayWelcome私有方法:
客户端,实时通知:
用户在导航栏点消息的时候,进入消息页面。
还有好多用法,向所有用户发实时一条消息:
伪代码:
当帖子有了新回复时,向关注帖子的用户发一条消息:
如果不打算做nodejs分布式的可以不用看下面,上面已经可行了。
好了,到了这里,好像都很ok,消息系统从原来的http移植到了nodejs,但是我发现了一个很严重的问题:
引入redis的pub/sub就是为了分布式设计的,现在我在另一个端口启动一个同样的nodejs,2个nodejs同时监听 test.socket频道。那么,同样的一个消息会被写入数据库2次,N个nodejs,那么同样的消息会被写入N次。因为大家都在监听同样的频道。
这个问题也能解决,在redis端先生成一个新消息的id,然后用redis的setnx命令去锁这个id,只可能被一个nodejs线程锁到,抢到的进行消息入库,没锁到的只负责通知。
但是这么一来,如此简单的一个消息系统被设计的这么复杂。。。。。。。。
我个人对设计一个理解:
于是打算回归第一种简单的设计,也许我对nodejs的这个使用场景根本就是错误的,欢迎大家一起聊聊。