internal Connection(string stanClusterID, string clientID, StanOptions options) { this.clientID = clientID; connID = Google.Protobuf.ByteString.CopyFrom(System.Text.Encoding.UTF8.GetBytes(pubNUID.Next)); opts = (options != null) ? new StanOptions(options) : new StanOptions(); if (opts.natsConn == null) { ncOwned = true; try { nc = new ConnectionFactory().CreateConnection(opts.NatsURL); nc.Opts.MaxReconnect = Options.ReconnectForever; // TODO: disable buffering. } catch (Exception ex) { throw new StanConnectionException(ex); } } else { nc = opts.natsConn; ncOwned = false; } // Prepare a subscription on ping responses, even if we are not // going to need it, so that if that fails, it fails before initiating // a connection. pingSubscription = nc.SubscribeAsync(newInbox(), processPingResponse); // create a heartbeat inbox string hbInbox = newInbox(); hbSubscription = nc.SubscribeAsync(hbInbox, processHeartBeat); string discoverSubject = opts.discoverPrefix + "." + stanClusterID; // The streaming server expects seconds, but can handle millis // millis are denoted by negative numbers. int pi; if (opts.PingInterval < 1000) { pi = opts.pingInterval * -1; } else { pi = opts.pingInterval / 1000; } ConnectRequest req = new ConnectRequest { ClientID = clientID, HeartbeatInbox = hbInbox, ConnID = (Google.Protobuf.ByteString)connID, Protocol = StanConsts.protocolOne, PingMaxOut = opts.PingMaxOutstanding, PingInterval = pi }; Msg cr; try { cr = nc.Request(discoverSubject, ProtocolSerializer.marshal(req), opts.ConnectTimeout); } catch (NATSTimeoutException) { protoUnsubscribe(); throw new StanConnectRequestTimeoutException( string.Format("No response from a streaming server with a cluster ID of '{0}'", stanClusterID)); } ConnectResponse response = new ConnectResponse(); try { ProtocolSerializer.unmarshal(cr.Data, response); } catch (Exception e) { protoUnsubscribe(); throw new StanConnectRequestException(e); } if (!string.IsNullOrEmpty(response.Error)) { protoUnsubscribe(); 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); // TODO - check out sub map and chans bool unsubPing = true; // Do this with servers which are at least at protcolOne. if (response.Protocol >= StanConsts.protocolOne) { // Note that in the future server may override client ping // interval value sent in ConnectRequest, so use the // value in ConnectResponse to decide if we send PINGs // and at what interval. // In tests, the interval could be negative to indicate // milliseconds. if (response.PingInterval != 0) { unsubPing = false; // These will be immutable pingRequests = response.PingRequests; pingInbox = pingSubscription.Subject; // negative values returned from the server are ms if (response.PingInterval < 0) { pingInterval = response.PingInterval * -1; } else { // if positive, the result is in seconds, but // in the .NET clients we always use millis. pingInterval = response.PingInterval * 1000; } pingMaxOut = response.PingMaxOut; pingBytes = ProtocolSerializer.createPing(connID); pingTimer = new Timer(pingServer, null, pingInterval, Timeout.Infinite); } } if (unsubPing) { pingSubscription.Unsubscribe(); pingSubscription = null; } }
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); }