Esempio n. 1
0
        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;
            }
        }