看博文的朋友,本文有些过时了,还有些BUG,如果想了解更多用法,看看这篇吧:.net core使用rabbitmq消息队列 (二)

  

  首先,如果你还没有安装好rabbitmq,可以参考我的博客:

  Ubuntu16.04下,erlang安装和rabbitmq安装步骤

  Ubuntu16.04下,rabbimq集群搭建

  另外,我的另外一篇博客有介绍rabbitmq的基础用法以及使用C#操作rabbitmq,并且对rabbitmq有一个简单的封装,这里使用.net core操作rabbitmq也会使用到这些封装类,所以感兴趣的可以看看:

  C# .net 环境下使用rabbitmq消息队列

  好了现在开始我们的正文

  Rabbitmq一般使用

  说明一下,这里我们使用的是.net core 2.1

  我们先创建一个RabbitMQDemo项目(我创建的是MVC项目),然后使用nuget安装RabbitMQ.Client:

  

  将上面封装的rabbitmq操作类添加到项目中:   

  

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4.  
  5. namespace RabbitMQDemo
  6. {
  7. public class QueueOptions
  8. {
  9. /// <summary>
  10. /// 是否持久化
  11. /// </summary>
  12. public bool Durable { get; set; } = true;
  13. /// <summary>
  14. /// 是否自动删除
  15. /// </summary>
  16. public bool AutoDelete { get; set; } = false;
  17. /// <summary>
  18. /// 参数
  19. /// </summary>
  20. public IDictionary<string, object> Arguments { get; set; } = new Dictionary<string, object>();
  21. }
  22. public class ConsumeQueueOptions : QueueOptions
  23. {
  24. /// <summary>
  25. /// 是否自动提交
  26. /// </summary>
  27. public bool AutoAck { get; set; } = false;
  28. /// <summary>
  29. /// 每次发送消息条数
  30. /// </summary>
  31. public ushort? FetchCount { get; set; }
  32. }
  33. public class ExchangeConsumeQueueOptions : ConsumeQueueOptions
  34. {
  35. /// <summary>
  36. /// 路由值
  37. /// </summary>
  38. public string[] RoutingKeys { get; set; }
  39. /// <summary>
  40. /// 参数
  41. /// </summary>
  42. public IDictionary<string, object> BindArguments { get; set; } = new Dictionary<string, object>();
  43. }
  44. public class ExchangeQueueOptions : QueueOptions
  45. {
  46. /// <summary>
  47. /// 交换机类型
  48. /// </summary>
  49. public string Type { get; set; }
  50. /// <summary>
  51. /// 队列及路由值
  52. /// </summary>
  53. public (string, string)[] QueueAndRoutingKey { get; set; }
  54. /// <summary>
  55. /// 参数
  56. /// </summary>
  57. public IDictionary<string, object> BindArguments { get; set; } = new Dictionary<string, object>();
  58. }
  59. }

QueueOptions

  

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4.  
  5. namespace RabbitMQDemo
  6. {
  7. public static class RabbitMQExchangeType
  8. {
  9. /// <summary>
  10. /// 普通模式
  11. /// </summary>
  12. public const string Common = "";
  13. /// <summary>
  14. /// 路由模式
  15. /// </summary>
  16. public const string Direct = "direct";
  17. /// <summary>
  18. /// 发布/订阅模式
  19. /// </summary>
  20. public const string Fanout = "fanout";
  21. /// <summary>
  22. /// 匹配订阅模式
  23. /// </summary>
  24. public const string Topic = "topic";
  25. }
  26. }

RabbitMQDemo

  

  1. using RabbitMQ.Client;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6.  
  7. namespace RabbitMQDemo
  8. {
  9. public abstract class RabbitBase : IDisposable
  10. {
  11. List<AmqpTcpEndpoint> amqpList;
  12. IConnection connection;
  13.  
  14. protected RabbitBase(params string[] hosts)
  15. {
  16. if (hosts == null || hosts.Length == 0)
  17. {
  18. throw new ArgumentException("invalid hosts!", nameof(hosts));
  19. }
  20.  
  21. this.amqpList = new List<AmqpTcpEndpoint>();
  22. this.amqpList.AddRange(hosts.Select(host => new AmqpTcpEndpoint(host, Port)));
  23. }
  24. protected RabbitBase(params (string, int)[] hostAndPorts)
  25. {
  26. if (hostAndPorts == null || hostAndPorts.Length == 0)
  27. {
  28. throw new ArgumentException("invalid hosts!", nameof(hostAndPorts));
  29. }
  30.  
  31. this.amqpList = new List<AmqpTcpEndpoint>();
  32. this.amqpList.AddRange(hostAndPorts.Select(tuple => new AmqpTcpEndpoint(tuple.Item1, tuple.Item2)));
  33. }
  34.  
  35. /// <summary>
  36. /// 端口
  37. /// </summary>
  38. public int Port { get; set; } = 5672;
  39. /// <summary>
  40. /// 账号
  41. /// </summary>
  42. public string UserName { get; set; } = ConnectionFactory.DefaultUser;
  43. /// <summary>
  44. /// 密码
  45. /// </summary>
  46. public string Password { get; set; } = ConnectionFactory.DefaultPass;
  47. /// <summary>
  48. /// 虚拟机
  49. /// </summary>
  50. public string VirtualHost { get; set; } = ConnectionFactory.DefaultVHost;
  51.  
  52. /// <summary>
  53. /// 释放
  54. /// </summary>
  55. public virtual void Dispose()
  56. {
  57. //connection?.Close();
  58. //connection?.Dispose();
  59. }
  60. /// <summary>
  61. /// 关闭连接
  62. /// </summary>
  63. public void Close()
  64. {
  65. connection?.Close();
  66. connection?.Dispose();
  67. }
  68.  
  69. #region Private
  70. /// <summary>
  71. /// 获取rabbitmq的连接
  72. /// </summary>
  73. /// <returns></returns>
  74. protected IModel GetChannel()
  75. {
  76. if (connection == null)
  77. {
  78. lock (this)
  79. {
  80. if (connection == null)
  81. {
  82. var factory = new ConnectionFactory();
  83. factory.Port = Port;
  84. factory.UserName = UserName;
  85. factory.VirtualHost = VirtualHost;
  86. factory.Password = Password;
  87. connection = factory.CreateConnection(this.amqpList);
  88. }
  89. }
  90. }
  91. return connection.CreateModel();
  92. }
  93.  
  94. #endregion
  95. }
  96. }

