/EasyNetQ/PersistentConnection.cs

http://github.com/mikehadlow/EasyNetQ · C# · 130 lines · 104 code · 19 blank · 7 comment · 15 complexity · 5db3c0adba78f052c0bf12ec14ca1e64 MD5 · raw file

  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.Threading;
  5. using RabbitMQ.Client;
  6. using RabbitMQ.Client.Exceptions;
  7. namespace EasyNetQ
  8. {
  9. public interface IPersistentConnection : IDisposable
  10. {
  11. event Action Connected;
  12. event Action Disconnected;
  13. bool IsConnected { get; }
  14. IModel CreateModel();
  15. void AddSubscriptionAction(Action subscriptionAction);
  16. }
  17. /// <summary>
  18. /// A connection that attempts to reconnect if the inner connection is closed.
  19. /// </summary>
  20. public class PersistentConnection : IPersistentConnection
  21. {
  22. private const int connectAttemptIntervalMilliseconds = 5000;
  23. private readonly IConnectionFactory connectionFactory;
  24. private readonly IEasyNetQLogger logger;
  25. private IConnection connection;
  26. private readonly ConcurrentBag<Action> subscribeActions;
  27. public PersistentConnection(IConnectionFactory connectionFactory, IEasyNetQLogger logger)
  28. {
  29. this.connectionFactory = connectionFactory;
  30. this.logger = logger;
  31. this.subscribeActions = new ConcurrentBag<Action>();
  32. TryToConnect(null);
  33. }
  34. public event Action Connected;
  35. public event Action Disconnected;
  36. public IModel CreateModel()
  37. {
  38. if(!IsConnected)
  39. {
  40. throw new EasyNetQException("Rabbit server is not connected.");
  41. }
  42. return connection.CreateModel();
  43. }
  44. public void AddSubscriptionAction(Action subscriptionAction)
  45. {
  46. subscribeActions.Add(subscriptionAction);
  47. try
  48. {
  49. subscriptionAction();
  50. }
  51. catch (OperationInterruptedException)
  52. {
  53. // Looks like the channel closed between our IsConnected check
  54. // and the subscription action. Do nothing here, when the
  55. // connection comes back, the subcription action will be run then.
  56. }
  57. }
  58. public bool IsConnected
  59. {
  60. get { return connection != null && connection.IsOpen && !disposed; }
  61. }
  62. void StartTryToConnect()
  63. {
  64. var timer = new Timer(TryToConnect);
  65. timer.Change(connectAttemptIntervalMilliseconds, Timeout.Infinite);
  66. }
  67. void TryToConnect(object timer)
  68. {
  69. if(timer != null) ((Timer) timer).Dispose();
  70. logger.DebugWrite("Trying to connect");
  71. if (disposed) return;
  72. try
  73. {
  74. connection = connectionFactory.CreateConnection();
  75. connection.ConnectionShutdown += OnConnectionShutdown;
  76. if (Connected != null) Connected();
  77. logger.InfoWrite("Connected to RabbitMQ. Broker: '{0}', VHost: '{1}'", connectionFactory.HostName, connectionFactory.VirtualHost);
  78. logger.DebugWrite("Re-creating subscribers");
  79. foreach (var subscribeAction in subscribeActions)
  80. {
  81. subscribeAction();
  82. }
  83. }
  84. catch (BrokerUnreachableException brokerUnreachableException)
  85. {
  86. logger.ErrorWrite("Failed to connect to Broker: '{0}', VHost: '{1}'. Retrying in {2} ms\n" +
  87. "Check HostName, VirtualHost, Username and Password.",
  88. connectionFactory.HostName,
  89. connectionFactory.VirtualHost,
  90. connectAttemptIntervalMilliseconds,
  91. brokerUnreachableException.Message);
  92. StartTryToConnect();
  93. }
  94. }
  95. void OnConnectionShutdown(IConnection _, ShutdownEventArgs reason)
  96. {
  97. if (disposed) return;
  98. if (Disconnected != null) Disconnected();
  99. // try to reconnect and re-subscribe
  100. logger.InfoWrite("Disconnected from RabbitMQ Broker");
  101. StartTryToConnect();
  102. }
  103. private bool disposed = false;
  104. public void Dispose()
  105. {
  106. if (disposed) return;
  107. disposed = true;
  108. if (connection != null) connection.Dispose();
  109. }
  110. }
  111. }