internal void processMsg(object sender, MsgHandlerEventArgs args) { bool isClosed = false; AsyncSubscription sub = null; Msg raw = null; MsgProto mp = new MsgProto(); ProtocolSerializer.unmarshal(args.Message.Data, mp); raw = args.Message; lock (mu) { isClosed = (nc == null); subMap.TryGetValue(raw.Subject, out sub); } if (isClosed || sub == null) { return; } sub.processMsg(mp); }
internal void manualAck(StanMsg m) { if (m == null) { return; } rwLock.EnterReadLock(); string localAckSubject = ackInbox; bool localManualAck = options.manualAcks; Connection sc = this.sc; rwLock.ExitReadLock(); if (localManualAck == false) { throw new StanManualAckException(); } if (sc == null) { throw new StanBadSubscriptionException(); } byte[] b = ProtocolSerializer.createAck(m.proto); sc.NATSConnection.Publish(localAckSubject, b); }
internal PublishAck publish(string subject, byte[] data, EventHandler <StanAckHandlerArgs> handler) { string localAckSubject = null; long localAckTimeout = 0; string subj = this.pubPrefix + "." + subject; string guidValue = newGUID(); byte[] b = ProtocolSerializer.createPubMsg(clientID, guidValue, subject, data); PublishAck a = new PublishAck(this, guidValue, handler, opts.PubAckWait); lock (mu) { if (nc == null) { throw new StanConnectionClosedException(); } while (!pubAckMap.TryAdd(guidValue, a)) { var bd = pubAckMap; Monitor.Exit(mu); // Wait for space outside of the lock so // acks can be removed. bd.waitForSpace(); Monitor.Enter(mu); if (nc == null) { throw new StanConnectionClosedException(); } } localAckSubject = ackSubject; localAckTimeout = opts.ackTimeout; } try { nc.Publish(subj, localAckSubject, b); } catch { removeAck(guidValue); throw; } return(a); }
internal void unsubscribe(string subject, string inbox, string ackInbox, bool close) { IConnection lnc; lock (mu) { lnc = nc; if (lnc == null) { throw new StanConnectionClosedException(); } subMap.Remove(inbox); } string requestSubject = unsubRequests; if (close) { requestSubject = subCloseRequests; if (string.IsNullOrEmpty(requestSubject)) { throw new StanNoServerSupport(); } } UnsubscribeRequest usr = new UnsubscribeRequest(); usr.ClientID = clientID; usr.Subject = subject; usr.Inbox = ackInbox; byte[] b = ProtocolSerializer.marshal(usr); var r = lnc.Request(requestSubject, b, 2000); SubscriptionResponse sr = new SubscriptionResponse(); ProtocolSerializer.unmarshal(r.Data, sr); if (!string.IsNullOrEmpty(sr.Error)) { throw new StanException(sr.Error); } }
private void processAck(object sender, MsgHandlerEventArgs args) { PubAck pa = new PubAck(); try { ProtocolSerializer.unmarshal(args.Message.Data, pa); } catch (Exception) { // TODO: (cls) handle this... return; } PublishAck a = removeAck(pa.Guid); if (a != null) { a.invokeHandler(pa.Guid, pa.Error); } }
internal void processMsg(MsgProto mp) { rwLock.EnterReadLock(); EventHandler <StanMsgHandlerArgs> cb = handler; bool isManualAck = options.manualAcks; string localAckSubject = ackInbox; IStanConnection subsSc = sc; IConnection localNc = null; if (subsSc != null) { localNc = sc.NATSConnection; } rwLock.ExitReadLock(); if (cb != null && subsSc != null) { StanMsgHandlerArgs args = new StanMsgHandlerArgs(new StanMsg(mp, this)); cb(this, args); } if (!isManualAck && localNc != null) { byte[] b = ProtocolSerializer.createAck(mp); try { localNc.Publish(localAckSubject, b); } catch (Exception) { /* * Ignore - subscriber could have closed the connection * or there's been a connection error. The server will * resend the unacknowledged messages. */ } } }
// in STAN, much of this code is in the connection module. internal void subscribe(string subRequestSubject, string subject, string qgroup, EventHandler <StanMsgHandlerArgs> handler) { rwLock.EnterWriteLock(); try { this.handler += handler; this.subject = subject; if (sc == null) { throw new StanConnectionClosedException(); } // Listen for actual messages. inboxSub = sc.NATSConnection.SubscribeAsync(inbox, sc.processMsg); SubscriptionRequest sr = new SubscriptionRequest(); sr.ClientID = sc.ClientID; sr.Subject = subject; sr.QGroup = (qgroup == null ? "" : qgroup); sr.Inbox = inbox; sr.MaxInFlight = options.MaxInflight; sr.AckWaitInSecs = options.AckWait / 1000; sr.StartPosition = options.startAt; sr.DurableName = (options.DurableName == null ? "" : options.DurableName); // Conditionals switch (sr.StartPosition) { case StartPosition.TimeDeltaStart: sr.StartTimeDelta = convertTimeSpan( options.useStartTimeDelta ? options.startTimeDelta : (DateTime.UtcNow - options.startTime)); break; case StartPosition.SequenceStart: sr.StartSequence = options.startSequence; break; } byte[] b = ProtocolSerializer.marshal(sr); // TODO: Configure request timeout? Msg m = sc.NATSConnection.Request(subRequestSubject, b, 2000); SubscriptionResponse r = new SubscriptionResponse(); ProtocolSerializer.unmarshal(m.Data, r); if (string.IsNullOrWhiteSpace(r.Error) == false) { throw new StanException(r.Error); } ackInbox = r.AckInbox; } catch { if (inboxSub != null) { try { inboxSub.Unsubscribe(); } catch (NATSTimeoutException) { // NOOP - this is unrecoverable. } } throw; } finally { rwLock.ExitWriteLock(); } }
public void Close() { Msg reply = null; lock (mu) { IConnection lnc = nc; nc = null; if (lnc == null) { return; } if (lnc.IsClosed()) { return; } if (ackSubscription != null) { ackSubscription.Unsubscribe(); ackSubscription = null; } if (hbSubscription != null) { hbSubscription.Unsubscribe(); hbSubscription = null; } if (heartbeatMonitor != null) { heartbeatMonitor.Stop(); } CloseRequest req = new CloseRequest(); req.ClientID = this.clientID; try { if (this.closeRequests != null) { reply = lnc.Request(closeRequests, ProtocolSerializer.marshal(req)); } } catch (StanBadSubscriptionException) { // it's possible we never actually connected. return; } if (reply != null) { CloseResponse resp = new CloseResponse(); try { ProtocolSerializer.unmarshal(reply.Data, resp); } catch (Exception e) { throw new StanCloseRequestException(e); } if (!string.IsNullOrEmpty(resp.Error)) { throw new StanCloseRequestException(resp.Error); } } if (ncOwned && lnc != null) { lnc.Close(); } } }
internal Connection(string stanClusterID, string clientID, StanOptions options) { this.clientID = clientID; if (options != null) { opts = new StanOptions(options); } else { opts = new StanOptions(); } if (opts.natsConn == null) { ncOwned = true; try { nc = new ConnectionFactory().CreateConnection(opts.NatsURL); } catch (Exception ex) { throw new StanConnectionException(ex); } } else { nc = opts.natsConn; ncOwned = false; } // create a heartbeat inbox string hbInbox = newInbox(); hbSubscription = nc.SubscribeAsync(hbInbox, processHeartBeat); string discoverSubject = opts.discoverPrefix + "." + stanClusterID; ConnectRequest req = new ConnectRequest(); req.ClientID = this.clientID; req.HeartbeatInbox = hbInbox; Msg cr; try { cr = nc.Request(discoverSubject, ProtocolSerializer.marshal(req), opts.ConnectTimeout); } catch (NATSTimeoutException) { throw new StanConnectRequestTimeoutException(); } ConnectResponse response = new ConnectResponse(); try { ProtocolSerializer.unmarshal(cr.Data, response); } catch (Exception e) { throw new StanConnectRequestException(e); } if (!string.IsNullOrEmpty(response.Error)) { throw new StanConnectRequestException(response.Error); } // capture cluster configuration endpoints to publish and subscribe/unsubscribe pubPrefix = response.PubPrefix; subRequests = response.SubRequests; unsubRequests = response.UnsubRequests; subCloseRequests = response.SubCloseRequests; closeRequests = response.CloseRequests; // setup the Ack subscription ackSubject = StanConsts.DefaultACKPrefix + "." + newGUID(); ackSubscription = nc.SubscribeAsync(ackSubject, processAck); // TODO: hardcode or options? ackSubscription.SetPendingLimits(1024 * 1024, 32 * 1024 * 1024); pubAckMap = new BlockingDictionary <string, PublishAck>(opts.maxPubAcksInflight); // Setup server heartbeat monitor if (options != null && options.ServerHeartbeatTimeoutMillis > 0) { heartbeatMonitor = new ServerHeartbeatMonitor(1000, options.ServerHeartbeatTimeoutMillis, () => { options.ServerHeartbeatTimeoutCallback?.Invoke(); }); heartbeatMonitor.Start(); } }