消息队列Rabbitmq

消息队列   软件--Rabbitmq 

解释:

生产者将消息发送到队列,消费者从队列中获取消息。

多个客户端向服务端同时发送信息,防止服务器崩溃,把客户端发送的信息先放入消息队列中,再由消息队列依次发送到服务端,防止并发引发服务器崩溃

一、Docker安装Rabbitmq

参考:csdn文档有教程

我此时云服务器用的腾讯云

https://blog.csdn.net/WLPJLP/article/details/125449712

Rabbitmq是最常用的消息队列软件,经常用于流量削峰、异步事务、系统解耦、日志处理等等。

---参考word云服务器安装Rabbitmq后测试一下

 登录时后有误

 

 

解决:

https://www.baidu.com/s?wd=%E8%A6%81%E6%B1%82%E8%BF%9B%E8%A1%8C%E8%BA%AB%E4%BB%BD%E9%AA%8C%E8%AF%81%E4%B8%8E%E6%AD%A4%E7%AB%99%E7%82%B9%E7%9A%84%E8%BF%9E%E6%8E%A5%E4%B8%8D%E5%AE%89%E5%85%A8&rsv_spt=1&rsv_iqid=0x9f42eaeb0003e0d7&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&rqlang=cn&tn=baiduhome_pg&rsv_enter=1&rsv_dl=tb&rsv_btype=t&inputT=1858&rsv_t=cd855HhWyD%2BxvPrLHizTgVM75kaT9iu86w9iqb1YalW%2F%2FixCa0fQ2sMRCzab8y7WQreX&oq=RabbitMQ%2520%25E8%25A6%2581%25E6%25B1%2582%25E8%25BF%259B%25E8%25A1%258C%25E8%25BA%25AB%25E4%25BB%25BD%25E9%25AA%258C%25E8%25AF%2581%25E4%25B8%258E%25E6%25AD%25A4%25E7%25AB%2599%25E7%2582%25B9%25E7%259A%2584%25E8%25BF%259E%25E6%258E%25A5%25E4%25B8%258D%25E5%25AE%2589%25E5%2585%25A8&rsv_pq=fa6e40e800098da9&rsv_sug3=124&rsv_sug1=99&rsv_sug7=100&rsv_sug2=0&rsv_sug4=1858

 

3、云服务器(腾讯云)配置防火墙  --设置端口号 (暂时需要配置两个)--必须配置好这个后才可以有上面那种登录页面的显示效果

(12条消息) 消息中间件RabbitMQ需要知道的6个端口的作用_像夏天一样热的博客-CSDN博客_rabbitmq 各个端口

 

 

 

二、.Net Core 引入消息队列

.NetCore中使用RabbitMQ

参考:

https://blog.csdn.net/m0_54852350/article/details/124501873

https://blog.csdn.net/qq_38284923/article/details/119675498

https://www.cnblogs.com/yan7/p/9498685.html

 

.Net Core 使用CAP框架实现异步化分布式事务

https://blog.csdn.net/qq_26900081/article/details/108509324

https://cap.dotnetcore.xyz/user-guide/zh/getting-started/quick-start/

 

1、Program中注册RabbitMq

介绍 - CAP (dotnetcore.xyz)

 

//使用CAP 来注册RabbitMq  
//引用nuget包  DotNetCore.CAP.RabbitMQ和DotNetCore.CAP.MySql
// CAP 是一个EventBus,同时也是一个在微服务或者SOA系统中解决分布式事务问题的一个框架。它有助于创建可扩展,可靠并且易于更改的微服务系统。
//https://cap.dotnetcore.xyz/user-guide/zh/getting-started/introduction/
var configuration = builder.Configuration;
builder.Services.AddCap(x =>
{
    // 8.2 使用mysql进行事务处理,防止推送MQ失败,会在指定数据库中自动生成以"cap."开头的表
    x.UseMySql(connection);

    // 8.3 使用RabbitMQ进行事件中心处理
    x.UseRabbitMQ(option =>
    {
        option.HostName = configuration["CAPRabbitMQ:HostName"];
        string portStr = configuration["CAPRabbitMQ:Port"];
        if (!string.IsNullOrEmpty(portStr))
        {
            option.Port = Convert.ToInt32(portStr);
        }
        //   option.VirtualHost = configuration["CAPRabbitMQ:VirtualHost"];
        option.UserName = configuration["CAPRabbitMQ:UserName"];
        option.Password = configuration["CAPRabbitMQ:Password"];
        //if (!string.IsNullOrEmpty(configuration["CAPRabbitMQ:ExchangeName"]))
        //{
        //    option.ExchangeName = configuration["CAPRabbitMQ:ExchangeName"];
        //}
        //option.ConnectionFactoryOptions = factory => factory.AutomaticRecoveryEnabled = true;
    });
    //// 设置处理成功的数据在数据库中保存的时间(秒),为了保证系统性能,数据会定期清理
    //x.SucceedMessageExpiredAfter = Convert.ToInt32(configuration["CAPRabbitMQ:SucceedMessageExpiredAfter"]);
    //// 重试次数
    //x.FailedRetryCount = Convert.ToInt32(configuration["CAPRabbitMQ:FailedRetryCount"]);
    //// 间隔时间 单位:秒
    //x.FailedRetryInterval = Convert.ToInt32(configuration["CAPRabbitMQ:FailedRetryInterval"]);
    //发送消息失败后的回调
    x.FailedThresholdCallback = (failedInfo) =>
    {
        Log4Tools log = new Log4Tools();
        log.InfoObj($"消息队列出现错误:{failedInfo}");

    };
    // x.Version = configuration["CAPRabbitMQ:Version"];
});
Program注册使用CAP 来注册RabbitMq

 ②步骤对应数据库会自动生成了cap开头的表

 

 

 

