• 我的微服务项目之设计一个消息通知


           上一遍文章我讲了下自己的IdentityServer4整合到自己的项目里面,最近有改造了一下自己的原有的消息通知的功能,这里我用的是.net自带的组件Signalr,这是一款很不错的Socket组件,用起来非常简单,我这里讲一下自己的设计思路。,所以也把这个分享出来,如果大佬有觉得不妥的地方,欢迎指出。

           首先是添加项目对Signalr的支持

      services.AddSignalR();
    

      Signalr提供了一个抽象类Hub,这是一个很重要的抽象类,我们要想使用Signalr,就必须实现它。我结合自己的需求,因为需要指点的客户发送,每个用户登录之后都会连接Singalr产生一个connectionid,准确的说是每个页面都会产生一个connectionid,所以难免会有一个用户打开多个相同的页面,这样就要给每个页面都发送消息通知。所以我的做法是将用户账号和connectionid存入内存中,connectionId作为key:

          /// <summary>
            /// 连接对象集合
            /// </summary>
            public static ConcurrentDictionary<string, string> ConnectionMaps = new ConcurrentDictionary<string, string>();
            /// <summary>
            /// 添加连接对象
            /// </summary>
            /// <param name="connectionId"></param>
            /// <param name="value"></param>
            public static void SetConnectionMaps(string connectionId, string value)
            {
                ConnectionMaps.AddOrUpdate(connectionId, value, (string s, string y) => value);
            }
    

      添加一个类继承自Hub,Context是Hub一个上下文属性,SetConnectionMaps是自己定义的一个方法,以至于客户端可以调用这个方法,当你刷新或关闭页面时就会断开连接调用OnDisconnectedAsync,刷新时会产生新的connectionid:

      public class SingalrClient : Hub
        {
            public void SetConnectionMaps(string account)
            {
                string connectionid = Context.ConnectionId;
                SingalrConnection.SetConnectionMaps(connectionid, account);
            }
            public override Task OnDisconnectedAsync(Exception exception)
            {
                SingalrConnection.Remove(Context.ConnectionId);
                return base.OnDisconnectedAsync(exception);
            }
        }
    

      然后再设置路由信息,/SingalrClient代表着路由匹配的地址:

       app.UseEndpoints(endpoints =>
                {
                    endpoints.MapHub<SingalrClient>("/SingalrClient");
                });
    

        以上是完成了服务端的代码,接下是客户端,客户端需要添加引用 signalr.js,下载方法自行百度。引入好之后,根据自己的需求,在指定的页面来设置连接,我这里是登录之后调用SetConnectionMaps来存储账号与connectionid的内容:

                var connection = new signalR.HubConnectionBuilder().withUrl("http://127.0.0.1:5004/SingalrClient").build();
                $.ajax({
                    url: api + '/user/loginuser',
                    type: 'get',
                    success: function (response) {
                            connection.start().then(function () {
                                connection.invoke('SetConnectionMaps', response.data.account).catch(function(errer){
                                    console.error(errer.toString())
                                });
                            });
                        } 
                    }
                });
    

      这个时候我们运行程序,就会看到如下的代表初步已经完成:

           接下来我们来看如何向客户端发送消息,我来封装一个发送消息的类和它的接口,并且通过注入IHubContext,当然,你也可以直接通过Hub来直接发送,可以看到,我在每一个SendAsync方法里面都要一个字符串,这个字符串很重要,客户端就是根据这个字符串来接收的方法:

     public  interface ISingalrContent
        {
    
            /// <summary>
            /// 向所有客户端(用户)发送消息
            /// </summary>
            /// <param name="message"></param>
            /// <returns></returns>
            Task SendAllClientsMessage(Message message);
            /// <summary>
            /// 向指定的部分客户端(用户)发送消息
            /// </summary>
            /// <param name="connectionIds"></param>
            /// <param name="message"></param>
            /// <returns></returns>
            Task SendSomeClientsMessage(IReadOnlyList<string> connectionIds, Message message);
            /// <summary>
            /// 向指定的客户端(用户)发送消息
            /// </summary>
            /// <param name="connectionIds"></param>
            /// <param name="message"></param>
            /// <returns></returns>
            Task SendClientMessage(Message message);
        }
    

      

       public class SingalrContent : ISingalrContent
        {
           
            private IHubContext<SingalrClient> _hubContext;
            public SingalrContent(IHubContext<SingalrClient> hubContext)
            {
                _hubContext = hubContext;
            }
              
            #region 向客户端发送消息
            public async Task SendAllClientsMessage(Message message)
            {
                await _hubContext.Clients.All.SendAsync("AllReviceMesage", message);
            }
            public async Task SendSomeClientsMessage(IReadOnlyList<string> connectionIds, Message message)
            {
                if (connectionIds == null || connectionIds.Count == 0)
                    throw new ArgumentNullException("指定的客户端连接为空");
                await _hubContext.Clients.Clients(connectionIds).SendAsync("SendClientMessage", message);
            }
    
            public async Task SendClientMessage(Message message)
            {
                if (string.IsNullOrEmpty(message.Revicer))
                    throw new ArgumentNullException("指定的客户端连接为空");
                IReadOnlyList<string> connectionsByUser = SingalrConnection.GetConnectionIds(message.Revicer);
                await _hubContext.Clients.Clients(connectionsByUser).SendAsync("ReviceMesage", message);
            }
            #endregion
        }
    

      

       services.AddScoped<ISingalrContent, SingalrContent>();
    

      

         然后当又消息通过SingalrContent的发送时,客户端通过设置 connection.on的方法来确定接收的数据,就好比我这里,我需要根据登录的账号来接收指定的犯法,所以我传入ReviceMessage来接收,:

                connection.on('ReviceMesage', function (message) {
                    var count=$('#notice').html();
                    var oldNum = parseInt(count);
                    var newNum = parseInt(message.data);
                    $('#notice').html(newNum);
                    if (oldNum < newNum) {
                        $('#notice').addClass('blink');
                    }
                });
    

      我还有个设计的是就是向所有的页面,不管有没有登录都发生消息,AllReviceMesage变对应的是SendAllClientsMessage(Message message)这个方法:

    	var connection = new signalR.HubConnectionBuilder().withUrl("http://111.229.211.248:5004/SingalrClient").build();
        connection.on('AllReviceMesage',function(reviceMessage){
            var data = {
                'list': reviceMessage.data
            };
            bindWhisper(data);
        } );
        connection.start();
    

      具体的调用就是这样子的:

         public void SendWhisper(List<WhisperDTO> whisperDTOs)
            {          
                Message message = new Message();
                message.Data = whisperDTOs;
                _singalrContent.SendAllClientsMessage(message);
            }
    
            public void SendTidingsCount(string account, int count)
            {
                Message message = new Message();
                message.Data = count;
                message.Revicer = account;
                _singalrContent.SendClientMessage(message);
            }
    

      到此,我的设计思路已经讲完了,具体的代码可以参考:https://github.com/Hansdas/Blog_New/tree/master/Socket,我的项目的地址是:http://www.ttblog.site/

         

  • 相关阅读:
    win10 开机背景图
    关于在不知道具体实例化哪个窗体时调用该窗体公共变量的方法
    devexpress 严重性 代码 说明 项目 文件 行 禁止显示状态 错误 “lc.exe”已退出,代码为 -1。
    获取打印机设置的纸张参数
    mysql 添加字段语句
    curl模拟post请求
    php CI框架log写入
    winform程序更新
    JSP的EL和JSTL解析
    报错:java.lang.IllegalArgumentException: object is not an instance of declaring class
  • 原文地址:https://www.cnblogs.com/MrHanBlog/p/14498794.html
Copyright © 2020-2023  润新知