RabbitBase

  

  1. using RabbitMQ.Client;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6.  
  7. namespace RabbitMQDemo
  8. {
  9. public class RabbitMQProducer : RabbitBase
  10. {
  11. public RabbitMQProducer(params string[] hosts) : base(hosts)
  12. {
  13.  
  14. }
  15. public RabbitMQProducer(params (string, int)[] hostAndPorts) : base(hostAndPorts)
  16. {
  17.  
  18. }
  19.  
  20. #region 普通模式、Work模式
  21. /// <summary>
  22. /// 发布消息
  23. /// </summary>
  24. /// <param name="queue"></param>
  25. /// <param name="message"></param>
  26. /// <param name="options"></param>
  27. public void Publish(string queue, string message, QueueOptions options = null)
  28. {
  29. options = options ?? new QueueOptions();
  30. var channel = GetChannel();
  31. channel.QueueDeclare(queue, options.Durable, false, options.AutoDelete, options.Arguments ?? new Dictionary<string, object>());
  32. var buffer = Encoding.UTF8.GetBytes(message);
  33. channel.BasicPublish("", queue, null, buffer);
  34. channel.Close();
  35. }
  36. /// <summary>
  37. /// 发布消息
  38. /// </summary>
  39. /// <param name="queue"></param>
  40. /// <param name="message"></param>
  41. /// <param name="configure"></param>
  42. public void Publish(string queue, string message, Action<QueueOptions> configure)
  43. {
  44. QueueOptions options = new QueueOptions();
  45. configure?.Invoke(options);
  46. Publish(queue, message, options);
  47. }
  48. #endregion
  49. #region 订阅模式、路由模式、Topic模式
  50. /// <summary>
  51. /// 发布消息
  52. /// </summary>
  53. /// <param name="exchange"></param>
  54. /// <param name="routingKey"></param>
  55. /// <param name="message"></param>
  56. /// <param name="options"></param>
  57. public void Publish(string exchange, string routingKey, string message, ExchangeQueueOptions options = null)
  58. {
  59. options = options ?? new ExchangeQueueOptions();
  60. var channel = GetChannel();
  61. channel.ExchangeDeclare(exchange, string.IsNullOrEmpty(options.Type) ? RabbitMQExchangeType.Fanout : options.Type, options.Durable, options.AutoDelete, options.Arguments ?? new Dictionary<string, object>());
  62. if (options.QueueAndRoutingKey != null)
  63. {
  64. foreach (var t in options.QueueAndRoutingKey)
  65. {
  66. if (!string.IsNullOrEmpty(t.Item1))
  67. {
  68. channel.QueueBind(t.Item1, exchange, t.Item2 ?? "", options.BindArguments ?? new Dictionary<string, object>());
  69. }
  70. }
  71. }
  72. var buffer = Encoding.UTF8.GetBytes(message);
  73. channel.BasicPublish(exchange, routingKey, null, buffer);
  74. channel.Close();
  75. }
  76. /// <summary>
  77. /// 发布消息
  78. /// </summary>
  79. /// <param name="exchange"></param>
  80. /// <param name="routingKey"></param>
  81. /// <param name="message"></param>
  82. /// <param name="configure"></param>
  83. public void Publish(string exchange, string routingKey, string message, Action<ExchangeQueueOptions> configure)
  84. {
  85. ExchangeQueueOptions options = new ExchangeQueueOptions();
  86. configure?.Invoke(options);
  87. Publish(exchange, routingKey, message, options);
  88. }
  89. #endregion
  90. }
  91. }

