如何用nodejs 分布式构建分布式聊天系统

 上传我的文档
 下载
 收藏
该文档贡献者很忙,什么也没留下。
 下载此文档
正在努力加载中...
基于NodeJS的实时推送系统的设计与实现
下载积分:1998
内容提示:基于NodeJS的实时推送系统的设计与实现
文档格式:PDF|
浏览次数:150|
上传日期: 05:55:32|
文档星级:
该用户还上传了这些文档
基于NodeJS的实时推送系统的设计与实现
官方公共微信& Nodejs实现websocket的4种方式
Nodejs实现websocket的4种方式
,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发。Nodejs框架是基于V8的引擎,是目前速度最快的Javascript引擎。chrome浏览器就基于V8,同时打开20-30个网页都很流畅。Nodejs标准的web开发框架Express,可以帮助我们迅速建立web站点,比起PHP的开发效率更高,而且学习曲线更低。非常适合小型网站,个性化网站,我们自己的Geek网站!!
张丹(Conan), 程序员Java,R,PHP,Javascript
weibo:@Conan_Z
转载请注明出处:
WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。在WebSocket API中,浏览器和服务器只需要要做一个握手(handshaking)的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
WebSocket是一个通信的协议,分为服务器和客户端。服务器放在后台,保持与客户端的长连接,完成双方通信的任务。客户端一般都是实现在支持HTML5浏览器核心中,通过提供JavascriptAPI使用网页可以建立websocket连接。Java实现的案例,请参考:
今天让我们来看看在nodejs中,如何实现websocket的通信。
为什么用Nodejs
node-websocket-server:测试失败
node-websocket:测试成功
faye-websocket-node: 测试成功
socket.io: 测试成功
1. 为什么用Nodejs?
1. 事件驱动,通过闭包很容易实现客户端的生命活期。
2. 不用担心多线程,锁,并行计算的问题
3. V8引擎速度非常快
4. 对于游戏来说,写一遍游戏逻辑代码,前端后端通用。
当然Nodejs也有一些缺点:
1. nodejs更新很快,可能会出现版本联兼容
2. nodejs还不算成熟,还没有大制作。
3. nodejs不像其他的服务器,对于不同的连接,不支持进程和线程操作。
在权衡Nodejs给我们带来无限畅快的开发的同时,要考虑到他的不成熟,特别是对于“长连接”的网络通信应用。下面我将分别,测试一下网上几种Nodejs实现websocket的框架。
我的系统环境
win7 64bit
Nodejs:v0.10.5
Npm:1.2.19
~ D:\workspace\javascript&node -v
~ D:\workspace\javascript&npm -v
2. node-websocket-server: 测试失败
github: /miksago/node-websocket-server
node-websocket-server:是基于nodejs底层API实现的,可能产生不兼容的机率是90-100%,现在已经不建议再使用了。我查了代码库,发现已经有两年没有更新了,所以还在准备用node-websocket-server框架的同学,要特别小心了。
我也做了一个实验,一直会报错,贴一下实验代码。
~ D:\workspace\javascript&mkdir nodejs-websocket-server
~ D:\workspace\javascript&cd nodejs-websocket-server
~ D:\workspace\javascript\nodejs-websocket-server&npm install websocket-server
npm http GET https://registry.npmjs.org/websocket-server
npm http 304 https://registry.npmjs.org/websocket-server
npm http GET https://registry.npmjs.org/websocket-server/-/websocket-server-1.4.04.tgz
npm http 200 https://registry.npmjs.org/websocket-server/-/websocket-server-1.4.04.tgz
websocket-server@1.4.04 node_modules\websocket-server
~ vi app.js
var conns = new Array();
var ws = require("websocket-server");
var server = ws.createServer();
server.addListener("connection", function(connection){
console.log("Connection request on Websocket-Server");
conns.push(connection);
connection.addListener('message',function(msg){
console.log(msg);
for(var i=0; i&conns. i++){
if(conns[i]!=connection){
conns[i].send(msg);
server.listen(3001);
客户端连接:
&div id="output"&&/div&
function checkBrowser(){
if (window.WebSocket){
log("This browser supports WebSocket!");
log("This browser does not support WebSocket.");
function setup(){
var wsServer = 'ws://localhost:3001';
var ws = new WebSocket(wsServer);
ws.onopen = function (e) {
log("Connected to WebSocket server.",e);
sendMessage("Conan");
ws.onclose = function (e) {
log("Disconnected",e);
ws.onmessage = function(e) {
log("RECEIVED: " + e.data, e);
ws.close();
ws.onerror = function (e) {
log('Error occured: ' + e.data,e);
var sendMessage = function(msg){
ws.send(msg);
log("SEND : "+ msg);
function log(s,e) {
var output = document.getElementById("output");
var p = document.createElement("p");
p.style.wordWrap = "break-word";
p.style.padding="10px";
p.style.background="#eee";
p.textContent = "LOG : "+s;
output.appendChild(p);
console.log("LOG : "+s, e);
checkBrowser();
错误提示:
查了一下原因,node-websocket-server,不支持websocket的draft-10,而chrome 14+浏览器,只支持draft-10的websocket,这样chrome基本都不能用了。我的chrome版本是28.0.1500.95。所以,大家就换个思路吧!
3. WebSocket-Node: 测试成功
github: /Worlize/WebSocket-Node
WebSocket-Node,是一个简单的库,不仅支持draft-10,还有之前的各种版本。
服务器端配置
~ D:\workspace\javascript&mkdir nodejs-websocket
~ D:\workspace\javascript&cd nodejs-websocket
D:\workspace\javascript\nodejs-websocket&npm install websocket
npm http GET https://registry.npmjs.org/websocket
npm http 304 https://registry.npmjs.org/websocket
& websocket@1.0.8 install D:\workspace\javascript\nodejs-websocket\node_modules\websocket
& node install.js
[websocket v1.0.8] Attempting to compile native extensions.
[websocket v1.0.8]
Native code compile failed!!
Please note that this module DOES NOT REQUIRE the native components
and will still work without them, though not quite as efficiently.
On Windows, native extensions require Visual Studio and Python.
On Unix, native extensions require Python, make and a C++ compiler.
Start npm with --websocket:verbose to show compilation output (if any).
websocket@1.0.8 node_modules\websocket
上面提示有错误,我本机已经装了Visual Studio和Python,看来有些模块本地编译不成功了。
增加app.js
~ vi app.js
// http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
"use strict";
// Optional. You will see this name in eg. 'ps' or 'top' command
process.title = 'node-chat';
// Port where we'll run the websocket server
var webSocketsServerPort = 3001;
// websocket and http servers
var webSocketServer = require('websocket').
var http = require('http');
* Global variables
// latest 100 messages
var history = [ ];
// list of currently connected clients (users)
var clients = [ ];
* Helper function for escaping input strings
function htmlEntities(str) {
return String(str).replace(/&/g, '&').replace(/&/g, '&').replace(/"/g, '"');
// Array with some colors
var colors = [ 'red', 'green', 'blue', 'magenta', 'purple', 'plum', 'orange' ];
// ... in random order
colors.sort(function(a,b) { return Math.random() & 0.5; } );
* HTTP server
var server = http.createServer(function(request, response) {
// Not important for us. We're writing WebSocket server, not HTTP server
server.listen(webSocketsServerPort, function() {
console.log((new Date()) + " Server is listening on port " + webSocketsServerPort);
* WebSocket server
var wsServer = new webSocketServer({
// WebSocket server is tied to a HTTP server. WebSocket request is just
// an enhanced HTTP request. For more info http://tools.ietf.org/html/rfc6455#page-6
httpServer: server
// This callback function is called every time someone
// tries to connect to the WebSocket server
wsServer.on('request', function(request) {
console.log((new Date()) + ' Connection from origin ' + request.origin + '.');
// accept connection - you should check 'request.origin' to make sure that
// client is connecting from your website
// (http://en.wikipedia.org/wiki/Same_origin_policy)
var connection = request.accept(null, request.origin);
// we need to know client index to remove them on 'close' event
var index = clients.push(connection) - 1;
var userName =
var userColor =
console.log((new Date()) + ' Connection accepted.');
// send back chat history
if (history.length & 0) {
connection.sendUTF(JSON.stringify( { type: 'history', data: history} ));
// user sent some message
connection.on('message', function(message) {
if (message.type === 'utf8') { // accept only text
if (userName === false) { // first message sent by user is their name
// remember user name
userName = htmlEntities(message.utf8Data);
// get random color and send it back to the user
userColor = colors.shift();
connection.sendUTF(JSON.stringify({ type:'color', data: userColor }));
console.log((new Date()) + ' User is known as: ' + userName
+ ' with ' + userColor + ' color.');
} else { // log and broadcast the message
console.log((new Date()) + ' Received Message from '
+ userName + ': ' + message.utf8Data);
// we want to keep history of all sent messages
var obj = {
time: (new Date()).getTime(),
text: htmlEntities(message.utf8Data),
author: userName,
color: userColor
history.push(obj);
history = history.slice(-100);
// broadcast message to all connected clients
var json = JSON.stringify({ type:'message', data: obj });
for (var i=0; i & clients. i++) {
clients[i].sendUTF(json);
// user disconnected
connection.on('close', function(connection) {
if (userName !== false && userColor !== false) {
console.log((new Date()) + " Peer "
+ connection.remoteAddress + " disconnected.");
// remove user from the list of connected clients
clients.splice(index, 1);
// push back user's color to be reused by another user
colors.push(userColor);
启动服务器
~ D:\workspace\javascript\nodejs-websocket&node app.js
Warning: Native modules not compiled.
XOR performance will be degraded.
Warning: Native modules not compiled.
UTF-8 validation disabled.
Wed Aug 21 :48 GMT+0800 (中国标准时间) Server is listening on port 3001
Wed Aug 21 :53 GMT+0800 (中国标准时间) Connection from origin null.
Wed Aug 21 :53 GMT+0800 (中国标准时间) Connection accepted.
Wed Aug 21 :53 GMT+0800 (中国标准时间) User is known as: Conan with red color.
Wed Aug 21 :53 GMT+0800 (中国标准时间) Peer undefined disconnected.
我们看到XOR 和UTF-8 validation 两个模块是被禁止的,不过不影响测试。
客户端,还是使用一样的。
另外,WebSocket-Node还提供C/S模式的交互,可以不使用浏览器
服务端代码
~ vi app2.js
#!/usr/bin/env node
var WebSocketServer = require('websocket').
var http = require('http');
var server = http.createServer(function(request, response) {
console.log((new Date()) + ' Received request for ' + request.url);
response.writeHead(404);
response.end();
server.listen(3001, function() {
console.log((new Date()) + ' Server is listening on port 3001');
wsServer = new WebSocketServer({
httpServer: server,
// You should not use autoAcceptConnections for production
// applications, as it defeats all standard cross-origin protection
// facilities built into the protocol and the browser.
You should
// *always* verify the connection's origin and decide whether or not
// to accept it.
autoAcceptConnections: false
function originIsAllowed(origin) {
// put logic here to detect whether the specified origin is allowed.
wsServer.on('request', function(request) {
if (!originIsAllowed(request.origin)) {
// Make sure we only accept requests from an allowed origin
request.reject();
console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
var connection = request.accept('echo-protocol', request.origin);
console.log((new Date()) + ' Connection accepted.');
connection.on('message', function(message) {
if (message.type === 'utf8') {
console.log('Received Message: ' + message.utf8Data);
connection.sendUTF(message.utf8Data);
else if (message.type === 'binary') {
console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
connection.sendBytes(message.binaryData);
connection.on('close', function(reasonCode, description) {
console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
客户端代码
~ vi client.js
#!/usr/bin/env node
var WebSocketClient = require('websocket').
var client = new WebSocketClient();
client.on('connectFailed', function(error) {
console.log('Connect Error: ' + error.toString());
client.on('connect', function(connection) {
console.log('WebSocket client connected');
connection.on('error', function(error) {
console.log("Connection Error: " + error.toString());
connection.on('close', function() {
console.log('echo-protocol Connection Closed');
connection.on('message', function(message) {
if (message.type === 'utf8') {
console.log("Received: '" + message.utf8Data + "'");
function sendNumber() {
if (connection.connected) {
var number = Math.round(Math.random() * 0xFFFFFF);
connection.sendUTF(number.toString());
setTimeout(sendNumber, 1000);
sendNumber();
client.connect('ws://localhost:3001/', 'echo-protocol');
程序启动:
~ D:\workspace\javascript\nodejs-websocket&node app2.js
~ D:\workspace\javascript\nodejs-websocket&node client.js
测试成功!!
4. faye-websocket-node: 测试成功
github: /faye/faye-websocket-node
faye-websocket-node,是扩展faye项目而开发的websocket的一个实现。
服务器端配置
~ D:\workspace\javascript&mkdir nodejs-faye-websocket
~ D:\workspace\javascript&cd nodejs-faye-websocket
~ D:\workspace\javascript\nodejs-faye-websocket&npm install faye-websocket
npm http GET https://registry.npmjs.org/faye-websocket
npm http 304 https://registry.npmjs.org/faye-websocket
npm http GET https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.6.1.tgz
npm http 200 https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.6.1.tgz
npm http GET https://registry.npmjs.org/websocket-driver
npm http 200 https://registry.npmjs.org/websocket-driver
npm http GET https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.2.2.tgz
npm http 200 https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.2.2.tgz
faye-websocket@0.6.1 node_modules\faye-websocket
└── websocket-driver@0.2.2
~ vi app.js
var WebSocket = require('faye-websocket'),
= require('http');
var server = http.createServer();
server.on('upgrade', function(request, socket, body) {
if (WebSocket.isWebSocket(request)) {
var ws = new WebSocket(request, socket, body);
ws.on('message', function(event) {
ws.send(event.data);
ws.on('close', function(event) {
console.log('close', event.code, event.reason);
server.listen(3001);
~ D:\workspace\javascript\nodejs-faye-websocket&node app.js
用网页客户端访问:
测试成功,非常简单!!而且,没有依赖其他的库!!
5. socket.io: 测试成功
github: /LearnBoost/socket.io
~ D:\workspace\javascript&express -e nodejs-socketio
~ D:\workspace\javascript&cd nodejs-socketio && npm install
~ D:\workspace\javascript\nodejs-socketio&npm install socket.io
修改app.js配置文件
~ vi app.js
var app = require('express')()
, server = require('http').createServer(app)
, io = require('socket.io').listen(server);
server.listen(80);
app.get('/', function (req, res) {
res.sendfile(__dirname + '/client/index.html');
io.sockets.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
增加客户端文件,注意这个的index.html要根据app.js指定的位置”res.sendfile(__dirname + ‘/client/index.html’);”
~ mkdir client
~ vi /client/index.html
&!DOCTYPE html&
&title&socket.io&/title&
&link rel='stylesheet' href='/stylesheets/style.css' /&
&h1&socket.io&/h1&
&p&Welcome to socket.io&/p&
&script src="/socket.io/socket.io.js"&&/script&
var socket = io.connect('http://localhost');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
启动服务器
~ D:\workspace\javascript\nodejs-socketio&node app.js
- socket.io started
打开浏览器: http://localhost
查看服务器日志:
debug - served static content /socket.io.js
debug - client authorized
- handshake authorized ZR-xQhsKCCqM03TRHW4b
debug - setting request GET /socket.io/1/websocket/ZR-xQhsKCCqM03TRHW4b
debug - set heartbeat interval for client ZR-xQhsKCCqM03TRHW4b
debug - client authorized for
debug - websocket writing 1::
debug - websocket writing 5:::{"name":"news","args":[{"hello":"world"}]}
{ my: 'data' }
debug - emitting heartbeat for client ZR-xQhsKCCqM03TRHW4b
debug - websocket writing 2::
debug - set heartbeat timeout for client ZR-xQhsKCCqM03TRHW4b
debug - got heartbeat packet
debug - cleared heartbeat timeout for client ZR-xQhsKCCqM03TRHW4b
debug - set heartbeat interval for client ZR-xQhsKCCqM03TRHW4b
测试成功。
6. 最后总结
今天尝试了4种,基于nodejs的websocket的框架。
node-websocket-server:是直接放弃的。
node-websocket:需要依赖于底层的C++,Python的环境,支持以node做客户端的访问。
faye-websocket-node:是faye软件框架体系的一部分,安装简单,不需要其他依赖库。
socket.io:功能强大,支持集成websocket服务器端和Express3框架与一身。
websocket我也是初次尝试,对于开发效率,代码结构,稳定性,服务器性能都需要做更多的测试。目前还无法定论,哪个框架是最好的,不过我比较看好socket.io和faye-websocket-node的未来前景。
转载请注明出处:
This entry was posted in
Designed by如何利用Node.js 构建分布式集群
 作者: 肖授 编辑:
  【IT168厂商动态】在软件定义的世界里,企业通过Web应用和移动应用程序来提供大部分的服务,而Node.js迅速成为时下最为流行的一个平台之一,就和它可以搭建响应速度快、易于扩展的web应用和移动应用很很大关系,并凭借这点成为了新的主流。作为大规模使用Node.js 的服务提供商,UCloud积累了丰富的使用经验。  本文为UCloud 公司高级工程师文天乐在深JS大会上发表的演讲内容,主要介绍了UCloud内部如何利用Node.js 构建分布式集群,并分享了实践过程中走过的坑,希望对正在使用Node.js或是即将使用Node.js的朋友有一些帮助。  图:UCloud高级工程师文天乐  UCloud内部大规模使用了Node.js 技术,利用Node.js研发了一套RPC框架,主要涉及API、Web Console、服务中间层、运营报表、内部运营工具和内部系统等,解决以下四个问题:  1. 服务调动发现程序间解耦;  2. 自动快速扩容服务能力;  3. 脚本语?言提高研发效率;  4. 配置集中管理变更应用自动加载。  架构演进  在RPC框架V1版本的架构中,如下图。从图中可以看出,是一个金字塔架构,也就意味所有通信服务需要首先和名字服务进行通信,获取到对端节点状态和IP端口信息,然后再进行通信,这样导致系统的高耦合,增加了系统的复杂性,这并不是一件好事。  为此,我们改进了RPC框架架构,如图2。在V2版本中,可以看到改进的架构已是一个网状架构,实现了将所有消息出入口统一到RabbitMQ Server ,以便所有的通信可以在不知道对端节点状态时,就可以调用对端服务,从而实现了服务端调用关系解耦。  实现方案  那么到底是如何实现服务端调用解耦的呢?在实现方案中,我们采用了(Node.js + Protocol Buffers + Zookeeper + RabbitMQ)的组合,从而实现配置集中化管理:  1.Node.js,主要用于开发业务逻辑。  作为天生的异步脚本语言,Node.js 使用事件驱动、 非阻塞I/O模型大大提升了研发效率,非常适合在分布式设备上运行的数据密集型的实时应用。  我们通过 fibers库采用协程的方式来解决Node.js 异步编程匿名回调问题,将异步回调逻辑转化为同步,同时也满足了程序员使用同步方法编写异步程序的情怀。  可参考官方介绍:https://nodejs.org/  /laverdet/node-fibers  2.Protocol Buffers,用于强约束消息定义。  Protocol Buffers一种数据交换的格式,它独立于语言,独立于平台。由于它是一种二进制的格式,相比XML和JSON,传输效率会更高,可以将它用于分布式应用之间的数据通信或者异构环境下的数据交换。我们主要将Protocol Buffers用来模版化定义消息结构。  可参考:/google/protobuf  3.Zookeeper,实现配置集中管理。  Zookeeper分布式服务框架是Apache Hadoop 的一个子项目,简单的说,Zookeeper=文件系统+通知机制。它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。  我们使用ZooKeeper看重的是它不仅支持集群高可用,还支持持久化节点、临时节点存储和节点变更监控的特点,主要使用了它提供的命名服务、配置管理和集群管理服务。其中,临时节点特性用以实现名字服务注册,节点变更监控实现配置集中管理。  参考:https://zookeeper.apache.org  4.RabbitMQ,实现异构通讯服务间的解耦。  Rabbitmq是一种应用程序对应用程序的通信方法,选择RabbitMQ的原因在于它可以支持集群高可用、简单易用、性能出色和完善的管理工具(如:Web ui / Rest API )的特点。  使用Rabbitmq中间件服务端实现解耦,其中主要是利用( Work Queue + Topics Exchange )来实现后端的无缝扩容,并采用Publish/ Subscribe + RPC 实现调用解耦,并利用MQ 统一输入输出。  参考:  走过的一些坑  最后,总结经验避免犯同样的错,是非常重要的,还有一些技术遗留问题,需要我们自行避开这些坑。以下是我们在构建RPC框架过程中遇到的一些坑:  异步编程效率问题(Fibers)& Node.js 泄漏问题  在复杂在构建复杂应用的时候,很多地方都可能发生泄露,也需要考虑异步编程效率问题。为解决这两个问题,我们目前主要采取以下四个手段来解决:  a)框架封装所有,业务方只关注业务逻辑、提高研发效率;  b)通过Fibers 封装所有异步匿名函数调用转换为同步方法;  c)谨慎选择第三方库。  异步框架中日志跟踪  异步程序记录日志乱序不利于跟踪业务逻辑调用路径。为解决这个问题,我们通过包装 Fibers 对每一个 Fiber 实例进行编号,在所有日志输出中打印 Fiber id 记录异步调用路径,并配合跨模块会话编号实现请求调用跟踪,以此解决日志纪录的无序问题。  RabbitMQ HA 高可用问题  如果需要实现RabbitMQ HA 高可用特性,有两种途径可以实现:Server 端 HA 和 Client HA。Server 端的高可用性可使用 LVS 或 HAProxy来实现,Client 端的高可用性也是一种选择,这样可以减少架构复杂度和层次依赖。值得注意的是,实现高可用特性时,要记得开启Queue 高可用配置。  (/ha.html)  RabbitMQ HA 网络闪断导致节点分区问题  网络不稳定导致RabbitMQ HA 网络闪断,进而导致节点分区问题。针对这个问题,需要添加对 /api/nodes 进行监控,并及时处理分区问题。  具体的解决方法可参考: /partitions.html。  ZooKeeper Session Expired  针对ZooKeeper 会话过期问题,需要大家特别关注处理Zookeeper 集群断开后的重连处理,因为如果重连逻辑没有处理好的话,所有依赖ZooKeeper的特性都将不可用。  具体解决方法可参考:http://wiki.apache.org/hadoop/ZooKeeper/FAQ  【结语】  经过应用实践,目前看来 Node.js几乎可以做到其他后端语言所能做到所有的事情,ES6特性正式发布如今有人已经开始高喊“JavaScript: The World's Best Programming Language”,但我也并不认为整个后端完全用Node.js来实现会是一个很好的方案。  本文中提到了Node.js的诸多优点,如异步、非阻塞和事件驱动等,但其也存在一些缺点,如默认单进程单线程不能利用多核,脚本弱类型容易出现运行时BUG,同时因为它简单易用,也导致了代码质量不易控制,对开发人员也提出了更高的要求。所以,就个人经验来看,建议偏复杂业务逻辑控制使用Node.js,如果是偏极致性能的业务建议和C++等其他方案结合使用。
大学生分期购物销量榜
IT168企业级
正在努力加载文档,请稍等…

我要回帖

更多关于 分布式聊天系统 的文章

 

随机推荐