socket.io实践(一.实现简单的图表推送)
Posted 天下雨水
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket.io实践(一.实现简单的图表推送)相关的知识,希望对你有一定的参考价值。
引子:随着nodejs蓬勃发展,虽然主要业务系统因为架构健壮性不会选择nodejs座位应用服务器。但是大量的内部系统却可以使用nodejs试水,大量的前端开发人员转入全堆开发也是一个因素。
研究本例主要为后期BI软件,CRM图标系统使用nodejs socket做铺垫.主要实现的是一个分析表图的推送。
socketio.io 代码库以及官网
https://github.com/socketio/socket.io
http://socket.io/
使用redis来实现集群读写 消息 (采用订阅 分发的策略)
https://github.com/socketio/socket.io-redis
在非socket客户行为中发送socket事件(本例在http中调用)
https://github.com/socketio/socket.io-emitter
node_redis
https://github.com/NodeRedis/node_redis
1.安装和基本使用 npm install socketio.io --save
使用因为本人写的例子是Express照搬官网说明,基本代码结构如下
var app = require(\'express\')(); var server = require(\'http\').createServer(app); var io = require(\'socket.io\')(server); io.on(\'connection\', function(){ /* … */ }); server.listen(3000);
2.使用websocket初始化一个echart的图片
socket.js 简单封装下socket.io的一些基本功能,socket.io 提供了三种默认的事件(客户端和服务器都有):connect 、message 、disconnect 。当与对方建立连接后自动触发 connect 事件,当收到对方发来的数据后触发 message 事件(通常为 socket.send() 触发),当对方关闭连接后触发 disconnect 事件。
除去自定义事件之外,本例中还定义了一个额外事件init chart data,用来初始化echart图的数据。
服务端推送消息的主要方式
1.socket.emit() :向建立该连接的客户端广播
2.socket.broadcast.emit() :向除去建立该连接的客户端的所有客户端广播
3.io.sockets.emit() :向所有客户端广播,等同于上面两个的和
此处用到了socket.emit()
socket.io-redis 用来集群推送消息,使用出版/订阅的软件模式,io.adapter()是设置消息缓存方式的,默认为内存,但是集群中内存显然是无法共享的,所以使用socket.io-redis用redis作为缓存中心,注意https://github.com/socketio/socket.io-redis是基于node-redis的,socket.io-redis的说明文档很长时间没有更新了,如果redis有密码一类的雁阵,必须使用
io.adapter(adapter({ pubClient: pub, subClient: sub })); 出版订阅的缓存可以用同一个redis服务器,redis可以部署集群,本例就不在这方面说明了。node-redis2.6之后密码验证使用password为密码参数,而不是官网所写的auth_pass
var mockData = [ [10, 52, 200, 334, 390, 330, 220] ]; var ioCreater = function(server) { var io = require(\'socket.io\')(server); io.on(\'connection\', function (socket) { socket.emit(\'init chart data\', {data:mockData[0]}); //socket客户端发送一个消息 init chart data ,内容为一组数据 socket.on(\'disconnect\', function(){ //断开socket连接的时候触发 console.log(\'user disconnected\'); }); socket.on(\'message\', function(){ //接收socket连接消息的时候触发 console.log(\'received a message\'); }); socket.on(\'connect\', function(){ //建立socket连接时候触发 console.log(\'connect a socket client\'); }); });
var redis = require(\'redis\');
var adapter = require(\'socket.io-redis\');
var pub = redis.createClient({host:"192.168.0.13", port:"6379", password: "123456" });
var sub = redis.createClient({host:"192.168.0.13", port:"6379", password: "123456" ,return_buffers: true});
io.adapter(adapter({ pubClient: pub, subClient: sub })); //使用socket.io-adapter设置缓存依赖
return io;
}
module.exports = ioCreater;
app.js express项目主入口,创建8080端口web服务器的时候创建一个socket服务
var socket = require(\'./socket/socket\'); var port = normalizePort(process.env.PORT || \'8080\'); app.set(\'port\', port); var server = http.createServer(app); var io = socket(server); server.listen(port);
推送入口,一个express的路由charts.js,其中2个路由,
simpleBarChart转跳simpleBarChart页面,因为用了handlebars的模版引擎设置layout:false来不包含layout模版片段。
pollingChartData 为推送socket消息请求路由,使用socket.io-emitter.js 来发送事件,注意socket.io-emitter.js也长时间没有人维护了,修改了其中一段代码来实现支持redis密码验证.
socket.io-emitter.js 59行修改如下
if (!redis) { if (!opts.socket && !opts.host) throw new Error(\'Missing redis `host`\'); if (!opts.socket && !opts.port) throw new Error(\'Missing redis `port`\'); redis = opts.socket ? client(opts.socket) : client(opts); }
使用count基数器计数,每次推送重mockdata中取模得到新的数据,通过emitter redis服务器,推送消息
var express = require(\'express\');
var router = express.Router();
var server = express();
server.use(\'/charts\', router);
var emitter = require(\'../tools/socket.io-emitter.js\'); //此处修改来自socket.io-emitter.js
var mockData = [
[10, 52, 200, 334, 390, 330, 220],
[10, 52, 200, 334, 390, 330, 220],
[88, 32, 87, 432, 4, 30, 87],
[42, 52, 87, 23, 390, 42, 87],
[42, 52, 200, 334, 390, 42, 876],
[53, 52, 321, 324, 42, 330, 32],
[44, 87, 4, 32, 390, 42, 32],
[53, 87, 42, 334, 54, 330, 220],
[530, 52, 54, 43, 43, 330, 32],
[88, 13, 233, 98, 43, 44, 32]
];
var count =1;
const disableLayout ={layout: false};
// disable interface layout.hbs user config layout: false
router.get(\'/simpleBarChart\', function(req, res, next) {
var result = Object.assign({title: \'chart demo\'},disableLayout);
res.render(\'charts/simpleBarChart\',result);
});
router.get(\'/pollingChartData\', function(req, res, next) {
//10.10.10.96:8080
var io = emitter({ host: \'192.168.0.13\', port: 6379, password: "123456" }); //创建一个emitter事件服务器,
setTimeout(function(){
count++;
io.emit(\'new chart data\', {data:mockData[count%10]}); //推送一条消息
res.render(\'index\', { title: \'推送一条消息成功!\'});
}, 500);
});
module.exports = server;
前端页面,express引用socket.io后,自动可以通过访问/socket.io/socket.io.js 的前端库,本例中接收了2种socket消息,一种刚进入页面创建socket连接的时候接收的一条init chart data的消息,以及接收通过pollingChartData推送来的new chart data的消息.
<!DOCTYPE html> <html> <head> <!-- 声明文档使用的字符编码 --> <meta charset=\'utf-8\'> <!-- 优先使用 IE 最新版本和 Chrome --> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> <!-- 启用360浏览器的极速模式(webkit) --> <meta name="renderer" content="webkit"> <!-- 页面描述 --> <meta name="description" content="chart demo"/> <!-- 页面关键词 --> <meta name="keywords" content="chart demo"/> <!-- 网页作者 --> <meta name="author" content="name, email@gmail.com"/> <!-- 搜索引擎抓取 --> <meta name="robots" content="index,follow"/> <!-- 为移动设备添加 viewport --> <meta name="viewport" content="initial-scale=1, maximum-scale=3, minimum-scale=1, user-scalable=no"> <title>chart</title> <link rel=\'stylesheet\' href=\'/css/style.css\'/> <link rel=\'stylesheet\' href=\'/css/charts/charts.css\'/> <script type="text/javascript" src="/js/library/echart/3.3.2/echarts.min.js"></script> <script type="text/javascript" src="/socket.io/socket.io.js"></script> </head> <body> <div class="chart-wrap" id="chart-wrap"></div> <script type="text/javascript"> var mychart = echarts.init(document.getElementById("chart-wrap")); var option = { color: [\'#3398DB\'], tooltip: { trigger: \'axis\', axisPointer: { // 坐标轴指示器,坐标轴触发有效 type: \'shadow\' // 默认为直线,可选为:\'line\' | \'shadow\' } }, grid: { left: \'3%\', right: \'4%\', bottom: \'3%\', containLabel: true }, xAxis: [ { type: \'category\', data: [\'Mon\', \'Tue\', \'Wed\', \'Thu\', \'Fri\', \'Sat\', \'Sun\'], axisTick: { alignWithLabel: true } } ], yAxis: [ { type: \'value\' } ], series: [ { name: \'直接访问\', type: \'bar\', barWidth: \'60%\', data: [10, 52, 200, 334, 390, 330, 220] } ] }; var drawChart = function(data){ option.series[0].data = data; mychart.setOption(option); }; var socket = io("ws://10.10.10.96:8080"); //通过ip和端口建立一个socket client socket.on(\'disconnect\', function(){ console.log(\'user disconnected\'); }); socket.on(\'message\', function(){ console.log(\'received a message\'); }); socket.on(\'connect\', function(){ console.log(\'connect a socket client\'); }); socket.on(\'init chart data\', function(data){ console.log(\'init chart data\'); drawChart(data.data); }); socket.on(\'new chart data\', function(data){ console.log(\'new chart data\'); drawChart(data.data); }); </script> </body> </html>
最后结果,通过http://localhost:8080/charts/simpleBarChart访问获得一个简单的echart图表,
通过http://localhost:8080/charts/pollingChartData访问推送新数据重绘图表,每次发送一次请求,重新绘制一次图片。
以上是关于socket.io实践(一.实现简单的图表推送)的主要内容,如果未能解决你的问题,请参考以下文章