RabbitMQProducer

  

  1. using RabbitMQ.Client;
  2. using RabbitMQ.Client.Events;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Text;
  6. using System.Threading;
  7.  
  8. namespace RabbitMQDemo
  9. {
  10. public class RabbitMQConsumer : RabbitBase
  11. {
  12. public RabbitMQConsumer(params string[] hosts) : base(hosts)
  13. {
  14.  
  15. }
  16. public RabbitMQConsumer(params (string, int)[] hostAndPorts) : base(hostAndPorts)
  17. {
  18.  
  19. }
  20.  
  21. public event Action<RecieveResult> Received;
  22.  
  23. /// <summary>
  24. /// 构造消费者
  25. /// </summary>
  26. /// <param name="channel"></param>
  27. /// <param name="options"></param>
  28. /// <returns></returns>
  29. private IBasicConsumer ConsumeInternal(IModel channel, ConsumeQueueOptions options)
  30. {
  31. EventingBasicConsumer consumer = new EventingBasicConsumer(channel);
  32. consumer.Received += (sender, e) =>
  33. {
  34. try
  35. {
  36. CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
  37. if (!options.AutoAck)
  38. {
  39. cancellationTokenSource.Token.Register(() =>
  40. {
  41. channel.BasicAck(e.DeliveryTag, false);
  42. });
  43. }
  44. Received?.Invoke(new RecieveResult(e, cancellationTokenSource));
  45. }
  46. catch { }
  47. };
  48. if (options.FetchCount != null)
  49. {
  50. channel.BasicQos(0, options.FetchCount.Value, false);
  51. }
  52. return consumer;
  53. }
  54.  
  55. #region 普通模式、Work模式
  56. /// <summary>
  57. /// 消费消息
  58. /// </summary>
  59. /// <param name="queue"></param>
  60. /// <param name="options"></param>
  61. public ListenResult Listen(string queue, ConsumeQueueOptions options = null)
  62. {
  63. options = options ?? new ConsumeQueueOptions();
  64. var channel = GetChannel();
  65. channel.QueueDeclare(queue, options.Durable, false, options.AutoDelete, options.Arguments ?? new Dictionary<string, object>());
  66. var consumer = ConsumeInternal(channel, options);
  67. channel.BasicConsume(queue, options.AutoAck, consumer);
  68. ListenResult result = new ListenResult();
  69. result.Token.Register(() =>
  70. {
  71. try
  72. {
  73. channel.Close();
  74. channel.Dispose();
  75. }
  76. catch { }
  77. });
  78. return result;
  79. }
  80. /// <summary>
  81. /// 消费消息
  82. /// </summary>
  83. /// <param name="queue"></param>
  84. /// <param name="configure"></param>
  85. public ListenResult Listen(string queue, Action<ConsumeQueueOptions> configure)
  86. {
  87. ConsumeQueueOptions options = new ConsumeQueueOptions();
  88. configure?.Invoke(options);
  89. return Listen(queue, options);
  90. }
  91. #endregion
  92. #region 订阅模式、路由模式、Topic模式
  93. /// <summary>
  94. /// 消费消息
  95. /// </summary>
  96. /// <param name="exchange"></param>
  97. /// <param name="queue"></param>
  98. /// <param name="options"></param>
  99. public ListenResult Listen(string exchange, string queue, ExchangeConsumeQueueOptions options = null)
  100. {
  101. options = options ?? new ExchangeConsumeQueueOptions();
  102. var channel = GetChannel();
  103. channel.QueueDeclare(queue, options.Durable, false, options.AutoDelete, options.Arguments ?? new Dictionary<string, object>());
  104. if (options.RoutingKeys != null && !string.IsNullOrEmpty(exchange))
  105. {
  106. foreach (var key in options.RoutingKeys)
  107. {
  108. channel.QueueBind(queue, exchange, key, options.BindArguments);
  109. }
  110. }
  111. var consumer = ConsumeInternal(channel, options);
  112. channel.BasicConsume(queue, options.AutoAck, consumer);
  113. ListenResult result = new ListenResult();
  114. result.Token.Register(() =>
  115. {
  116. try
  117. {
  118. channel.Close();
  119. channel.Dispose();
  120. }
  121. catch { }
  122. });
  123. return result;
  124. }
  125. /// <summary>
  126. /// 消费消息
  127. /// </summary>
  128. /// <param name="exchange"></param>
  129. /// <param name="queue"></param>
  130. /// <param name="configure"></param>
  131. public ListenResult Listen(string exchange, string queue, Action<ExchangeConsumeQueueOptions> configure)
  132. {
  133. ExchangeConsumeQueueOptions options = new ExchangeConsumeQueueOptions();
  134. configure?.Invoke(options);
  135. return Listen(exchange, queue, options);
  136. }
  137. #endregion
  138. }
  139. public class RecieveResult
  140. {
  141. CancellationTokenSource cancellationTokenSource;
  142. public RecieveResult(BasicDeliverEventArgs arg, CancellationTokenSource cancellationTokenSource)
  143. {
  144. this.Body = Encoding.UTF8.GetString(arg.Body);
  145. this.ConsumerTag = arg.ConsumerTag;
  146. this.DeliveryTag = arg.DeliveryTag;
  147. this.Exchange = arg.Exchange;
  148. this.Redelivered = arg.Redelivered;
  149. this.RoutingKey = arg.RoutingKey;
  150. this.cancellationTokenSource = cancellationTokenSource;
  151. }
  152.  
  153. /// <summary>
  154. /// 消息体
  155. /// </summary>
  156. public string Body { get; private set; }
  157. /// <summary>
  158. /// 消费者标签
  159. /// </summary>
  160. public string ConsumerTag { get; private set; }
  161. /// <summary>
  162. /// Ack标签
  163. /// </summary>
  164. public ulong DeliveryTag { get; private set; }
  165. /// <summary>
  166. /// 交换机
  167. /// </summary>
  168. public string Exchange { get; private set; }
  169. /// <summary>
  170. /// 是否Ack
  171. /// </summary>
  172. public bool Redelivered { get; private set; }
  173. /// <summary>
  174. /// 路由
  175. /// </summary>
  176. public string RoutingKey { get; private set; }
  177.  
  178. public void Commit()
  179. {
  180. if (cancellationTokenSource == null || cancellationTokenSource.IsCancellationRequested) return;
  181.  
  182. cancellationTokenSource.Cancel();
  183. cancellationTokenSource.Dispose();
  184. cancellationTokenSource = null;
  185. }
  186. }
  187. public class ListenResult
  188. {
  189. CancellationTokenSource cancellationTokenSource;
  190.  
  191. /// <summary>
  192. /// CancellationToken
  193. /// </summary>
  194. public CancellationToken Token { get { return cancellationTokenSource.Token; } }
  195. /// <summary>
  196. /// 是否已停止
  197. /// </summary>
  198. public bool Stoped { get { return cancellationTokenSource.IsCancellationRequested; } }
  199.  
  200. public ListenResult()
  201. {
  202. cancellationTokenSource = new CancellationTokenSource();
  203. }
  204.  
  205. /// <summary>
  206. /// 停止监听
  207. /// </summary>
  208. public void Stop()
  209. {
  210. cancellationTokenSource.Cancel();
  211. }
  212. }
  213. }

