public void OnError(Exception exception) { InitializePublisherOnFirstUse(); var exceptionWrapper = new SerializableException(exception); byte[] serializedException = exceptionWrapper.SerializeException(); string exceptionAsString = exception.ToString(); m_publisherSocket.SendMoreFrame(QueueName) .SendMoreFrame("E") // "N", "E" or "C" for "OnNext", "OnError" or "OnCompleted". .SendMoreFrame(exceptionAsString.SerializeProtoBuf()) // Human readable exception. Added for 100% // cross-platform debugging, so we can read // the error on the wire. .SendFrame(serializedException); // Machine readable exception. So we can pass the full exception to // the .NET client. // Comment in the remaining code for the standard pub/sub pattern. //if (this.HasObservers == false) //{ //throw new QxNoSubscribers("Error E28244. As there are no subscribers to this publisher, this published exception will be lost."); //} //lock (_subscribersLock) //{ //this._subscribers.ForEach(msg => msg.OnError(exception)); //} }
public static SerializableException DeSerializeException(this byte[] bytes) { // Round-trip the exception: Serialize and de-serialize with a BinaryFormatter. var bf = new BinaryFormatter(); using (var ms = new MemoryStream(bytes)) { ms.Seek(0, 0); // Good practice. // Replace the original exception with de-serialized one SerializableException ex = (SerializableException)bf.Deserialize(ms); return(ex); } }
public static byte[] SerializeException(this SerializableException exception) { // Round-trip the exception: Serialize and de-serialize with a BinaryFormatter. var bf = new BinaryFormatter(); using (var ms = new MemoryStream()) { // "Save" object state bf.Serialize(ms, exception); ms.Seek(0, 0); // Good practice. return(ms.ToArray()); } }
private void InitializeSubscriberOnFirstUse() { if (m_initializeSubscriberDone == false) // Double checked locking. { lock (m_subscribersLock) { if (m_initializeSubscriberDone == false) { Console.WriteLine("Subscriber socket connecting to: {0}", ZeroMqAddress); m_subscriberSocket = new SubscriberSocket(); // Corner case: wait until subscriber socket is ready (see code below that waits for // "_subscriberReadySignal"). NetMQMonitor monitor; { // Must ensure that we have a unique monitor name for every instance of this class. string endpoint = string.Format("inproc://#SubjectNetMQ#Subscriber#{0}#{1}", this.QueueName, this.ZeroMqAddress); monitor = new NetMQMonitor(m_subscriberSocket, endpoint, SocketEvents.ConnectRetried | SocketEvents.Connected); monitor.ConnectRetried += Subscriber_Event_ConnectRetried; monitor.Connected += Subscriber_Event_Connected; monitor.StartAsync(); } m_subscriberSocket.Options.ReceiveHighWatermark = 2000 * 1000; m_subscriberSocket.Connect(this.ZeroMqAddress); m_subscriberSocket.Subscribe(this.QueueName); if (m_cancellationTokenSource == null) { m_cancellationTokenSource = new CancellationTokenSource(); } ManualResetEvent threadReadySignal = new ManualResetEvent(false); m_thread = new Thread(() => { try { Console.Write("Thread initialized.\n"); threadReadySignal.Set(); while (m_cancellationTokenSource.IsCancellationRequested == false) { string messageTopicReceived = m_subscriberSocket.ReceiveFrameString(); if (messageTopicReceived != QueueName) { throw new Exception(string.Format("Error E65724. We should always subscribe on the queue name '{0}', instead we got '{1}'.", QueueName, messageTopicReceived)); } var type = m_subscriberSocket.ReceiveFrameString(); switch (type) { // Originated from "OnNext". case "N": T messageReceived = m_subscriberSocket.ReceiveFrameBytes().DeserializeProtoBuf <T>(); lock (m_subscribersLock) { m_subscribers.ForEach(o => o.OnNext(messageReceived)); } break; // Originated from "OnCompleted". case "C": lock (m_subscribersLock) { m_subscribers.ForEach(o => o.OnCompleted()); // We are done! We don't want to send any more messages to subscribers, and we // want to close the listening socket. m_cancellationTokenSource.Cancel(); } break; // Originated from "OnException". case "E": Exception exception; string exceptionAsString = "Uninitialized."; try { // Not used, but useful for cross-platform debugging: we can read the error straight off the wire. exceptionAsString = m_subscriberSocket.ReceiveFrameBytes().DeserializeProtoBuf <string>(); SerializableException exceptionWrapper = m_subscriberSocket.ReceiveFrameBytes().DeSerializeException(); exception = exceptionWrapper.InnerException; } catch (Exception ex) { // If we had trouble deserializing the exception (probably due to a // different version of .NET), then do the next best thing: (1) The // inner exception is the error we got when deserializing, and (2) the // main exception is the human-readable "exception.ToString()" that we // originally captured. exception = new Exception(exceptionAsString, ex); } lock (m_subscribersLock) { m_subscribers.ForEach(o => o.OnError(exception)); } break; // Originated from a "Ping" request. case "P": // Do nothing, this is a ping command used to wait until sockets are initialized properly. Console.Write("Received ping.\n"); break; default: throw new Exception(string.Format("Error E28734. Something is wrong - received '{0}' when we expected \"N\", \"C\" or \"E\" - are we out of sync?", type)); } } } catch (Exception ex) { Console.Write("Error E23844. Exception in threadName \"{0}\". Thread exiting. Exception: \"{1}\".\n", QueueName, ex.Message); lock (m_subscribersLock) { this.m_subscribers.ForEach((ob) => ob.OnError(ex)); } } finally { lock (m_subscribersLock) { m_subscribers.Clear(); } m_cancellationTokenSource.Dispose(); // Disconnect from the socket. m_subscriberSocket.Dispose(); } }) { Name = this.QueueName, IsBackground = true // Have to set it to background, or else it will not exit when the program exits. }; m_thread.Start(); // Wait for thread to properly spin up. threadReadySignal.WaitOne(TimeSpan.FromMilliseconds(3000)); // Corner case: wait until the publisher socket is ready (see code above that sets // "_subscriberReadySignal"). { Stopwatch sw = Stopwatch.StartNew(); m_subscriberReadySignal.WaitOne(TimeSpan.FromMilliseconds(3000)); Console.Write("Subscriber: Waited {0} ms for connection.\n", sw.ElapsedMilliseconds); monitor.ConnectRetried -= Subscriber_Event_ConnectRetried; monitor.Connected -= Subscriber_Event_Connected; // Issue with NetMQ - cannot .Stop or .Dispose, or else it will dispose of the parent socket. //monitor.Stop(); //monitor.Dispose(); } Console.Write("Subscriber: finished setup.\n"); m_initializeSubscriberDone = true; } } // lock Thread.Sleep(500); // Otherwise, the first item we subscribe to may get missed by the subscriber. } }
private void InitializeSubscriberOnFirstUse(string addressZeroMq) { if (_initializeSubscriberDone == false) // Double checked locking. { lock (_subscribersLock) { if (_initializeSubscriberDone == false) { if (_cancellationTokenSource == null) { _cancellationTokenSource = new CancellationTokenSource(); } _subscriberSocket = NetMqTransportShared.Instance(_loggerDelegate).GetSharedSubscriberSocket(addressZeroMq); ManualResetEvent threadReadySignal = new ManualResetEvent(false); _thread = new Thread(() => { try { _loggerDelegate?.Invoke(string.Format("Thread initialized.\n")); threadReadySignal.Set(); while (_cancellationTokenSource.IsCancellationRequested == false) { //_loggerDelegate?.Invoke(string.Format("Received message for {0}.\n", typeof(T))); string messageTopicReceived = _subscriberSocket.ReceiveFrameString(); if (messageTopicReceived != SubscriberFilterName) { // This message is for another subscriber. This should never occur. #if DEBUG throw new Exception("Error E38444. Internal exception, this should never occur, as the ZeroMQ lib automaticlaly filters by subject name."); #else return; #endif } var type = _subscriberSocket.ReceiveFrameString(); switch (type) { // Originated from "OnNext". case "N": T messageReceived = _subscriberSocket.ReceiveFrameBytes().DeserializeProtoBuf <T>(); lock (_subscribersLock) { _subscribers.ForEach(o => o.OnNext(messageReceived)); } break; // Originated from "OnCompleted". case "C": lock (_subscribersLock) { _subscribers.ForEach(o => o.OnCompleted()); // We are done! We don't want to send any more messages to subscribers, and we // want to close the listening socket. _cancellationTokenSource.Cancel(); } break; // Originated from "OnException". case "E": Exception exception; string exceptionAsString = "Uninitialized."; try { // Not used, but useful for cross-platform debugging: we can read the error straight off the wire. exceptionAsString = _subscriberSocket.ReceiveFrameBytes().DeserializeProtoBuf <string>(); SerializableException exceptionWrapper = _subscriberSocket.ReceiveFrameBytes().DeSerializeException(); exception = exceptionWrapper.InnerException; } catch (Exception ex) { // If we had trouble deserializing the exception (probably due to a // different version of .NET), then do the next best thing: (1) The // inner exception is the error we got when deserializing, and (2) the // main exception is the human-readable "exception.ToString()" that we // originally captured. exception = new Exception(exceptionAsString, ex); } lock (_subscribersLock) { _subscribers.ForEach(o => o.OnError(exception)); } break; // Originated from a "Ping" request. case "P": // Do nothing, this is a ping command used to wait until sockets are initialized properly. _loggerDelegate?.Invoke(string.Format("Received ping.\n")); break; default: throw new Exception(string.Format("Error E28734. Something is wrong - received '{0}' when we expected \"N\", \"C\" or \"E\" - are we out of sync?", type)); } } } catch (Exception ex) { _loggerDelegate?.Invoke(string.Format("Error E23844. Exception in threadName \"{0}\". Thread exiting. Exception: \"{1}\".\n", SubscriberFilterName, ex.Message)); lock (_subscribersLock) { this._subscribers.ForEach((ob) => ob.OnError(ex)); } } finally { lock (_subscribersLock) { _subscribers.Clear(); } _cancellationTokenSource.Dispose(); // Disconnect from the socket. _subscriberSocket.Dispose(); } }) { Name = this.SubscriberFilterName, IsBackground = true // Have to set it to background, or else it will not exit when the program exits. }; _thread.Start(); // Wait for subscriber thread to properly spin up. threadReadySignal.WaitOne(TimeSpan.FromMilliseconds(3000)); // Intent: Now connect to the socket. { _loggerDelegate?.Invoke(string.Format("Subscriber socket connecting to: {0}\n", addressZeroMq)); // this.SubscriberFilterName is set to the type T of the incoming class by default, so we can // have many types on the same transport. _subscriberSocket.Subscribe(this.SubscriberFilterName); } _loggerDelegate?.Invoke(string.Format("Subscriber: finished setup.\n")); _initializeSubscriberDone = true; } } // lock Thread.Sleep(500); // Otherwise, the first item we subscribe to may get missed by the subscriber. } }