信号灯集线器& Twitter流媒体

时间:2013-11-01 13:32:28

标签: c# asp.net-mvc twitter signalr signalr-hub

所以我试图将Signalr与Twitter Streaming API一起使用,具体来说,我正在使用Tweetinvi C#API(http://tweetinvi.codeplex.com/)。

该应用的目的是通过特定关键字实时过滤推文到页面。

TweetInvi库是一种享受,我有一个命令行应用程序成功打印出包含某些关键字的推文。

我的用法基本概要如下:

我有一个带有单页的MVC Web应用程序,带有文本输入和按钮(用于更新过滤器),然后调用Signalr Hub中的Hub方法,如果没有已存在的流,则启动流点击第二个按钮停止它。

所有这一切都正常,除非是信号器部件。

public class TweetHub : Hub
{
    private IStreamManager _streamManager;

    public void AddTweet(String tweet, double lat, double lon)
    {
        Clients.All.addTweet(tweet, lat, lon);
    }

    public void StartStream(String[] filters)
    {
        string accessToken = ConfigurationManager.AppSettings["AccessToken"];
        string accessTokenSecret = ConfigurationManager.AppSettings["AccessTokenSecret"];
        string consumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
        string consumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];

        IToken token = new Token(accessToken, accessTokenSecret, consumerKey, consumerSecret);

        if (_streamManager != null && _streamManager.StreamIsOpen())
        {
            _streamManager.StopStream();
            _streamManager.StartStream(token, filters, tweet => AddTweet(tweet.Text, tweet.LocationCoordinates.Lattitude, tweet.LocationCoordinates.Longitude));
        }
        else if (_streamManager != null && !_streamManager.StreamIsOpen())
        {
            _streamManager.StartStream(token, filters, tweet => AddTweet(tweet.Text, tweet.LocationCoordinates.Lattitude, tweet.LocationCoordinates.Longitude));
        }
        else
        {
            _streamManager = new StreamManager();
            _streamManager.StartStream(token, filters, tweet => AddTweet(tweet.Text, tweet.LocationCoordinates.Lattitude, tweet.LocationCoordinates.Longitude));
        }
    }

    public void StopStream()
    {
        if (_streamManager != null && _streamManager.StreamIsOpen())
        {
            _streamManager.StopStream();
        }
    }
}

这是我的Signalr Hub的代码。正如我所说,使用js我可以触发启动和停止流方法。

这是我的StreamManager类的代码:

public class StreamManager : IStreamManager
{
    private StreamClient _streamClient;
    private bool _streamOpen = false;

    public void StartStream(IToken token, String[] filters, Action<ITweet> action)
    {
        if (_streamClient == null)
            _streamClient = new StreamClient(token, filters, new FilteredStream());

        _streamClient.StartStream(action);
        _streamOpen = true;
    }

    public void StopStream()
    {
        if (_streamClient != null)
        {
            _streamClient.StopStream();
            _streamOpen = false;
        }
    }

    public bool StreamIsOpen()
    {
        return _streamOpen;
    }

    public void Dispose()
    {
        if (_streamOpen)
        {
            StopStream();
        }
        _streamClient.Dispose();
        _streamClient = null;
    }
}

我的StreamClient类的代码:

public class StreamClient : IStreamClient
{
    private IFilteredStream _filteredStream;
    private IToken _token;
    private bool _streamOpen = false;


    public StreamClient(IToken token, String[] filters, IFilteredStream filteredStream)
    {
        _token = token;
        _filteredStream = filteredStream;
        AddFilters(filters);
    }

    private void AddFilters(String[] filters)
    {
        for (int i = 0; i < filters.Length; ++i)
        {
            _filteredStream.AddTrack(filters[i]);
        }
    }

    public void StartStream(Action<ITweet> action)
    {
        _filteredStream.StartStream(_token, action);
        _streamOpen = true;

    }

    public void StartStream(Func<ITweet, bool> predicateFunc)
    {
        _filteredStream.StartStream(_token, predicateFunc);
        _streamOpen = true;
    }

    public void StopStream()
    {
        _filteredStream.StopStream();
        _streamOpen = false;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // free managed resources
            if (_streamOpen)
            {
                _filteredStream.StopStream();
                _filteredStream = null;
                _token = null;
            }
        }
    }

上面的代码是直接调用Tweetinvi库的地方。

我的问题是,当我将Hub方法作为Action参数传递给StreamManager的StartStream方法时,AddTweet方法永远不会被命中。

正如我所说,当使用命令提示符应用程序作为客户端并使用此代码时,一切正常:

static void Main(string[] args)
{

    string accessToken = ConfigurationManager.AppSettings["AccessToken"];
    string accessTokenSecret = ConfigurationManager.AppSettings["AccessTokenSecret"];
    string consumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
    string consumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];


    IToken token = new Token(accessToken, accessTokenSecret, consumerKey, consumerSecret);

    String[] filters = new string[2]
    {
            "test",
            "twitter"
    };

    StreamClient streamClient = new StreamClient(token, filters, new FilteredStream());
        streamClient.StartStream(tweet => TestMethod());
}

public static void TestMethod()
{
    Console.WriteLine("test");
}

这非常有效,并且在接收到这些关键字后打印出推文。

这让我相信这是我使用Signalr的方式的一个问题,信号器方法永远不会受到打击,因为流肯定会被打开,我只是偷偷摸摸地怀疑它是否与集线器的使用寿命以及我使用它的方式。

我怀疑这是因为,虽然我的Hub中的StartStream方法被称为正常,并且更新了被单击的按钮,但当我再次单击以调用StopStream时,StopStream方法被命中,但我的“_streamManager”成员变量为null如果集线器在其使用寿命期间维持状态,则不应该如此,我想这不是。

无论是那个还是它被处理掉,然后流不再存在。

我对Signalr没有足够的经验来正确调试。

提前感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

集线器类的实例是瞬态的。这意味着每次调用该集线器类中的方法或发生连接事件时,SignalR都会创建集线器类的实例(例如onConnected,onDisconnected)。当被调用的方法完成其工作时,该中心实例被处理掉。 阅读:http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#transience

因此,您不应尝试维护有关集线器类中连接的状态信息。在你的情况下,将是“_streamManager”。 因此,我认为您应该将所有业务逻辑移动到另一个类(不是从Hub派生)。在方法中对它们进行封装,并从signalR方法中调用它们。

如果需要从服务器代码调用集线器中的方法,则应首先获取集线器上下文。 请在此处查看如何执行此操作:http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#callfromoutsidehub

希望这有帮助!