RabbitMQConsumer

  修改Startup,在ConfigureServices中将rabbitmq的生产类RabbitMQProducer加入到DI容器中:   

  1.   //将rabbitmq的生产类加入到DI容器中
  2.   var producer = new RabbitMQProducer("192.168.187.129");
  3.   producer.Password = "123456";
  4.   producer.UserName = "admin";
  5.   services.AddSingleton(producer);//这里我没有使用集群

  至于消息的消费,我们可以将消费者使用一个线程启动并监听,但是这里我个人推荐使用.net core 自带的HostedService去实现,至于消费者的功能,我们就简单的将消息记录都文本文档中,类似日志记录。

  我们创建一个RabbitHostedService类:   

  

  1. using Microsoft.Extensions.Hosting;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using System.IO;
  8.  
  9. namespace RabbitMQDemo
  10. {
  11. public class RabbitHostedService : IHostedService
  12. {
  13. RabbitMQConsumer consumer;
  14.  
  15. public RabbitHostedService()
  16. {
  17. consumer = new RabbitMQConsumer("192.168.187.129");
  18. consumer.Password = "123456";
  19. consumer.UserName = "admin";
  20. }
  21.  
  22. /// <summary>
  23. /// 服务启动
  24. /// </summary>
  25. /// <param name="cancellationToken"></param>
  26. /// <returns></returns>
  27. public async Task StartAsync(CancellationToken cancellationToken)
  28. {
  29. await Task.Run(() =>
  30. {
  31. consumer.Received += new Action<RecieveResult>(result =>
  32. {
  33. //文件路径
  34. string path = Path.Combine(Directory.GetCurrentDirectory(), "logs");
  35. if (!Directory.Exists(path))
  36. {
  37. Directory.CreateDirectory(path);
  38. }
  39.  
  40. //文件
  41. string fileName = Path.Combine(path, $"{DateTime.Now.ToString("yyyyMMdd")}.log");
  42. string message = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}接收到消息:{result.Body}{Environment.NewLine}";
  43. File.AppendAllText(fileName, message);
  44.  
  45. //提交
  46. result.Commit();
  47. });
  48.  
  49. consumer.Listen("queue1", options =>
  50. {
  51. options.AutoAck = false;
  52. options.Arguments = new Dictionary<string, object>() { { "x-queue-type", "classic" } };
  53. options.AutoDelete = false;
  54. options.Durable = true;
  55. });
  56. });
  57. }
  58. /// <summary>
  59. /// 服务停止
  60. /// </summary>
  61. /// <param name="cancellationToken"></param>
  62. /// <returns></returns>
  63. public async Task StopAsync(CancellationToken cancellationToken)
  64. {
  65. var task = Task.Run(() =>
  66. {
  67. consumer.Close();
  68. });
  69.  
  70. await Task.WhenAny(task, Task.Delay(-1, cancellationToken));
  71. cancellationToken.ThrowIfCancellationRequested();
  72. }
  73. }
  74. }

RabbitHostedService

  同时,我们需要在Startup中将RabbitHostedService注入到容器中:  

  1. //注入消费者
  2. services.AddSingleton<IHostedService, RabbitHostedService>();

  得到Startup的代码如下:  

  

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. using Microsoft.AspNetCore.Builder;
  6. using Microsoft.AspNetCore.Hosting;
  7. using Microsoft.AspNetCore.Http;
  8. using Microsoft.AspNetCore.HttpsPolicy;
  9. using Microsoft.AspNetCore.Mvc;
  10. using Microsoft.Extensions.Configuration;
  11. using Microsoft.Extensions.DependencyInjection;
  12. using Microsoft.Extensions.Hosting;
  13.  
  14. namespace RabbitMQDemo
  15. {
  16. public class Startup
  17. {
  18. public Startup(IConfiguration configuration)
  19. {
  20. Configuration = configuration;
  21. }
  22.  
  23. public IConfiguration Configuration { get; }
  24.  
  25. // This method gets called by the runtime. Use this method to add services to the container.
  26. public void ConfigureServices(IServiceCollection services)
  27. {
  28. services.Configure<CookiePolicyOptions>(options =>
  29. {
  30. // This lambda determines whether user consent for non-essential cookies is needed for a given request.
  31. options.CheckConsentNeeded = context => true;
  32. options.MinimumSameSitePolicy = SameSiteMode.None;
  33. });
  34.  
  35. //将rabbitmq的生产类加入到DI容器中
  36. var producer = new RabbitMQProducer("192.168.187.129");
  37. producer.Password = "123456";
  38. producer.UserName = "admin";
  39. services.AddSingleton(producer);//这里我没有使用集群
  40. //注入消费者
  41. services.AddSingleton<IHostedService, RabbitHostedService>();
  42.  
  43. services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
  44. }
  45.  
  46. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  47. public void Configure(IApplicationBuilder app, Microsoft.Extensions.Hosting.IHostingEnvironment env)
  48. {
  49. if (env.IsDevelopment())
  50. {
  51. app.UseDeveloperExceptionPage();
  52. }
  53. else
  54. {
  55. app.UseExceptionHandler("/Home/Error");
  56. app.UseHsts();
  57. }
  58.  
  59. app.UseHttpsRedirection();
  60. app.UseStaticFiles();
  61. app.UseCookiePolicy();
  62.  
  63. app.UseMvc(routes =>
  64. {
  65. routes.MapRoute(
  66. name: "default",
  67. template: "{controller=Home}/{action=Index}/{id?}");
  68. });
  69. }
  70. }
  71. }

Startup

  到这里,.net core 集成rabbitmq就写好了,然后就是发送消息使用了,我们添加一个名为RabbitController的控制器,里面代码如下:   

  

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. using Microsoft.AspNetCore.Mvc;
  7. using RabbitMQDemo.Models;
  8.  
  9. namespace RabbitMQDemo.Controllers
  10. {
  11. public class RabbitController : Controller
  12. {
  13. RabbitMQProducer producer;
  14. public RabbitController(RabbitMQProducer producer)
  15. {
  16. this.producer = producer;
  17. }
  18.  
  19. /// <summary>
  20. /// 首页
  21. /// </summary>
  22. /// <returns></returns>
  23. public IActionResult Index()
  24. {
  25. return View();
  26. }
  27. /// <summary>
  28. /// 消息提交
  29. /// </summary>
  30. /// <param name="message"></param>
  31. /// <returns></returns>
  32. public IActionResult Submit(string message)
  33. {
  34. if (!string.IsNullOrEmpty(message))
  35. {
  36. //发送消息
  37. producer.Publish("queue1", message, options =>
  38. {
  39. options.Arguments = new Dictionary<string, object>() { { "x-queue-type", "classic" } };
  40. options.AutoDelete = false;
  41. options.Durable = true;
  42. });
  43. }
  44. return View("Index");
  45. }
  46. }
  47. }

