之前的文章介紹過asp.net SignalR, ASP .NET SignalR是一個ASP .NET 下的類庫,可以在ASP .NET 的Web項目中實現實時通信. 今天我們來實現服務端消息推送到Web端, 首先回顧一下它抽象層次圖是這樣的:

實際上 Asp.net SignalR 2 實現 服務端消息推送到Web端, 更加簡單. 為了獲取更好的可伸縮性, 我們引入消息隊列, 看如下基本流程圖:
消息隊列MQ監聽, 在Web site 服務端一收到消息,馬上通過Signalr 推送廣播到客戶端. 創建ASP.NET MVC WEB APP, 從NuGet 安裝SignalR 2.12
Install-Package Microsoft.AspNet.SignalR
具體實現代碼,是這樣的,我們增加一個空的Hub:
public class FeedHub : Hub{public void Init(){}}是簡單的消息模型, 標題與正文屬性:
[Serializable]public class PushMessageModel{public int Id { get; set; }public string MSG_TITLE { get; set; }public string MSG_CONTENT { get; set; }}
服務端推送具體類,記錄日志, 創建消息隊列實例,監聽, 等待收取消息. 這里我們使用的是AcitveMQ的.net客戶端. ActiveMQListenAdapter是一個封裝過的對象.
public class MQHubsConfig{private static ILogger log = new Logger("MQHubsConfig");/// <summary>/// Registers the mq listen and hubs./// </summary>public static void RegisterMQListenAndHubs(){var activemq = Megadotnet.MessageMQ.Adapter.ActiveMQListenAdapter<PushMessageModel>.Instance(MQConfig.MQipAddress, MQConfig.QueueDestination);activemq.MQListener += m =>{log.InfoFormat("從MQ收到消息{0}", m.MSG_CONTENT);GlobalHost.ConnectionManager.GetHubContext<FeedHub>().Clients.All.receive(m);};activemq.ReceviceListener<PushMessageModel>();}}
上面有一句關鍵代碼GlobalHost.ConnectionManager.GetHubContext<FeedHub>().Clients.All.receive(m); 這里使用了GetHubContext方法后,直接來廣播消息.
需要在MVCapplication下加載:
public class MvcApplication : System.Web.HttpApplication{protected void Application_Start(){AreaRegistration.RegisterAllAreas();FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);RouteConfig.RegisterRoutes(RouteTable.Routes);BundleConfig.RegisterBundles(BundleTable.Bundles);MQHubsConfig.RegisterMQListenAndHubs();}}
同時需要增加一個Starup.cs, 用于Owin[assembly: OwinStartup(typeof(RealTimeApp.Startup))]namespace RealTimeApp{public class Startup{public void Configuration(IAppBuilder app){// Any connection or hub wire up and configuration should go hereapp.MapSignalR();}}}接下來是客戶端App.js:
function App() {var init = function () {Feed();$.connection.hub.logging = true;$.connection.hub.start().done(function() {console.log("Connected!");$(document).trigger("Connected");}).fail(function() { console.log("Could not Connect!"); });};init();};Feed.js 具體與SignalR.js通信, 創建名為receive的function, 與服務端對應
function Feed() {var chat = undefined;var init = function () {// Reference the auto-generated proxy for the hub.chat = $.connection.feedHub;// Create a function that the hub can call back to display messages.chat.client.receive = function (item) {var selector = "ul.feed-list li[data-id=" + item.Id + "]";if (!($(selector).length > 0)) {$("ul.feed-list").prepend($(".feed-template").render(item));$("ul.feed-list li:gt(3)").remove();}$.messager.show({title: 'Tips',msg: item.MSG_CONTENT,showType: 'show'});};// Start the connection.$.connection.hub.start().done(function () {chat.server.init();});};init();};
上面的javascript代碼與服務端有通信, 具體看如下圖:
        
在Index.cshtml, 我們需要引用SignalR客戶端JS, 放置hubs, 這里我們使用了jsrender, easyui.js 來呈現推送的消息.
@model dynamic
@section Scripts {  <link href="/Content/themes/default/window.CSS" rel="stylesheet" />
<link href="~/Content/themes/default/progressbar.css" rel="stylesheet" />
<link href="~/Content/themes/default/linkbutton.css" rel="stylesheet" />
<script src="~/Scripts/jquery.signalR-2.1.2.min.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="~/signalr/hubs"></script>  <script src="~/Scripts/jsrender.js"></script>  <script src="~/Scripts/jquery.easyui.min-1.4.1.js"></script>  @Scripts.Render("~/Scripts/project.js")      <script type="text/Javascript">          $(document).ready(function () {  var app = new App();
});
</script>
}
<div class="row-fluid">
<div class="span8">
<div class="widget">
<div class="widget-header">
<h2>Feed</h2>
</div>
<div class="widget-content">
<ul class="span12 feed-list"></ul>
</div>
</div>
</div>
</div>
<script class="chat-template" type="text/x-jquery-tmpl">
<li>
        <p>{{>Message}}</p>  </li>
</script>
<script class="feed-template" type="text/x-jquery-tmpl">
    <li data-id="{{>Id}}">  <div class="row-fluid">
<div class="span8">
                <h3>{{>MSG_CONTENT}}</h3>  </div>
</div>
</li>
</script>
上代碼服務端引用js的Script.Render, 需要在BundleConfig.cs中加入以下代碼:
bundles.Add(new ScriptBundle("~/Scripts/project.js") .IncludeDirectory("~/Scripts/Project", "*.js", false));
同時我們構建一個WebAPI來發送需要推送的消息, 片斷代碼:
        /// <summary>          /// SendMessage          /// </summary>          /// <param name="messagemodel">The messagemodel.</param>          /// <returns></returns>  [HttpPost]
        public IHttpActionResult SendMessage(PushMessageModel messagemodel)          {              return SendToServer(messagemodel);  }
        /// <summary>          /// Sends to server.          /// </summary>          /// <param name="messagemodel">The messagemodel.</param>          /// <returns></returns>          private IHttpActionResult SendToServer(PushMessageModel messagemodel)          {              if (ModelState.IsValid)              {                  if (messageRepository.SendMessage(messagemodel))                  {                      log.Debug("發送成功!");                      return Ok();  }
                else                  {                      log.ErrorFormat("發送失??!{0}", messagemodel);  return Content(HttpStatusCode.ExpectationFailed, new Exception("send message error"));
}
}
            else              {                  log.ErrorFormat("參數驗證失??!{0}", messagemodel);                  return Content(HttpStatusCode.BadRequest, ModelState);  }
}
發送消息到ActiveMQ的關鍵代碼:
public class MessageRepository:IMessageRepository
    {  private static ILogger log = new Logger("MessageRepository");
        /// <summary>          /// 發送消息          /// </summary>          /// <param name="messagemodel"></param>          /// <returns></returns>  public bool SendMessage(PushMessageModel messagemodel)
        {            var activemq = new ActiveMQAdapter<PushMessageModel>(MQConfig.MQIpAddress, MQConfig.QueueDestination);            return activemq.SendMessage<PushMessageModel>(messagemodel)>0;  }
}
如果您需要運行DEMO程序,需要構建基于ActiveMQ的消息隊列, 運行效果是這樣的, 我們在一個靜態html中, 發送一個Ajax到webapi服務端, 發送后

另一個website網站收到后, 列表更新, 并在右下角彈出框

IE的控制臺輸出:
HTML1300: Navigation occurred.   
File: Index   
[11:05:25 GMT+0800 (China Standard Time)] SignalR: Client subscribed to hub 'feedhub'.   
[11:05:25 GMT+0800 (China Standard Time)] SignalR: Negotiating with '/signalr/negotiate?clientProtocol=1.4&connectionData=%5B%7B%22name%22%3A%22feedhub%22%7D%5D'.   
[11:05:25 GMT+0800 (China Standard Time)] SignalR: This browser doesn't support SSE.   
[11:05:25 GMT+0800 (China Standard Time)] SignalR: Binding to iframe's load event.   
[11:05:25 GMT+0800 (China Standard Time)] SignalR: Iframe transport started.   
[11:05:25 GMT+0800 (China Standard Time)] SignalR: foreverFrame transport selected. Initiating start request.   
[11:05:25 GMT+0800 (China Standard Time)] SignalR: The start request succeeded. Transitioning to the connected state.   
[11:05:25 GMT+0800 (China Standard Time)] SignalR: Now monitoring keep alive with a warning timeout of 13333.333333333332 and a connection lost timeout of 20000.   
[11:05:25 GMT+0800 (China Standard Time)] SignalR: Invoking feedhub.Init   
Connected!   
[11:05:25 GMT+0800 (China Standard Time)] SignalR: Invoked feedhub.Init   
[11:07:12 GMT+0800 (China Standard Time)] SignalR: Triggering client hub event 'receive' on hub 'FeedHub'.   
[11:07:18 GMT+0800 (China Standard Time)] SignalR: Triggering client hub event 'receive' on hub 'FeedHub'.   
[11:07:32 GMT+0800 (China Standard Time)] SignalR: Triggering client hub event 'receive' on hub 'FeedHub'.   
[11:07:51 GMT+0800 (China Standard Time)] SignalR: Triggering client hub event 'receive' on hub 'FeedHub'.   
[11:09:25 GMT+0800 (China Standard Time)] SignalR: Triggering client hub event 'receive' on hub 'FeedHub'.
上面粗體是 最后我們發的第5條信息控制臺的輸出.
好了,到這兒, 由于篇幅有限, 示例代碼沒有全部展示, 可以在這兒下載, 需要安裝ActiveMQ
希望對您開發實時Web App有幫助.
你可能感興趣的文章:
SignalR介紹與Asp.net   
如有想了解更多軟件,系統 IT,企業信息化 資訊,請關注我的微信訂閱號:
![MegadotnetMicroMsg_thumb1_thumb1_thu[1] MegadotnetMicroMsg_thumb1_thumb1_thu[1]](http://s1.VeVb.com/20151016/1rii23v3r3w18.jpg)
  
作者:Petter Liu   
出處:http://m.survivalescaperooms.com/wintersun/   
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。   
該文章也同時發布在我的獨立博客中-Petter Liu Blog。 
新聞熱點
疑難解答