2016-05-23 77 views
2

我想使用服務器發送事件發送消息到JS客戶端。客戶只能獲得每6或7次事件。我究竟做錯了什麼?使用IServerEvents.NotifyChannel丟失事件

的特性可以用一個簡單的獨立樣本進行復制:

using System; 
using System.Threading; 

using Funq; 
using ServiceStack; 

namespace ServerSentEvents 
{ 
    public class AppHost : AppSelfHostBase 
    { 
     /// <summary> 
     /// Default constructor. 
     /// Base constructor requires a name and assembly to locate web service classes. 
     /// </summary> 
     public AppHost() 
      : base("ServerSentEvents", typeof(AppHost).Assembly) 
     { 

     } 

     /// <summary> 
     /// Application specific configuration 
     /// This method should initialize any IoC resources utilized by your web service classes. 
     /// </summary> 
     /// <param name="container"></param> 
     public override void Configure(Container container) 
     { 
      SetConfig(new HostConfig 
      { 
#if DEBUG 
       DebugMode = true, 
       WebHostPhysicalPath = "~/../..".MapServerPath(), 
#endif 
      }); 

      container.Register<IServerEvents>(c => new MemoryServerEvents()); 
      Plugins.Add(new ServerEventsFeature 
      { 
       OnPublish = (res, msg) => 
       { 
        // Throws an exception 
        //res.Write("\n\n\n\n\n\n\n\n\n\n"); // Force flush: http://stackoverflow.com/questions/25960723/servicestack-sever-sent-events/25983774#25983774 
        //res.Flush(); 
       } 
      }); 

      container.Register(new FrontendMessages(container.Resolve<IServerEvents>())); 
     } 
    } 

    public class FrontendMessage 
    { 
     public string Level { get; set; } 
     public string Message { get; set; } 
    } 

    public class FrontendMessages 
    { 
     private readonly IServerEvents _serverEvents; 
     private Timer _timer; 

     public FrontendMessages(IServerEvents serverEvents) 
     { 
      if (serverEvents == null) throw new ArgumentNullException(nameof(serverEvents)); 
      _serverEvents = serverEvents; 

      var ticks = 0; 
      _timer = new Timer(_ => Info($"Tick {ticks++}"), null, 500, 500); 
     } 

     public void Info(string message, params object[] parameters) 
     { 
      var frontendMessage = new FrontendMessage 
      { 
       Level = "success", 
       Message = message 
      }; 

      Console.WriteLine("Sending message: " + frontendMessage.Message); 
      _serverEvents.NotifyChannel("messages", frontendMessage); 
     } 
    } 
} 

而且客戶端:

<!DOCTYPE html> 

<html lang="en" xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
    <meta charset="utf-8" /> 
    <title></title> 
    <script src="js/jquery-1.11.1.min.js"></script> 
    <script src="js/ss-utils.js"></script> 
</head> 
<body> 
<script> 
    // Handle messages 
    var msgSource = new EventSource('event-stream?channel=messages&t=' + new Date().getTime()); 
    $(msgSource).handleServerEvents({ 
     handlers: { 
      FrontendMessage: function (msg) { 
       console.log('Message from server', msg); 
      } 
     } 
    }); 
</script> 
</body> 
</html> 

控制檯日誌是這樣的:

Message from server Object {Level: "success", Message: "Tick 28"} 
Message from server Object {Level: "success", Message: "Tick 35"} 
Message from server Object {Level: "success", Message: "Tick 42"} 
Message from server Object {Level: "success", Message: "Tick 49"} 
+1

你檢查了[這個問題](http:// stackoverflo w.com/questions/25960723/servicestack-sever-sent-events?lq=1)?它看起來類似於 –

+0

是的,正如你在示例中看到的,我包含了建議的OnPublish代碼。我已經評論了它,因爲它在這種情況下拋出異常。它並沒有拋棄我的真實世界代碼思想。但它也不起作用。 –

回答

2

的問題是你正試圖在之前發送消息至甚至已被註冊,因爲您立即以AppHost.Configure()立即開始註冊,而不是在AppHost初始化後立即註冊。問題的實際原因是,啓動計時器時IdleTimeout未正確初始化,導致每個服務器事件連接的使用期限爲00:00:00,這意味着它們將收到消息而不是自動處理並自動重新連接再次 - 整個過程約需6-7蜱:)

ServiceStack Plugins沒有登記在添加時,他們得到AppHost.Configure()這給其他插件的機會之前,添加/刪除/檢查其他插件後一起註冊他們已經註冊。您也不需要註冊MemoryServerEvents,因爲這是默認設置,並且recommended way to initialize a timer with an interval將在計時器回調中使用timer.Change()

考慮到這一點,我會重寫你的APPHOST爲:

public class AppHost : AppSelfHostBase 
{ 
    public AppHost() 
     : base("ServerSentEvents", typeof(AppHost).Assembly) { } 

    public override void Configure(Container container) 
    { 
     SetConfig(new HostConfig { 
#if DEBUG 
      DebugMode = true, 
      WebHostPhysicalPath = "~/../..".MapServerPath(), 
#endif 
     }); 

     Plugins.Add(new ServerEventsFeature()); 
     container.Register(c => new FrontendMessages(c.Resolve<IServerEvents>())); 
    } 
} 

,有你的FrontendMessages只有開始時Start()顯式調用,即:

public class FrontendMessage 
{ 
    public string Level { get; set; } 
    public string Message { get; set; } 
} 

public class FrontendMessages 
{ 
    private readonly IServerEvents _serverEvents; 
    private Timer _timer; 

    public FrontendMessages(IServerEvents serverEvents) 
    { 
     if (serverEvents == null) throw new ArgumentNullException(nameof(serverEvents)); 
     _serverEvents = serverEvents; 
    } 

    public void Start() 
    { 
     var ticks = 0; 
     _timer = new Timer(_ => { 
      Info($"Tick {ticks++}"); 
      _timer.Change(500, Timeout.Infinite); 
     }, null, 500, Timeout.Infinite); 
    } 

    public void Info(string message, params object[] parameters) 
    { 
     var frontendMessage = new FrontendMessage { 
      Level = "success", 
      Message = message 
     }; 

     Console.WriteLine("Sending message: " + frontendMessage.Message); 
     _serverEvents.NotifyChannel("messages", frontendMessage); 
    } 
} 

然後纔開始它的APPHOST後就一直初始化,即:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var appHost = new AppHost() 
      .Init() 
      .Start("http://*:2000/"); //Start AppSelfHost 

     appHost.Resolve<FrontendMessages>().Start(); //Start timer 

     Process.Start("http://localhost:2000/"); //View in Web browser 
     Console.ReadLine(); //Prevent Console App from existing 
    } 
} 
+0

感謝德米斯,現在它像預期的那樣工作:)還要感謝定時提示。 –