Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SignalR with paging using Groups

I am new to SignalR and in the learning process, I am trying to make Stock ticker. I have over 1000 stocks list and want to show paging and update on real-time changes that's why I am using Groups with SignalR. Problem is that when I open page 1 then everything works fine until I open page 2, then page 1 stop getting signals and page 2 start getting signals. What am I missing here in SignalR groups? Please help me, Thanks.

Hub

[HubName("stockTicker")]
public class StockTickerHub : Hub
{
    private StockTicker _stockTicker;

    public StockTickerHub()
        : this(StockTicker.Instance)
    {

    }

    public StockTickerHub(StockTicker stockTicker)
    {
        _stockTicker = stockTicker;
    }

    public void OpenMarket()
    {
        var page = this.Context.QueryString["page_no"];
        int pageno = Convert.ToInt32(page);
        _stockTicker.OpenMarket(pageno);
        tryAddGroup(page);
    }

    public Task tryAddGroup(string page)
    {
        return Groups.Add(Context.ConnectionId, page);
    }
}

StockTicker.cs

public class StockTicker
{

    MainModel _model = new MainModel();
    private static Lazy<StockTicker> _instance = new Lazy<StockTicker>(() => new StockTicker(GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>().Clients));
    private TimeSpan _updateInterval = TimeSpan.FromMilliseconds(3000);

    private Timer _timer;
    public volatile int pageno;
    private StockTicker(IHubConnectionContext<dynamic> clients)
    {
        Clients = clients;
    }

    public static StockTicker Instance
    {
        get
        {
            return _instance.Value;
        }
    }

    private IHubConnectionContext<dynamic> Clients
    {
        get;
        set;
    }


    public void OpenMarket(int page_no)
    {
        pageno = page_no;
        _timer = new Timer(UpdateStockPrices, pageno, _updateInterval, _updateInterval);
    }


    private void UpdateStockPrices(object state)
    {

        var latest_stocks = new List<StockViewModel>();
        latest_stocks = _model.Get100Stocks(pageno);
        foreach (var stock in latest_stocks)
        {
            BroadcastStockPrice(stock, pageno);
        }
    }
    private void BroadcastStockPrice(StockViewModel stock, int pageno)
    {
        Clients.Group(pageno.ToString()).updateStockPrice(stock);
        //Clients.All.updateStockPrice(stock);
    }
}

(Updated Question)

StockTicker.js

if (!String.prototype.supplant) {
    String.prototype.supplant = function (o) {
        return this.replace(/{([^{}]*)}/g,
            function (a, b) {
                var r = o[b];
                return typeof r === 'string' || typeof r === 'number' ? r : a;
            }
        );
    };
}

jQuery.fn.flash = function (color, duration) {
    var current = this.css('backgroundColor');
    this.animate({ backgroundColor: 'rgb(' + color + ')' }, duration / 2)
        .animate({ backgroundColor: current }, duration / 2);
};