RabbitController

  RabbitController里面有两个Action,它们返回同一个视图:    

  

  1. @{
  2. Layout = null;
  3. }
  4.  
  5. @using (Html.BeginForm("Submit", "Rabbit"))
  6. {
  7. @Html.DisplayName("消息:");
  8. <br />
  9. @Html.TextArea("message", new { @class = "multieditbox" })
  10. <br />
  11. <input type="submit" class="buttoncss" value="确定" />
  12. }
  13. <style type="text/css">
  14. .buttoncss {
  15. font-family: "tahoma", "宋体"; /*www.52css.com*/
  16. font-size: 9pt;
  17. color: #003399;
  18. border: 1px #003399 solid;
  19. color: #006699;
  20. border-bottom: #93bee2 1px solid;
  21. border-left: #93bee2 1px solid;
  22. border-right: #93bee2 1px solid;
  23. border-top: #93bee2 1px solid;
  24. background-image: url(../images/bluebuttonbg.gif);
  25. background-color: #e8f4ff;
  26. font-style: normal;
  27. width: 60px;
  28. height: 22px;
  29. }
  30.  
  31. .multieditbox {
  32. background: #f8f8f8;
  33. border-bottom: #b7b7b7 1px solid;
  34. border-left: #b7b7b7 1px solid;
  35. border-right: #b7b7b7 1px solid;
  36. border-top: #b7b7b7 1px solid;
  37. color: #000000;
  38. cursor: text;
  39. font-family: "arial";
  40. font-size: 9pt;
  41. padding: 1px; /*www.52css.com*/
  42. width: 200px;
  43. height: 80px;
  44. }
  45. </style>

Index

  现在可以启动项目,输入http://localhost:5000/Rabbit就可以进入页面测试了:

  

  点击上面确定之后,你会发现在项目根目录下生成了一个logs目录,里面有一个文件,文件里面就是我们发送的消息了

    注:如果报错,可以登录rabbitmq后台查看账号虚拟机权限是否存在

  

  Rabbitmq日志记录

  上面是我们使用.net core集成rabbitmq的一种简单方式,但是不建议在开发时这么使用

  可以注意到,上面的例子我直接将RabbitMQProducer注入到容器中,但开发时应该按自己的需求对RabbitMQProducer做一层封装,然后将封装类注入到容器中,比如我们要使用rabbitmq做日志记录,可以记录到数据库,也可以记录到文件中去,但是.net core为我们提供了一整套的日志记录功能,因此我们只需要将rabbitmq集成进去就可以了

  首先,我们需要创建几个类,将rabbitmq继承到日志记录功能中去:  

  

  1. using Microsoft.Extensions.Logging;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6.  
  7. namespace RabbitMQDemo
  8. {
  9. public abstract class RabbitMQOptions
  10. {
  11. /// <summary>
  12. /// 服务节点
  13. /// </summary>
  14. public string[] Hosts { get; set; }
  15. /// <summary>
  16. /// 端口
  17. /// </summary>
  18. public int Port { get; set; }
  19. /// <summary>
  20. /// 账号
  21. /// </summary>
  22. public string UserName { get; set; }
  23. /// <summary>
  24. /// 密码
  25. /// </summary>
  26. public string Password { get; set; }
  27. /// <summary>
  28. /// 虚拟机
  29. /// </summary>
  30. public string VirtualHost { get; set; }
  31. /// <summary>
  32. /// 是否持久化
  33. /// </summary>
  34. public bool Durable { get; set; } = true;
  35. /// <summary>
  36. /// 是否自动删除
  37. /// </summary>
  38. public bool AutoDelete { get; set; } = false;
  39. /// <summary>
  40. /// 队列
  41. /// </summary>
  42. public string Queue { get; set; }
  43. /// <summary>
  44. /// 交换机
  45. /// </summary>
  46. public string Exchange { get; set; }
  47. /// <summary>
  48. /// 交换机类型,放空则为普通模式
  49. /// </summary>
  50. public string Type { get; set; }
  51. /// <summary>
  52. /// 参数
  53. /// </summary>
  54. public IDictionary<string, object> Arguments { get; set; } = new Dictionary<string, object>();
  55. }
  56. public class RabbitMQLoggerOptions : RabbitMQOptions
  57. {
  58. /// <summary>
  59. /// 最低日志记录
  60. /// </summary>
  61. public LogLevel MinLevel { get; set; } = LogLevel.Information;
  62. /// <summary>
  63. /// 分类
  64. /// </summary>
  65. public string Category { get; set; } = "Rabbit";
  66. }
  67. public class RabbitMQConsumerOptions : RabbitMQOptions
  68. {
  69. /// <summary>
  70. /// 是否自动提交
  71. /// </summary>
  72. public bool AutoAck { get; set; } = false;
  73. /// <summary>
  74. /// 每次发送消息条数
  75. /// </summary>
  76. public ushort? FetchCount { get; set; }
  77. }
  78. }

