前言:最近接手了新的项目,一个MVC的Web端页面实时监控并控制着多台机器上面的多个应用程序,及分布式管理,而每台机器上面的应用都部署在计划任务上面,这里用到任务调度器,后边会更新一篇文章来介绍任务调度器在这里边的用法,每台机器上面都部署一个Wcf服务,即时通信用的SignalR也部署在Wcf中,接手的第一个任务就是把SignalR换成WebSocket。要求:每个应用程序就是一个客户端,web也是客户端,实现客户端对客户端发送消息,而每台机器上要有一个SocketServer的服务端,这样就避免了高并发;WebSocket框架我选的是Fleck简单易用,单个并发量达到6万多上限也是够用了,再加上异步就绰绰有余。
引包:
因为是在Wcf中部署,所以只需在每次启动服务时打开websocket连接即可:
static List<IWebSocketConnection> allSockets = new List<IWebSocketConnection>(); public SpiderMonitor() { var server = new WebSocketServer(AppSettings.SocketUrl); server.RestartAfterListenError = true; server.Start(socket => { socket.OnOpen = () => { allSockets.Add(socket); }; socket.OnClose = () => { allSockets.Remove(socket); }; socket.OnMessage = message => { allSockets.ToList().ForEach(s => s.Send(message)); }; }); }View Code
服务端部署好以后就是客户端了,因为是分布式,所以每台机器都有一个wcf服务,每个服务里面有一个server服务端;
客户端代码:
function handler(e) { //console.log(e.data); //日志[0] id[1] 信息[2] CreateDate[3] var reg = RegExp(/日志/); if (e.data.match(reg)) { var str = e.data.split("*"); console.log(str[2]); //将item加入到日志中 if (app.log[0].TaskId == str[1]) { app.log[0].Description = str[2]; var date = new Date(str[3]); app.log[0].CreateDate = date; } //将任务启动和停止添加到实时信息中 if (str[2] == 'start' || str[2] == 'end') { var len = app.task.length; for (var idx = 0; idx < len; idx++) { if (app.task[idx].Id == str[1]) { app.task[idx].Status = str[2]; break; } } } //任务开始(收到start消息页面按钮开始) if (str[2] == 'start') { var len = app.task.length; for (var idx = 0; idx < len; idx++) { if (app.task[idx].Id == str[1]) { app.task[idx].State = 'Running'; break; } } } //任务停止(收到end消息页面按钮停止) if (str[2] == 'end') { //$.getJSON(path + 'monitor/stop?id=' + str[1]); var len = app.task.length; for (var idx = 0; idx < len; idx++) { if (app.task[idx].Id == str[1]) { app.task[idx].State = 'Ready'; break; } } $.getJSON(path + 'monitor/getlog?id=' + str[1], function (d) { app.log = d; }); } } //爬取的信息 var str = e.data.split("*"); var id = str[0]; var Data = str[1]; console.log(Data); var len = app.task.length; for (var idx = 0; idx < len; idx++) { if (app.task[idx].Id == id) { app.task[idx].Status = Data; break; } } } var lockReconnect = false; //避免ws重复连接 var ws = null; // 判断当前浏览器是否支持WebSocket //var wsUrl = serverConfig.socketUrl; //createWebSocket(wsUrl); //连接ws function createWebSocket(url) { try { if ('WebSocket' in window) { ws = new WebSocket(url); } initEventHandle(url); } catch (e) { reconnect(url); console.log(e); } } function initEventHandle(url) { ws.onclose = function () { reconnect(url); console.log(url+" "+"连接关闭!" + new Date().toLocaleString()); }; ws.onerror = function (e) { reconnect(url); console.log(url+" "+"连接错误!"+ e.reason); }; ws.onopen = function () { console.log(url+" "+"连接成功!" + new Date().toLocaleString()); }; ws.onmessage = handler; } // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。 window.onbeforeunload = function () { ws.close(); } function reconnect(url) { if (lockReconnect) return; lockReconnect = true; setTimeout(function () { //没连接上会一直重连,设置延迟避免请求过多 createWebSocket(url); lockReconnect = false; }, 1000); } //WebSocket将信息和日志实时推送上来; $(function () { var strArray = new Array(); strArray.push('ws://10.88.22.27:5008'); strArray.push('ws://10.88.22.82:5008'); strArray.push('ws://10.88.22.38:5008'); strArray.push('ws://10.88.22.62:5008'); strArray.push('ws://10.88.22.70:5008'); strArray.push('ws://10.88.22.19:5008'); strArray.push('ws://10.88.22.30:5008'); strArray.push('ws://10.88.22.12:5008'); for (var i = 0; i < strArray.length; i++) { createWebSocket(strArray[i]); //console.log(strArray[i] + "开始创建连接"); } });View Code
前端使用的是Vue,所以数据绑定看似跟其他不一样。
最后发布出来上线时,websocket连接使用规则要跟你的项目的IP一致;即当你项目ip是localhost时,websocket链接也是本地IP,发布上线时同理;
还有一个很重要的,websocket发布时必须绕过代理,否则会连接不上;
效果:
这里的websocket链接如果不发送消息只能保持一分钟,一分钟后自动断开;如果想长时间保持连接可以定时发送一些空的信息