$(function () {
    var ticker = $.connection.stockTicker;
    var $stockTable = $('#stockTable');
    var $stockTableBody = $stockTable.find('tbody');

    tdPrice = '<td data-rank-price="{currency_query}" data-sort="{currency_price_usd}"><div data-price-spn="{currency_query}">${currency_price_usd}</div></td>';
    tdPercentage = '<td data-rank-perc="{currency_query}" data-sort="{currency_change_24h_usd}"><div data-change-spn="{currency_query}"><span class="{DirectionClass}">{currency_change_24h_usd}</span></div></td>';
    tdVolume = '<td data-rank-volume="{currency_query}" data-sort="{currency_24h_volume_usd}"><div data-24-volume-spn="{currency_query}>${currency_24h_volume_usd}</div></td>';
    tdMarketcap = '<td data-rank-cap="{currency_query}" data-sort="{currency_market_cap_usd}"><div data-mcap-spn="{currency_query}">${currency_market_cap_usd}</div></td>';

    function formatStock(stock) {
        return $.extend(stock, {
            currency_price_usd: formatprices(stock.currency_price_usd),
            currency_change_24h_usd: stock.currency_change_24h_usd == null ? '0.00%' : (stock.currency_change_24h_usd).toFixed(2) + '%',
            currency_24h_volume_usd: stock.currency_24h_volume_usd == null ? '0' : nFormatter(stock.currency_24h_volume_usd, 2),
            currency_market_cap_usd: stock.currency_market_cap_usd == null ? '0' : nFormatter(stock.currency_market_cap_usd, 2),
            DirectionClass: stock.currency_change_24h_usd === 0 ? 'nochange' : stock.currency_change_24h_usd >= 0 ? 'green' : 'red'
        });
    }

    function nFormatter(num, digits) {
        var si = [
          { value: 1, symbol: "" },
          { value: 1E3, symbol: "K" },
          { value: 1E6, symbol: "M" },
          { value: 1E9, symbol: "B" },
          { value: 1E12, symbol: "T" },
          { value: 1E15, symbol: "P" },
          { value: 1E18, symbol: "E" }
        ];
        var rx = /\.0+$|(\.[0-9]*[1-9])0+$/;
        var i;
        for (i = si.length - 1; i > 0; i--) {
            if (num >= si[i].value) {
                break;
            }
        }
        return (num / si[i].value).toFixed(digits).replace(rx, "$1") + si[i].symbol;
    }

    function formatprices(n, curr) {
        var sep = sep || ".";
        var decimals;
        if (n > 0.99999999) {
            decimals = decimals || 2;
        }
        else {
            decimals = decimals || 6;
        }

        return n.toLocaleString().split(sep)[0]
        + sep
        + n.toFixed(decimals).split(sep)[1];
    }

    $.extend(ticker.client, {
        updateStockPrice: function (stock) {
            var displayStock = formatStock(stock),
                $tdprice = $(tdPrice.supplant(displayStock)),
                $tdpercentage = $(tdPercentage.supplant(displayStock)),
                $tdvolume = $(tdVolume.supplant(displayStock)),
                $tdcap = $(tdMarketcap.supplant(displayStock));

            if (stock.LastChange != 0.0) {
                bgprice = stock.LastChange < 0.0
                        ? '255,148,148'
                        : '154,240,117';
                $stockTableBody.find('td[data-rank-price=' + stock.currency_query + ']').replaceWith($tdprice);
                $tdpricespn = $stockTableBody.find('div[data-price-spn=' + stock.currency_query + ']');
                $tdpricespn.flash(bgprice, 1500);
            }

            if (stock.LastChangePercentage != 0.0) {
                bgper = stock.LastChangePercentage < 0.0
                        ? '255,148,148'
                        : '154,240,117';
                $stockTableBody.find('td[data-rank-perc=' + stock.currency_query + ']').replaceWith($tdpercentage);
                $tdpercentagespn = $stockTableBody.find('div[data-change-spn=' + stock.currency_query + ']');
                $tdpercentagespn.flash(bgper, 1500);
            }

            if (stock.LastChangeVolume != 0.0) {
                bgvol = stock.LastChangeVolume < 0.0
                        ? '255,148,148'
                        : '154,240,117';
                $stockTableBody.find('td[data-rank-volume=' + stock.currency_query + ']').replaceWith($tdvolume);
                $tdvolumespn = $stockTableBody.find('div[data-24-volume-spn=' + stock.currency_query + ']');
                $tdvolumespn.flash(bgvol, 1500);
            }


            if (stock.LastChangeCap != 0.0) {
                bgcap = stock.LastChangeCap < 0.0
                        ? '255,148,148'
                        : '154,240,117';
                $stockTableBody.find('td[data-rank-cap=' + stock.currency_query + ']').replaceWith($tdcap);
                $tdcapspn = $stockTableBody.find('div[data-mcap-spn=' + stock.currency_query + ']');
                $tdcapspn.flash(bgcap, 1500);
            }
        }
    });

    $.connection.hub.url = 'http://localhost:13429/signalr';
    $.connection.hub.qs = { 'page_no': $("#page").val() };
    $.connection.hub.start().done(function () {
        console.log("connected");
    }).then(function () {
        return ticker.server.openMarket();
    });
});
like image 532
aadi1295 Avatar asked Mar 16 '18 19:03

aadi1295


People also ask

How many groups can SignalR handle?

So if your SignalR service can handle 40k+ users, it will handle 40k+ groups.

What are groups in SignalR?

Groups in SignalR provide a method for broadcasting messages to specified subsets of connected clients. A group can have any number of clients, and a client can be a member of any number of groups. You don't have to explicitly create groups.

Is SignalR full duplex?

SignalR supports full duplex communication that allows the client and server to transfer data back and forth.

Is SignalR asynchronous?

SignalR is an asynchronous signaling library for ASP.NET that our team is working on to help build real-time multi-user web application.


1 Answers

Looking at your code, your global variable pageno is always the last page that was requested:

 public void OpenMarket(int page_no)
    {
        pageno = page_no;
        _timer = new Timer(UpdateStockPrices, pageno, _updateInterval, _updateInterval);
    }

You need another variable that tracks the max page.

 public volatile int pageMax;


public void OpenMarket(int page_no)
    {
        pageno = page_no;
        pageMax = pageno > pageMax
                  : pageno
                  ? pageMax;
        _timer = new Timer(UpdateStockPrices, pageno, _updateInterval, _updateInterval);
    }

Then make sure to broadcast the correct stocks to the pages.

like image 113
Wen W Avatar answered Oct 12 '22 23:10

Wen W