一般来说一个BS项目确定不止单独的一个BS应用,可能涉及到不少后台服务来支持BS的运行,特别是针对耗时较长的某些任务来讲,Windows服务确定是必不可少的,咱们还须要利用B/S与windows服务进行交互,来实现更好的用户体验,搭配redis,memcached等来实现分布式缓存,消息列队处理等等。。。javascript
可是一般状况咱们在B/S端是没法得知其依赖的windows服务当前处于什么样的运行状态,只能经过到server里面去进行查看,或者经过其余途径!html
今天咱们就经过SignalR来实现一个B/S端对windows服务运行状态的监控,这里咱们用SignalR selfHost,不依赖IIS,而后利用topshelf把SignalR Server部署成windows服务,而后在B/S端经过SignalR js client进行链接获取服务运行状态!java
首先建立一个控制台应用程序,.NET 4.5,Nuget添加 Microsoft.AspNet.SignalR.SelfHost Microsoft.Owin.Cors TopShelf(实现windows服务安装)jquery
具体新建SignalR SelfHost Server的方法能够看我之前的博客:SignalR SelfHost实时消息,集成到web中,实现服务器消息推送
web
新建一个hub命名为ServiceMonitorHub,继承Microsoft.AspNet.SignalR.Hub,咱们要实现服务状态1秒钟推送一次redis
具体代码以下windows
1 using System.Linq; 2 using System.Threading; 3 4 namespace wxRbt.Service.Realtime.Hub 5 { 6 /// <summary> 7 /// 服务监控器 8 /// </summary> 9 public class ServiceMonitorHub:Microsoft.AspNet.SignalR.Hub 10 { 11 static ServiceMonitorHub() 12 { 13 new Thread(new ThreadStart(() => 14 { 15 while (true) 16 { 17 //获取全部服务名称以wxRbt开头的服务 18 var services = System.ServiceProcess.ServiceController.GetServices().Where(t => t.ServiceName.StartsWith("wxRbt")) 19 .Select(t => new Model.Service 20 { 21 DisplayName = t.DisplayName, 22 ServiceName = t.ServiceName, 23 Status = (int)t.Status 24 }).ToArray(); 25 Microsoft.AspNet.SignalR.GlobalHost.ConnectionManager.GetHubContext<ServiceMonitorHub>().Clients.All.refresh(services); 26 //休眠一秒,实现每秒推送服务运行状态 27 System.Threading.Thread.Sleep(1000); 28 } 29 })).Start(); 30 } 31 } 32 }
如今咱们再利用TopShelf把当前的控制台安装成windows服务缓存
新建一个类ServiceMonitorService,继承Topshelf.ServiceControl接口,实现其Start跟Stop方法,具体代码以下服务器
1 using Microsoft.AspNet.SignalR; 2 using Microsoft.Owin.Cors; 3 using Microsoft.Owin.Hosting; 4 using Owin; 5 using System; 6 using Topshelf; 7 using System.Configuration; 8 9 namespace wxRbt.Service.Realtime.Service 10 { 11 public class ServiceMonitorService:ServiceControl 12 { 13 private IDisposable app; 14 private static string domain="http://*:3333"; 15 16 static ServiceMonitorService() { 17 domain = ConfigurationManager.AppSettings["Domain"] ?? domain; 18 Console.WriteLine("获取配置:"+domain); 19 } 20 21 public bool Start(HostControl hostControl) 22 { 23 Console.WriteLine("事实消息服务运行在:"+domain); 24 25 app = WebApp.Start(domain, builder => 26 { 27 builder.UseCors(CorsOptions.AllowAll); 28 builder.MapSignalR(new HubConfiguration 29 { 30 EnableJSONP = true, 31 EnableDetailedErrors = true, 32 EnableJavaScriptProxies = true 33 }); 34 }); 35 return true; 36 } 37 38 public bool Stop(HostControl hostControl) 39 { 40 if (app != null) { 41 app.Dispose(); 42 } 43 return true; 44 } 45 } 46 }
这里给个默认的监听域名,而后从app.config读取配置的监听域名微信
最后打开Progarm.cs文件,代码以下:
1 using Topshelf; 2 3 4 namespace wxRbt.Service.Realtime 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 HostFactory.Run(s => { 11 s.Service<Service.ServiceMonitorService>(); 12 s.SetDisplayName("微信实时消息服务"); 13 s.StartAutomatically(); 14 }); 15 } 16 } 17 }
调试运行程序,如图
上面服务端已经完成,下面,咱们来实现客户端:
建立一个MVC4.0web空项目(随便,我的爱好),Nuget引用Microsoft.AspNet.SignalR.JS,该js依赖jquery,会自动下载jquery,写TypeScript同窗能够顺带下载这两个JS的d.ts文件
而后建立一个HomeController,在Index里面返回view便可
Views文件夹建立Home文件夹,建立一个Index.cshtml 的razor试图,引用jquery跟signalrjs
而后建立一个单独的JS,尽可能不要把js写到页面里面去
这里我用TypeScript写一个消息模块
1 /// <reference path="../../../scripts/typings/signalr/signalr.d.ts" /> 2 3 module wxrbt.manager { 4 export const enum ServiceStatus { 5 /**服务中止*/ 6 Stopped = 1, 7 /**正在运行*/ 8 StartPending = 2, 9 /**正在中止*/ 10 StopPending = 3, 11 /**运行中*/ 12 Running = 4, 13 /**正在继续*/ 14 ContinuePending = 5, 15 /**正在暂停*/ 16 PausePending = 6, 17 /**已暂停*/ 18 Paused = 7, 19 } 20 interface IService { 21 DisplayName: string; 22 ServiceName: string; 23 Status: ServiceStatus 24 } 25 /**管理服务*/ 26 export class service { 27 private proxy: SignalR.Hub.Proxy; 28 private $: JQueryStatic; 29 private ip: string; 30 private port: number; 31 constructor(ip: string, port: number) { 32 this.ip = ip; 33 this.port = port; 34 } 35 /** 36 * 开启服务运行状态监测 37 * @param {(services} callback 38 */ 39 start(callback: (services: Array<IService>) => void) { 40 jQuery.getScript("http://" + this.ip + ":" + this.port + "/signalr/hubs", () => { 41 jQuery.connection.hub.url = "http://" + this.ip + ":" + this.port + "/signalr"; 42 this.proxy = jQuery.signalR.hub.createHubProxy("ServiceMonitorHub"); 43 44 //每次刷新数据回调 45 this.proxy.on("refresh", (services: Array<IService>) => { 46 callback(services); 47 }); 48 49 50 jQuery.connection.hub.start().fail(() => { 51 alert("链接实时消息服务期:http://" + this.ip + ":" + this.port + "失败,请确认消息服务配置正确且正常开启!"); 52 }); 53 }); 54 } 55 } 56 }
下面我结合RequireJs实现的该模块调用
1 require(["message"], () => { 2 3 jQuery(() => { 4 5 var $service = jQuery("#serviceList"); 6 var msg = new wxrbt.manager.service("127.0.0.1", 3333); 7 msg.start(services=>{ 8 $service.empty(); 9 for (let service of services) { 10 var isRunning = service.Status == wxrbt.manager.ServiceStatus.Running; 11 var statusCls = isRunning ? "success" : "warning"; 12 var statusTxt = isRunning ? "运行中" : "已中止"; 13 var status = `<label class='label label-${statusCls}'>${statusTxt}</label>`; 14 $service.append(`<li><a href='javascript:;'><i class='icon-check'></i>${service.DisplayName}${status}</a></li><li class="divider"></li>`); 15 } 16 }); 17 18 }); 19 20 21 });
最后运行页面查看效果:
惟一不足的就是1秒钟这个dropdownlist会闪动一次,我这里是先清除再append进来,因此会出现这个状况,若是采用dom节点递归更新状态就不会有这个问题了!
由于是公司项目,没办法上源码!有不清除的能够留言!
下面是在windows服务器上跑的服务截图