2、配置文件里加入一系列http路径、端口号一系列配置

(放在了appsettings.json文件进行配置了,因为无论开发状态还是发布状态下都是用云主机上面的这个RabbitMQ

  "CAPRabbitMQ": {
    "HostName": "云主机Ip",//多个逗号隔开
    "Port": 5601,
    "UserName": "rbmq",
    "Password": "YHUasd#$%45",
    "VirtualHost": "mess_manager", //虚拟消息服务器 类似于mysql表 参考https://blog.csdn.net/h996666/article/details/83304626
    "ExchangeName": "scp.exchange", //交换机.v3
    "FailedRetryCount": 5, //失败重试次数
    "FailedRetryInterval": 3, // 失败重试间隔时间  单位:秒
    "Version": "v1",
    "SucceedMessageExpiredAfter": 1440 // 定期清理成功的数据,单位分钟
  },
appsettings.json

不加这个会有错

 

3、创建Model用于往MongoDb中存入信息--类似于查看聊天记录

public class SignalRMessage
    {
        /// <summary>
        /// mongodb 主键id
        /// </summary>
        public ObjectId id { get; set; }
        /// <summary>
        /// signal 连接id
        /// </summary>
        public string? conn_id { get; set; }
        /// <summary>
        /// 发起人
        /// </summary>
        public string? send_user { get; set; }
        /// <summary>
        /// 接受人
        /// </summary>

        public string get_user { get; set; }
        /// <summary>
        /// 标题
        /// </summary>

        public string title { get; set; }
        /// <summary>
        /// 内容
        /// </summary>

        public string content { get; set; }
        /// <summary>
        /// 发送时间
        /// </summary>

        public DateTime? send_date { get; set; }
        /// <summary>
        /// 创建时间
        /// </summary>

        public DateTime? created_date { get; set; }
    }
SignalRMessage

 

  修改上一博客的SignalR的model,加一个连接SignalR的Id的conn_id字符串

 //SignalR用的Mdoel
    public class SignalRMessageViewModel
    {
        /// <summary>
        /// 发起人
        /// </summary>
        public string? send_user { get; set; }
        /// <summary>
        /// signal 连接id
        /// </summary>
        public string? conn_id { get; set; }
        /// <summary>
        /// 接受人
        /// </summary>

        public string get_user { get; set; }
        /// <summary>
        /// 标题
        /// </summary>

        public string title { get; set; }
        /// <summary>
        /// 内容
        /// </summary>

        public string content { get; set; }
        /// <summary>
        /// 发送时间
        /// </summary>

        public DateTime? send_date { get; set; }
        /// <summary>
        /// 创建时间
        /// </summary>

        public DateTime? created_date { get; set; }
    }
SignalRMessageViewModel

     

    并且必须也要加上public ObjectId id { get; set; }字段--否则显示Mongodb数据时候报错

/// <summary>
        /// mongodb 主键id
        /// </summary>
        public ObjectId id { get; set; }

 

4、更新修改上一博客实现signalR推送消息创建连接初始化那里(SystemMessageHub类中)

 

5、在上一博客实现signalR推送消息中发送消息的控制器处加入实现消息队列

public class MessManagerController :BaseMessConterController
    {
        private readonly IHubContext<SystemMessageHub> systemMessageHub;
        //事件总线
        private readonly ICapPublisher _capBus;
        private readonly systemConterMessService _systemConterMessService;
        /// <summary>
        /// 注入signalr 的消息推送的hub
        /// </summary>
        /// <param name="systemMessageHub"></param>
        public MessManagerController(IHubContext<SystemMessageHub> systemMessageHub, ICapPublisher _capBus, systemConterMessService _systemConterMessServices)
        {
            this.systemMessageHub = systemMessageHub;
            this._capBus = _capBus;
            _systemConterMessService = _systemConterMessServices;

        }
        /// <summary>
        /// 发送消息的接口
        /// </summary>
        /// <param name="message">要发送的消息</param>
        /// <returns></returns>
        [HttpPost]
        public async Task<ResultModel<bool>> SendMess(SignalRMessageViewModel message)
        {
            ////如果接口人是all  代表给所有人都发送
            //if (message.get_user == "all")
            //{
            //    //     给所有客户端发送消息   message 表示给客户端传递的参数
            //    systemMessageHub.Clients.All.SendAsync("GetAllMess", message);
            //}

            //return MyOk<bool>(true);

            try
            {
                message.send_user = LoginInfo.operator_name;
                message.send_date = DateTime.Now;
                _capBus.Publish("GroupThreeObject01sysmess.usermess", message); //将要发送的消息存入消息队列
                return MyOk(true);
            }
            catch (Exception ex)
            {
                return MyError<bool>(ex);
            }
        }
        /// <summary>
        /// 监听,并获取 sysmess.usermess 的消息队列的消息
        /// </summary>
        /// <param name="MessageDto"></param>
        /// <returns></returns>
        ///   [NonAction]表示在控制器中他不是一个请求
        ///   [CapSubscribe("sysmess.usermess")]发到消息对列后,这个方法加这个就会由消息队列转到这里
        [NonAction]
        [CapSubscribe("GroupThreeObject01sysmess.usermess")]
        public async Task CheckReceivedMessage(SignalRMessageViewModel message)
        {
           
                //把发送数据存入Mongodb中--聊天记录保存
                _systemConterMessService.SystemConterMessAdd(message);

                //如果接收入人是all  代表给所有人都发送
                if (message.get_user == "all")
                {
                    //给所有客户端发送消息   message 表示给客户端传递的参数 使用singnal往外推消息
                    systemMessageHub.Clients.All.SendAsync("GetAllMess", message);
                }
                //否则发送给指定人
                else
                {
                    if (string.IsNullOrEmpty(message.conn_id))
                    {
                        //用户名获取连接id
                        message.conn_id = SystemMessageHub.GetConnIdByUserName(message.get_user);
                    }
                    if (!string.IsNullOrEmpty(message.conn_id))
                        //通过SingnalR推送消息
                        systemMessageHub.Clients.Clients(message.conn_id).SendAsync("GetAllMess", message);
                }       
        }

        /// <summary>
        /// 获取此系统登录的用户列表
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        [HttpGet]
        public async Task<ResultModel<object>> GetMessUsers(string? userName)
        {
            IEnumerable<KeyValuePair<string, UserData>> di;

            //每一次登录都会加入SystemMessageHub声明那个字典里保存对应登录信息
            if (!string.IsNullOrEmpty(userName))
            {
                di = SystemMessageHub.UserIdAndCid.Where(c => c.Key.Contains(userName));
            }
            else
            {
                di = SystemMessageHub.UserIdAndCid;
            }
            var result = di.Select(c => new { user_name = c.Value.UserName, pwd=c.Value.Pwd })
                .GroupBy(d => d.user_name)
                .Select(e => new { user_name = e.Key, pwd = e.Select(f => f.pwd).FirstOrDefault() })
                .ToList();
            return MyOk<object>(result);
        }
        /// <summary>
        //获取mogodb数据,显示聊天记录
        /// </summary>
        /// <param name="searchModel"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<ResultModel<PagingModel<SignalRMessageViewModel>>> SearchMessagePage(SearchModel searchModel)
        {        
            return MyOk(_systemConterMessService.SearchSystemMess(searchModel));
        }
    }
MessManagerController

 

 6、在SystemMessageHub类编写根据用户名获取到他的连接SignalR的连接ID

/// <summary>
        /// 根据userName获取连接id
        /// </summary>
        /// <param name="userName">用户名</param>
        /// <returns></returns>
        public static string GetConnIdByUserName(string userName)
        {
            if (UserIdAndCid.Keys.Contains(userName))
            {
                return UserIdAndCid[userName].ConnectionId;
            }
            return "";
        }
GetConnIdByUserName

 

7、仿照SignalR测试方式--可以实现给指定人发送消息

 

posted @ 2022-07-14 15:50  じ逐梦  阅读(251)  评论(0)    收藏  举报