RabbitMQOptions

  

  1. using Microsoft.Extensions.Logging;
  2. using Microsoft.Extensions.Options;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Threading.Tasks;
  7.  
  8. namespace RabbitMQDemo
  9. {
  10. public class RabbitLoggerProvider : ILoggerProvider
  11. {
  12. RabbitMQLoggerOptions loggerOptions;
  13.  
  14. public RabbitLoggerProvider(IOptionsMonitor<RabbitMQLoggerOptions> options)
  15. {
  16. loggerOptions = options.CurrentValue;
  17. }
  18.  
  19. /// <summary>
  20. /// 创建Logger对象
  21. /// </summary>
  22. /// <param name="categoryName"></param>
  23. /// <returns></returns>
  24. public ILogger CreateLogger(string categoryName)
  25. {
  26. //可缓存实例,这里略过了
  27. return new RabbitLogger(categoryName, loggerOptions);
  28. }
  29.  
  30. /// <summary>
  31. /// 释放
  32. /// </summary>
  33. public void Dispose()
  34. {
  35.  
  36. }
  37. }
  38. }

RabbitLoggerProvider

  

  1. using Microsoft.Extensions.Logging;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6.  
  7. namespace RabbitMQDemo
  8. {
  9. public class RabbitLogger : ILogger, IDisposable
  10. {
  11. string category;
  12. RabbitMQLoggerOptions loggerOptions;
  13. RabbitMQProducer producer;
  14.  
  15. public RabbitLogger(string category, RabbitMQLoggerOptions options)
  16. {
  17. this.category = category;
  18. this.loggerOptions = options;
  19.  
  20. producer = new RabbitMQProducer(options.Hosts);
  21. producer.Password = options.Password;
  22. producer.UserName = options.UserName;
  23. producer.Port = options.Port;
  24. producer.VirtualHost = options.VirtualHost;
  25. }
  26.  
  27. public IDisposable BeginScope<TState>(TState state)
  28. {
  29. return this;
  30. }
  31. /// <summary>
  32. /// 释放
  33. /// </summary>
  34. public void Dispose()
  35. {
  36. GC.Collect();
  37. }
  38. /// <summary>
  39. /// 是否记录日志
  40. /// </summary>
  41. /// <param name="logLevel"></param>
  42. /// <returns></returns>
  43. public bool IsEnabled(LogLevel logLevel)
  44. {
  45. //只记录日志等级大于指定最小等级且属于Rabbit分类的日志
  46. return logLevel >= loggerOptions.MinLevel && category.Contains(loggerOptions.Category, StringComparison.OrdinalIgnoreCase);
  47. }
  48.  
  49. /// <summary>
  50. /// 日志记录
  51. /// </summary>
  52. /// <typeparam name="TState"></typeparam>
  53. /// <param name="logLevel"></param>
  54. /// <param name="eventId"></param>
  55. /// <param name="state"></param>
  56. /// <param name="exception"></param>
  57. /// <param name="formatter"></param>
  58. public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
  59. {
  60. if (IsEnabled(logLevel))
  61. {
  62. string message = "";
  63. if (state != null)
  64. {
  65. message = state.ToString();
  66. }
  67. if (exception != null)
  68. {
  69. message += Environment.NewLine + formatter?.Invoke(state, exception);
  70. }
  71. //发送消息
  72. producer.Publish(loggerOptions.Queue, message, options =>
  73. {
  74. options.Arguments = loggerOptions.Arguments;
  75. options.AutoDelete = loggerOptions.AutoDelete;
  76. options.Durable = loggerOptions.Durable;
  77. });
  78. }
  79. }
  80. }
  81. }

RabbitLogger

  接着,我们修改Startup的服务对象:  

  

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. using Microsoft.AspNetCore.Builder;
  6. using Microsoft.AspNetCore.Hosting;
  7. using Microsoft.AspNetCore.Http;
  8. using Microsoft.AspNetCore.HttpsPolicy;
  9. using Microsoft.AspNetCore.Mvc;
  10. using Microsoft.Extensions.Configuration;
  11. using Microsoft.Extensions.DependencyInjection;
  12. using Microsoft.Extensions.Hosting;
  13. using Microsoft.Extensions.Logging;
  14.  
  15. namespace RabbitMQDemo
  16. {
  17. public class Startup
  18. {
  19. public Startup(IConfiguration configuration)
  20. {
  21. Configuration = configuration;
  22. }
  23.  
  24. public IConfiguration Configuration { get; }
  25.  
  26. // This method gets called by the runtime. Use this method to add services to the container.
  27. public void ConfigureServices(IServiceCollection services)
  28. {
  29. services.Configure<CookiePolicyOptions>(options =>
  30. {
  31. // This lambda determines whether user consent for non-essential cookies is needed for a given request.
  32. options.CheckConsentNeeded = context => true;
  33. options.MinimumSameSitePolicy = SameSiteMode.None;
  34. });
  35.  
  36. //配置消息发布
  37. services.Configure<RabbitMQLoggerOptions>(options =>
  38. {
  39. options.Category = "Rabbit";
  40. options.Hosts = new string[] { "192.168.187.129" };
  41. options.MinLevel = Microsoft.Extensions.Logging.LogLevel.Information;
  42. options.Password = "123456";
  43. options.Port = 5672;
  44. options.Queue = "queue1";
  45. options.UserName = "admin";
  46. options.VirtualHost = "/";
  47. options.Arguments = new Dictionary<string, object>() { { "x-queue-type", "classic" } };
  48. options.AutoDelete = false;
  49. options.Durable = true;
  50. });
  51. //将RabbitLoggerProvider加入到容器中
  52. services.AddSingleton<ILoggerProvider, RabbitLoggerProvider>();
  53.  
  54. //配置消息消费
  55. services.Configure<RabbitMQConsumerOptions>(options =>
  56. {
  57. options.Hosts = new string[] { "192.168.187.129" };
  58. options.Password = "123456";
  59. options.Port = 5672;
  60. options.Queue = "queue1";
  61. options.UserName = "admin";
  62. options.VirtualHost = "/";
  63. options.Arguments = new Dictionary<string, object>() { { "x-queue-type", "classic" } };
  64. options.AutoDelete = false;
  65. options.Durable = true;
  66. options.AutoAck = false;
  67. });
  68. //注入消费者
  69. services.AddSingleton<IHostedService, RabbitHostedService>();
  70.  
  71. services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
  72. }
  73.  
  74. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
  75. public void Configure(IApplicationBuilder app, Microsoft.Extensions.Hosting.IHostingEnvironment env)
  76. {
  77. if (env.IsDevelopment())
  78. {
  79. app.UseDeveloperExceptionPage();
  80. }
  81. else
  82. {
  83. app.UseExceptionHandler("/Home/Error");
  84. app.UseHsts();
  85. }
  86.  
  87. app.UseHttpsRedirection();
  88. app.UseStaticFiles();
  89. app.UseCookiePolicy();
  90.  
  91. app.UseMvc(routes =>
  92. {
  93. routes.MapRoute(
  94. name: "default",
  95. template: "{controller=Home}/{action=Index}/{id?}");
  96. });
  97. }
  98. }
  99. }

