node.js聊天室架构设计

 

2011-11-21 11:11:04|  分类: node |  标签:node.js聊天室  聊天室架构  node.js聊天室架构  node.jsmongodb  php聊天室  |字号 订阅

最近公司想在自己的项目中加入node.js项目,公司项目中有一个聊天室的东西,原来是基于flash socket制作的,服务器运营成本比较大,而且需要用户安装flash插件,对于使用ipad等平板用户比较悲剧。

公司的项目需要兼容IE6-8,所以web socket无法使用,只能使用常规的comet服务器推技术。而由于PHP在这方面天生短板,如果要服务端使用PHP的话,同时有1000人在一个服务器上聊天,服务器内存会被占用大半,而且频繁的释放建立fastcgi进程也在一定程度上影响效率。并且PHP不支持事件驱动,所以对于服务器推消息,只能去轮询数据库或缓存是否有新消息,这样数据库服务器压力也会倍增。

所以最后决定使用单进程,事件驱动的node.js作为服务端,不必每次重新建立进程处理请求,而且可以在有新消息时将消息推给客户端,不必去频繁扫描数据库。但是由于原本系统是基于php+flash socket的,所以一些如:用户信息,聊天室用户列表等,需要node.js异步去php接口获取,这样node.js异步的特性也发挥出来了。

我们的整体思路就是这样,上个架构图吧:

 


简单说明一下:
1、 客户端A、B、C、D、E分别连接到了nodejs聊天服务器1-4中,利用 长连接等待node.js 聊天服务器返回新消息。
2、 客户A向同在一个聊天室的客户B、客户C和客户E,发送了一条 新消息X
3、 Node.js聊天  服务器 1  将立即返回给同样 长连接在本服务器的客户E和客户A,并且同时异步将 新消息X 转发到 node.js数据库接口服务器
4、 注意:node.js聊天服务器和 node.js数据库接口服务器可以根据业务的处理量选择使用tcp不断连接或是http请求后断开连接
5、 Node.js接口服务器同时异步将 新消息X 存入mongodb数据库,并且同时根据 配置的地址池 将新消息广播转发到 node.js聊天服务器集群中。
6、 Node.js聊天服务器1-4接收到 新消息X,将 对 长连接在本服务器所有客户端信息和消息进行比对,判断是否需要返回 消息X长连接在本服务器的客户端,判断下来,客户端D不是消息接收方,node.js聊天服务器4 将 新消息X 抛弃,而B和C都获得了 新消息X。而node.js服务器 1 发现 新消息X 是本服务器发送的,则直接抛弃。
7、   另外这个 新消息X 中还会带有用户列表心跳包,即时更新用户活跃的时间戳,并且和PHP端保持用户列表的及时更新。

说明:
长连接等待:表示用户的ajax请求不立即返回,先挂起,等有新消息时再推给客户端。
负载均衡:在node.js聊天室服务器前可以部署一个nginx服务器用来做负载均衡,万一其中某一台node.js聊天室服务器宕机了,可以将其业务给其他服务器接管。

当然以上的架构比较大,可能部署的服务器比较多。但是扩展性和容错性比较好,属于花中等钱办中等事情。在网站日均pv 1000万以内,这套架构完全可以胜任了,估计以我们公司的业务几年内也突破不了,哈哈!

我们还有另外一套花小钱办中事情的方案,简称 方案-2。就是利用nginx 的 url_hash 模块,将同一聊天室的请求 反向代理 到同一台node.js聊天服务器,这样就可以将 node.js数据库接口服务器移走,node.js聊天服务器直接入库并且和PHP进行通信。
这样节约了服务器,而且由于在同一聊天室的客户端都在同一个服务器内,所以内存共享很容易就做到了,不必将消息转发,提高了效率。

不过 方案-2有一个严重的隐患,即利用url_hash做负载均衡后,期hash pool中有一台服务器宕机了,其业务并不会交由其他服务器接管,所以风险也较高,虽然可以做一个守护进程自动启动node.js主进程,也在node.js主进程中加入自动启动子进程,但是万一服务器断电了,还是会中断业务。

对于 方案-1方案-2需要公司权衡了,想花更少的钱达到同样的目的,那就必须承担一定的风险。不过如果公司业务预计增长很快,而且对于聊天系统的稳定性要求很高,那就不得不选择 方案-1了。

花絮:
在制作整个功能时踩了2个地雷,下面拿出来分享一下:
1、在我转发用户新消息时,出于方便,将用户的http请求头都转发了,所以出现PHP服务端返回一直是乱码,后来打印出请求头才发现,原来转发时增加了:“ Accept-Encoding : gzip, deflate”,所以nginx返回了gzip压缩以后的数据,转发时去掉那个http头就可以了。
2、在转发时一开始使用了post,结果造成了nginx 411错误,查阅资料发现是post请求的content-length没有设置,设置为POST body的字节数大小即可,当然后来我们改为了GET。

你可能感兴趣的:(node.js聊天室架构设计)