PageRenderTime 156ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/MiniMods/ZeroMqMessageStream/ZeroMqMessageStreamMinimod.cs

https://github.com/bdb-opensource/minimods
C# | 186 lines | 150 code | 22 blank | 14 comment | 10 complexity | 9630770c75523510636f397d9fb1910a MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Dynamic;
  5. using System.Linq;
  6. using System.Reactive.Concurrency;
  7. using System.Reactive.Linq;
  8. using System.Reactive.Subjects;
  9. using System.Text;
  10. using Minimod.MessageProcessor;
  11. using Newtonsoft.Json;
  12. using ZeroMQ;
  13. namespace Minimod.ZeroMqMessageStream
  14. {
  15. /// <summary>
  16. /// Minimod.ZeroMQMessageStream, Version 0.0.5
  17. /// <para>A minimod for messaging using ZeroMQ, Json and Rx.</para>
  18. /// </summary>
  19. /// <remarks>
  20. /// Licensed under the Apache License, Version 2.0; you may not use this file except in compliance with the License.
  21. /// http://www.apache.org/licenses/LICENSE-2.0
  22. /// </remarks>
  23. public interface IMessageStreamContext : IMessageStream
  24. {
  25. void Send<T>(T values, params string[] publishAddresses);
  26. }
  27. public class MessageNotSendException : Exception
  28. {
  29. public string Reason { get; private set; }
  30. public MessageNotSendException(string reason)
  31. : base(reason)
  32. {
  33. Reason = reason;
  34. }
  35. }
  36. public class DocumentMessage : IMessage
  37. {
  38. public IMessageStreamContext MessageStreamContext { get; set; }
  39. }
  40. public class ZeroMqMessageStream : IMessageStreamContext
  41. {
  42. private readonly Subject<object> _messageStream = new Subject<object>();
  43. private readonly Guid _correlationId;
  44. private readonly ZmqContext _context;
  45. private readonly ZmqSocket _subSocket;
  46. private readonly ZmqSocket _pubSocket;
  47. private readonly IScheduler _scheduler;
  48. public ZeroMqMessageStream(params string[] publishAddresses)
  49. : this(new EventLoopScheduler(), String.Empty, publishAddresses)
  50. {
  51. }
  52. public ZeroMqMessageStream(string subsciptionAddress)
  53. : this(new EventLoopScheduler(), subsciptionAddress)
  54. {
  55. }
  56. public ZeroMqMessageStream(string subsciptionAddress, params string[] publishAddresses)
  57. : this(new EventLoopScheduler(), subsciptionAddress, publishAddresses)
  58. {
  59. }
  60. public ZeroMqMessageStream(IScheduler scheduler, string subsciptionAddress, params string[] publishAddresses)
  61. {
  62. _scheduler = scheduler;
  63. _correlationId = Guid.NewGuid();
  64. _context = ZmqContext.Create();
  65. _subSocket = _context.CreateSocket(SocketType.SUB);
  66. _pubSocket = _context.CreateSocket(SocketType.PUB);
  67. if (!String.IsNullOrEmpty(subsciptionAddress))
  68. {
  69. _subSocket.Bind(subsciptionAddress);
  70. _subSocket.Connect(subsciptionAddress);
  71. _subSocket.SubscribeAll();
  72. }
  73. foreach (var publishAddress in publishAddresses)
  74. _pubSocket.Connect(publishAddress);
  75. Scheduler.NewThread.Schedule(() =>
  76. {
  77. while (true)
  78. {
  79. var zmqMessage = _subSocket.ReceiveMessage(TimeSpan.FromMilliseconds(1000));
  80. if (zmqMessage.FrameCount >= 0 && zmqMessage.TotalSize > 1 && zmqMessage.IsComplete)
  81. {
  82. var message = Encoding.UTF8.GetString(zmqMessage.First());
  83. _messageStream.OnNext(message);
  84. Debug.WriteLine("Correlation ID: {0} - message received with status {1}", _correlationId, _subSocket.ReceiveStatus);
  85. }
  86. }
  87. });
  88. }
  89. public void Send<T>(T value)
  90. {
  91. SendInternal(value, _pubSocket);
  92. }
  93. public void Send<T>(T value, params string[] publishAddresses)
  94. {
  95. foreach (var publishAddress in publishAddresses)
  96. _pubSocket.Connect(publishAddress);
  97. SendInternal(value, _pubSocket);
  98. }
  99. private void SendInternal<T>(T value, ZmqSocket socket)
  100. {
  101. var status = socket.SendFrame(new Frame(SerializeMessage(value)));
  102. if (status.ToString() != "Sent")
  103. throw new MessageNotSendException(status.ToString());
  104. Debug.WriteLine("Correlation ID: {0} - message was {1}", _correlationId, status);
  105. }
  106. private byte[] SerializeMessage<T>(T message)
  107. {
  108. var value = message as dynamic;
  109. //add message correlation information
  110. var serializeObject = JsonConvert.SerializeObject(value);
  111. var deserializeObject = JsonConvert.DeserializeObject<ExpandoObject>(serializeObject, new Newtonsoft.Json.Converters.ExpandoObjectConverter());
  112. deserializeObject.CorrelationTimeStamp = DateTime.Now;
  113. deserializeObject.CorrelationId = _correlationId.ToString();
  114. //information for deserializer
  115. deserializeObject.MessageTypeFullName = value.GetType().FullName;
  116. deserializeObject.MessageStreamContext = null;
  117. var serializedMessage = JsonConvert.SerializeObject(deserializeObject);
  118. return Encoding.UTF8.GetBytes(serializedMessage);
  119. }
  120. public void Dispose()
  121. {
  122. _messageStream.OnCompleted();
  123. _subSocket.Close();
  124. _pubSocket.Close();
  125. _context.Terminate();
  126. _messageStream.Dispose();
  127. }
  128. public IDisposable Subscribe(IObserver<object> observer)
  129. {
  130. return _messageStream
  131. .SubscribeOn(_scheduler)
  132. .Select<object, dynamic>(jsonMessage => JsonConvert.DeserializeObject<ExpandoObject>(jsonMessage.ToString(), new Newtonsoft.Json.Converters.ExpandoObjectConverter()))
  133. .Select<object, object>(message =>
  134. {
  135. try
  136. {
  137. //resolve message type for deserializer
  138. var messageTypeDescription = ((IDictionary<string, object>)message)
  139. .Single(x => x.Key == "MessageTypeFullName")
  140. .Value
  141. .ToString();
  142. var messageType = Type.GetType(messageTypeDescription);
  143. ((IDictionary<string, object>)message).Remove("MessageTypeFullName");
  144. //i don't need it yet
  145. ((IDictionary<string, object>)message).Remove("CorrelationId");
  146. ((IDictionary<string, object>)message).Remove("CorrelationTimeStamp");
  147. //deserialize
  148. var cleanedMessage = JsonConvert.SerializeObject(message);
  149. var deserializeObject = JsonConvert.DeserializeObject(cleanedMessage, messageType, new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Error, NullValueHandling = NullValueHandling.Include });
  150. //add message stream context if possible
  151. if (deserializeObject.GetType().BaseType == typeof(DocumentMessage))
  152. ((DocumentMessage)deserializeObject).MessageStreamContext = this;
  153. return deserializeObject;
  154. }
  155. catch (Exception error)
  156. {
  157. _messageStream.OnError(error);
  158. }
  159. return message;
  160. })
  161. .Subscribe(observer);
  162. }
  163. }
  164. }