Startup

  顺带提一下,这个Startup中服务最好使用拓展方法去实现,这里我是为了简单没有采用拓展方法,所以在开发时多注意一下吧

  另外,我们也可以调整一下RabbitHostedService:  

  

  1. using Microsoft.Extensions.Hosting;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using System.IO;
  8. using Microsoft.Extensions.Options;
  9.  
  10. namespace RabbitMQDemo
  11. {
  12. public class RabbitHostedService : IHostedService
  13. {
  14. RabbitMQConsumerOptions consumerOptions;
  15. RabbitMQConsumer consumer;
  16.  
  17. public RabbitHostedService(IOptions<RabbitMQConsumerOptions> options)
  18. {
  19. consumerOptions = options.Value;
  20.  
  21. consumer = new RabbitMQConsumer(consumerOptions.Hosts);
  22. consumer.Password = consumerOptions.Password;
  23. consumer.UserName = consumerOptions.UserName;
  24. consumer.VirtualHost = consumerOptions.VirtualHost;
  25. consumer.Port = consumerOptions.Port;
  26. }
  27.  
  28. /// <summary>
  29. /// 服务启动
  30. /// </summary>
  31. /// <param name="cancellationToken"></param>
  32. /// <returns></returns>
  33. public async Task StartAsync(CancellationToken cancellationToken)
  34. {
  35. await Task.Run(() =>
  36. {
  37. consumer.Received += new Action<RecieveResult>(result =>
  38. {
  39. //文件路径
  40. string path = Path.Combine(Directory.GetCurrentDirectory(), "logs");
  41. if (!Directory.Exists(path))
  42. {
  43. Directory.CreateDirectory(path);
  44. }
  45.  
  46. //文件
  47. string fileName = Path.Combine(path, $"{DateTime.Now.ToString("yyyyMMdd")}.log");
  48. string message = $"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}接收到消息:{result.Body}{Environment.NewLine}";
  49. File.AppendAllText(fileName, message);
  50.  
  51. //提交
  52. result.Commit();
  53. });
  54.  
  55. consumer.Listen(consumerOptions.Queue, options =>
  56. {
  57. options.AutoAck = consumerOptions.AutoAck;
  58. options.Arguments = consumerOptions.Arguments;
  59. options.AutoDelete = consumerOptions.AutoDelete;
  60. options.Durable = consumerOptions.Durable;
  61. });
  62. });
  63. }
  64. /// <summary>
  65. /// 服务停止
  66. /// </summary>
  67. /// <param name="cancellationToken"></param>
  68. /// <returns></returns>
  69. public async Task StopAsync(CancellationToken cancellationToken)
  70. {
  71. var task = Task.Run(() =>
  72. {
  73. consumer.Close();
  74. });
  75.  
  76. await Task.WhenAny(task, Task.Delay(-1, cancellationToken));
  77. cancellationToken.ThrowIfCancellationRequested();
  78. }
  79. }
  80. }

RabbitHostedService

  到这里,rabbitmq就被继承到.net core的日志记录功能中去了,但是这里面为了避免记录不要要的日志,我在其中添加了一个限制,rabbitmq只记录categoryName包含Rabbit的日志,这样一来,我们就可以在我们的控制器中使用:  

  

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. using Microsoft.AspNetCore.Mvc;
  7. using Microsoft.Extensions.Logging;
  8. using RabbitMQDemo.Models;
  9.  
  10. namespace RabbitMQDemo.Controllers
  11. {
  12. public class RabbitController : Controller
  13. {
  14. ILogger logger;
  15. public RabbitController(ILoggerFactory loggerFactory)
  16. {
  17. //下面的logger的categoryName=typeof(RabbitController).FullName=RabbitMQDemo.Controllers.RabbitController
  18. logger = loggerFactory.CreateLogger<RabbitController>();
  19. }
  20.  
  21. /// <summary>
  22. /// 首页
  23. /// </summary>
  24. /// <returns></returns>
  25. public IActionResult Index()
  26. {
  27. return View();
  28. }
  29. /// <summary>
  30. /// 消息提交
  31. /// </summary>
  32. /// <param name="message"></param>
  33. /// <returns></returns>
  34. public IActionResult Submit(string message)
  35. {
  36. if (!string.IsNullOrEmpty(message))
  37. {
  38. //发送消息,这里也可以检验日志级别的过滤
  39. logger.LogCritical($"Log from Critical:{message}");
  40. logger.LogDebug($"Log from Debug:{message}");
  41. logger.LogError($"Log from Error:{message}");
  42. logger.LogInformation($"Log from Information:{message}");
  43. logger.LogTrace($"Log from Trace:{message}");
  44. logger.LogWarning($"Log from Warning:{message}");
  45. }
  46. return View("Index");
  47. }
  48. }
  49. }

