简体   繁体   中英

Signalr Hubs & Twitter Streaming

So I'm trying to use Signalr with the Twitter Streaming API, and specifically, for this I'm using the Tweetinvi C# API ( http://tweetinvi.codeplex.com/ ).

The purpose of the app is to stream tweets to a page in realtime filtered with certain keywords.

The TweetInvi library works a treat, and I have a command line application successfully printing out tweets with certain keywords in.

The basic outline of my usage is as follows:

I have an MVC web app with a single page, with a text input and a button (for updating filters) it then calls the Hub method in the Signalr Hub, to start the stream if there isn't one already present and Stops it on a second button click.

All this is working fine, except when it comes to the signalr part.

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();
        }
    }
}

That is the code for my Signalr Hub. As I said, using js I can trigger the start and stop stream methods fine.

This is the code for my StreamManager class:

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;
    }
}

The code for my StreamClient class:

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;
            }
        }
    }

This code above, is where it makes a call to the Tweetinvi library directly.

My problem is that when I pass the Hub method into the StreamManager's StartStream method as an Action parameter, the AddTweet method never gets hit.

As I said, this all works fine, when using a command prompt application as a client instead, and using this code:

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");
}

This works perfectly and prints out tweets with those keywords as they are received.

This leads me to believe that is a problem with the way I am using Signalr, that the signalr method is never getting hit, because the stream definitely gets opened, I just have a sneaky suspicion that it is something to do with the lifetime of the hub and the way I am using it.

I suspect this because, although the StartStream Method in my Hub gets called fine, and updates the button being clicked, when I think click again to call StopStream, the StopStream method gets hit, but my "_streamManager" member variable is null, which it shouldn't be IF the hub maintains state during it's lifetime, which I guess it doesn't.

Either that or it's being disposed of and then the stream wouldnt exist anymore anyway.

I don't really have enough experience with Signalr to properly debug.

Thanks in advance for any help.

Instances of a hub class are transient. That means that SignalR creates an instance of the hub class each time a method in that hub class is called or when a connection event occurs (eg onConnected, onDisconnected). When the method being called is done doing its work, that hub instance is disposed. Read this: http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#transience

So you should not try to maintain state information about a connection in your hub class. In your case that would be "_streamManager". So I think you should move all your business logic to another class (that doesn't derive from Hub).Encapsulate them in methods and call them from your signalR methods.

If you need to call a method in the hub from your sever code, you should get a hub context first. See how to do that here: http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-server#callfromoutsidehub

Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM