SignalR实现服务器推送信息:广播与“组播”

    SignaR可以用来实现服务器向客户端推送信息,但是SignalR的每个Hub的生命周期很短,不能长期停留在内存里。因此,如果服务器想要对客户端推送消息时,应该采用signalR的一个“全局”的上下文来实现。此功能的应用背景是:服务器的数据发生了更新,它希望向所有的用户、某个分组的用户或者是单个用户发送推送消息,而不是由客户端发送请求来检测数据是否有更新。这才是真正的SignalR要做的事!

在Asp.Net有关SignalR的介绍里有提到向所有用户发送推送消息的实现方式(参见: http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/tutorial-server-broadcast-with-signalr-20)。但是向分组的用户或单个用户发送消息的实现方式,并没有提供。本文将说明如何向分组推送消息,其他的两种实现很相似。

另外,在我发现Signalr有提供全局机制之前,我采用了让一个Hub长期停留在内存的方式实现。大致意思为:
    我需要实现一个Hub,它可以动态地检测数据库数据的变化,然后推送给前端相关的用户。如果直接在继承Hub的类里实现,并调用的话,也就意味着由SignalR创建的所有Hub都要长时间地停留在内存里,而且由于我的推送信息是根据组(Group)进行的。结果导致的问题就是:有太多的Hub停留在内存了;而且针对某个分组的消息,会因为有多个Hub(每个Hub的实现是一样的)而发送多次。
    后来,我想到了一个解决这个的办法,就是我只在内存里保留一个Hub的实例。这样就不会出现一条消息多次发送的问题了。具体的做法是:  
    当客户端第一次新建一个Hub时,我就将这个Hub的实例保存起来。当其他的客户端再次新建Hub时,这些Hub不再保存。当这些客户端要提供注册的分组信息时,我就直接将这些分组信息注册到我保存的那个Hub去,而我的所有推送信息只在这个Hub里发出。

    老实说,系统已经运行很久了,这种方式还一直正常地使用着,没出现什么问题。 不过,既然SignalR有提供全局的实现,那么,我们还是采用它提供的实现为好。

一、前置条件:
    如何添加SignalR的类库引用以及初始化一个Hub,参见: http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/tutorial-getting-started-with-signalr-20

二、创建Hub Class
    实现一个Hub以及一个Ticker。Hub的实现很简单,就是要求将当前的Hub连接注册到Ticker上去。Ticker采用单例实现。具体代码如下:
 
 public class ChatHub : Hub
    {
        private readonly ChatTicker ticker;

        public ChatHub()
        {
            ticker = ChatTicker.Instance;
        }

        public void Send(string group, string username)
        {
            //注册到全局
            ticker.GlobalContext.Groups.Add(Context.ConnectionId, group);
            Clients.All.broadcastMessage(group, "user register:" + username);
        }
    }

    public class ChatTicker
    {
        #region 实现一个单例

        private static readonly ChatTicker _instance =
            new ChatTicker(GlobalHost.ConnectionManager.GetHubContext());

        private readonly IHubContext m_context;

        private ChatTicker(IHubContext context)
        {
            m_context = context;
            //这里不能直接调用Sender,因为Sender是一个不退出的“死循环”,否则这个构造函数将不会退出。
            //其他的流程也将不会再执行下去了。所以要采用异步的方式。
            Task.Run(() => Sender());
        }

        public IHubContext GlobalContext
        {
            get { return m_context; }
        }

        public static ChatTicker Instance
        {
            get { return _instance; }
        }

        #endregion

        public void Sender()
        {
            int count = 0;
            while (true)
            {
                Thread.Sleep(500);
                int tag = count%2;
                //动态绑定前端的js函数 broadcaseMessage
                m_context.Clients.Group(tag + "").broadcastMessage("group is:" + tag, "current count:" + count);
                count++;
            }
        }
    }

三、前端测试代码
前端通过调用 Sender进行用户的监听注册,后台通过broadcaseMessage进行动态绑定。



    SignalR Simple Chat
    


    


    四、测试:
    前端输入分组0或1,并输入用户名。点击注册之后可以见到:
    SignalR实现服务器推送信息:广播与“组播”_第1张图片

    你可能感兴趣的:(C#.Net,.Net应用框架)