RabbitController

  现在可以启动项目,输入http://localhost:5000/Rabbit就可以进入页面测试了,可以发现功能和上面是一样的

  细心的朋友可能会发现,本博文内使用的rabbitmq其实是它6中模式中最简单的一种模式:hello world模式,读者可以将上面的代码稍作修改,即可变成其他几种模式,但在现实开发中,具体使用哪种模式需要根据自己的业务需求定。 

  其实,有一个很好用的第三方封装好的插件,可以让我们很方便的操作rabbitmq,那就是EasyNetQ,可以使用一个消息总栈IBus来进行消息的操作,包含一系列消息模式:Publish/Subscribe, Request/Response和 Send/Receive,这些消息模式也是我们最常用的,所以以后有空再写

.net core使用rabbitmq消息队列的更多相关文章

  1. .net core使用rabbitmq消息队列 (二)

    之前有写过.net core集成使用rabbitmq的博文,见.net core使用rabbitmq消息队列,但是里面的使用很简单,而且还有几个bug,想改下,但是后来想了想,还是算了,之前使用的是. ...

  2. 基于ASP.NET Core 5.0使用RabbitMQ消息队列实现事件总线(EventBus)

    文章阅读请前先参考看一下 https://www.cnblogs.com/hudean/p/13858285.html 安装RabbitMQ消息队列软件与了解C#中如何使用RabbitMQ 和 htt ...

  3. (转)RabbitMQ消息队列(七):适用于云计算集群的远程调用(RPC)

    在云计算环境中,很多时候需要用它其他机器的计算资源,我们有可能会在接收到Message进行处理时,会把一部分计算任务分配到其他节点来完成.那么,RabbitMQ如何使用RPC呢?在本篇文章中,我们将会 ...

  4. RabbitMQ消息队列(七):适用于云计算集群的远程调用(RPC)

            在云计算环境中,很多时候需要用它其他机器的计算资源,我们有可能会在接收到Message进行处理时,会把一部分计算任务分配到其他节点来完成.那么,RabbitMQ如何使用RPC呢?在本篇 ...

  5. RabbitMQ消息队列(一): Detailed Introduction 详细介绍

     http://blog.csdn.net/anzhsoft/article/details/19563091 RabbitMQ消息队列(一): Detailed Introduction 详细介绍 ...

  6. RabbitMQ消息队列1: Detailed Introduction 详细介绍

    1. 历史 RabbitMQ是一个由erlang开发的AMQP(Advanced Message Queue )的开源实现.AMQP 的出现其实也是应了广大人民群众的需求,虽然在同步消息通讯的世界里有 ...

  7. (转)RabbitMQ消息队列(九):Publisher的消息确认机制

    在前面的文章中提到了queue和consumer之间的消息确认机制:通过设置ack.那么Publisher能不到知道他post的Message有没有到达queue,甚至更近一步,是否被某个Consum ...

  8. (转)RabbitMQ消息队列(六):使用主题进行消息分发

    在上篇文章RabbitMQ消息队列(五):Routing 消息路由 中,我们实现了一个简单的日志系统.Consumer可以监听不同severity的log.但是,这也是它之所以叫做简单日志系统的原因, ...

  9. (转)RabbitMQ消息队列(四):分发到多Consumer(Publish/Subscribe)

    上篇文章中,我们把每个Message都是deliver到某个Consumer.在这篇文章中,我们将会将同一个Message deliver到多个Consumer中.这个模式也被成为 "pub ...

随机推荐

  1. Linux基础命令---vmstat显示虚拟内存状态

    vmstat vmstat指令用来显示虚拟内存使用状态,同时也可以显示进程.cpu活动情况.vmstat报告有关进程.内存.分页.块IO.陷阱和CPU活动的信息.生成的第一份报告给出了自上次重新启动以 ...

  2. 编译安装redis之快速增加redis节点

    #: 下载安装包 [root@localhost ~]# wget http://download.redis.io/releases/redis-4.0.14.tar.gz #:解压 [root@l ...

  3. SpringBoot(2):运行原理

    一. pom.xml 进入父项目,这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心:以后我们导入依赖默认是不需要写版本:但是如果导入的包没有在依赖中管 ...

  4. System.exit(-1)和return 的区别

    对于只有一个单一方法的类或者系统来说是一样的,但是对于含有多个类和方法,且调用关系比较复杂时就不一样了. System.exit(-1)是指所有程序(方法,类等)停止,系统停止运行. return只是 ...

  5. List如何一边遍历,一边删除?

    1.新手常犯的错误 可能很多新手(包括当年的我,哈哈)第一时间想到的写法是下面这样的: public static void main(String[] args) { List<String& ...

  6. JUC概述

    JUC概述1: 首先是进程和线程的概念: 进程:是指系统在系统中正在运行的一个应用程序,程序一旦运行就是进程,进程是资源分配的最小单位 线程:进程之内独立执行,是程序执行的最小单位 线程的六大状态:在 ...

  7. Java(运算符)

    运算符 Java语言支持的运算符: 算术运算符:+,-,*,/,%(取余.求余)[模运算],++(自增),--(自减) 赋值运算符:= 关系运算符:>,<,>=(大于等于),< ...

  8. Python matplotlib绘图设置坐标轴的标题

    一.语法简介 plt.xlabel("销售月份",fontsize=16,color='red',fontweight='bold',loc='center',background ...

  9. 【紧急】Log4j又发新版2.17.0,只有彻底搞懂漏洞原因,才能以不变应万变,小白也能看懂

    1 事件背景 经过一周时间的Log4j2 RCE事件的发酵,事情也变也越来越复杂和有趣,就连 Log4j 官方紧急发布了 2.15.0 版本之后没有过多久,又发声明说 2.15.0 版本也没有完全解决 ...

  10. CF248A Cupboards 题解

    Content 在一个走廊上有 \(2n\) 扇门,排成两列分居左右.有个人很无聊,随意地开关了一些门,使得这些门看起来十分乱.现在请开关一些门,使得这些门恢复原来整齐的状态(要么都开.要么都关.要么 ...