SignalR连续消息传递

时间:2014-06-14 11:31:39

标签: c# asp.net signalr async-await

我有一个Web项目,需要从外部soap服务更新网页上的统计信息/日志。我决定采用的方法是使用signalR,通过使用代码执行服务方法并将结果返回给所有连接的客户端。此代码将持续执行,服务调用之间会有延迟。

我无法将所有碎片放在一起,可能是因为我不确定应该去哪里!以下是我到目前为止所做的粗略分析

class Data { ... }

interface IDataService
{
    Task GetDataAsync(TimeSpan interval, CancellationToken cancellationToken, Action<Data> callback);
}

class DataService : IDataService
{
    private readonly ExternalService _externalService;

    public async Task GetDataAsync(TimeSpan interval, CancellationToken cancellationToken, Action<Data> callback)
    {
         while (!cancellationToken.IsCancellationRequested)
         {
              var data = await this._externalService.GetData();
              callback(data);

              await Task.Delay(interval).ConfigureAwait(false);
         }
    }
}

以上是从外部服务获取数据并执行回调的逻辑。至于signalR,我唯一做的就是如下

public class DataHub : Hub
{
    private readonly IDataService _service;

    public DataHub(IDataService service)
    {
        this._service = service;
    }

    public async Task GetData()
    {
        var tenSeconds = new TimeSpan(0, 0, 10);
        var token = new CancellationToken();
        await _service.GetDataAsync(tenSeconds, token, d =>
        {
             // signal all connected clients
        });
    }
}

当客户端连接时(如果它还没有运行),可以调用GetData()方法,并且当没有更多客户端连接到集线器时,可以取消令牌。

(潜在)问题:

  1. SignalR中心不是单身人士吗?我认为它们是在需要时创建的,并在不再需要时处理(请求已完成)。在这种情况下,我将运行GetData方法的多个实例(除非我将DataService类设为单例/注入为单例)。
  2. 枢纽并不是一个合适的地方。集线器应该像MVC / Web Api控制器一样对待?所以请求进来,集线器处理它,请求完成。故事结束。
  3. 另一种方法是将集线器传递到dataservice。然后它可以呼叫客户端。但我真的不想传递IHubContext对象。我希望尽可能简化服务。
  4. 这里的ASP.NET后台任务会更合适吗?
  5. 如果有人能指出我正确/最好的方法,将不胜感激!

3 个答案:

答案 0 :(得分:3)

我创建了一个将您的域与SignalR分离的库,它将为您解决问题1-3。

请在这里查看https://github.com/AndersMalmgren/SignalR.EventAggregatorProxy/wiki

问题4,你应该绝对使用ASP.NET后台工作者而不是标准线程,否则ASP不会知道它存在并回收应用程序等

答案 1 :(得分:2)

1,2,4:我将使用后台任务,然后利用Connectionmanager类来检索适当的中心。 Here您可以找到有关如何从集线器外部拨打电话的更多信息。

3:为了保持我的服务清洁,我通常会注册所谓的消息服务。然后,我的数据服务从消息服务中调用一个方法,该方法在内部解析集线器并传播消息。通过这种方式,我保持数据服务整洁,因为它们只调用特定的消息服务方法(通常是单行)。我的数据服务不需要知道我的消息服务正在做什么。

答案 2 :(得分:1)

我已经设法通过在System.Web.Hosting命名空间中使用IRegisteredObject接口来解决此问题。所以我的预定任务是:

public class DataTask : IRegisteredObject
{
     private readonly IGlobalData _global;
     private readonly IDataService _service;
     private readonly IHubContext _hub;

     private Timer _timer;

     public DataTask(IGlobalData global, IDataService service, IHubContext hub)
     {
          this._global = global;
          this._service = service;
          this._hub = hub;

          var interval = new TimeSpan(0, 0, 10);
          this._timer = new Timer(updateClients, null, TimeSpan.Zero, interval);

          // register this task with asp.net
          HostingEnvironment.RegisterObject(this);
     }

     public void Stop(bool immediate)
     {
          _timer.Dispose();

          HostingEnvironment.UnregisterObject(this);
     }

     private async void updateClients(object state)
     {
          var result = await this._service.GetData();
          // call the hub
          this._hub.Clients.All.updateData(result);
     }
}

我确实有很多问题!但这是由于我用于signalR的自定义依赖解析器(问题是没有从此任务调用客户端js函数)。一旦解决了,一切都按预期工作。