Esempio n. 1
0
        async Task SendMessagesPump()
        {
            try
            {
                Events.StartSendMessagesPump(this);
                int batchSize = this.options.BatchSize * this.Endpoint.FanOutFactor;

                // Keep the stores and prefetchers for each priority loaded
                // for the duration of the pump
                var messageProviderPairs = new Dictionary <uint, (IMessageIterator, StoreMessagesProvider)>();

                // Outer loop to maintain the message pump until the executor shuts down
                while (!this.cts.IsCancellationRequested)
                {
                    try
                    {
                        await this.hasMessagesInQueue.WaitAsync(this.options.BatchTimeout);

                        SortedDictionary <uint, EndpointExecutorFsm> snapshot = this.prioritiesToFsms;
                        bool haveMessagesRemaining = false;

                        // Iterate through all the message queues in priority order
                        foreach (KeyValuePair <uint, EndpointExecutorFsm> entry in snapshot)
                        {
                            // Also check for cancellation in every inner loop,
                            // since it could take time to send a batch of messages
                            if (this.cts.IsCancellationRequested)
                            {
                                break;
                            }

                            uint priority           = entry.Key;
                            EndpointExecutorFsm fsm = entry.Value;

                            // Update the lastUsedFsm to be the current FSM
                            this.lastUsedFsm = fsm;

                            (IMessageIterator, StoreMessagesProvider)pair;
                            if (!messageProviderPairs.TryGetValue(priority, out pair))
                            {
                                // Create and cache a new pair for the message provider
                                // so we can reuse it every loop
                                pair.Item1 = this.messageStore.GetMessageIterator(GetMessageQueueId(this.Endpoint.Id, priority), fsm.Checkpointer.Offset + 1);
                                pair.Item2 = new StoreMessagesProvider(pair.Item1, batchSize);
                                messageProviderPairs.Add(priority, pair);
                            }

                            StoreMessagesProvider storeMessagesProvider = pair.Item2;
                            IMessage[]            messages = await storeMessagesProvider.GetMessages();

                            if (messages.Length > 0)
                            {
                                // Tag the message with the priority that we're currently
                                // processing, so it can be used by metrics later
                                messages.Select(m => m.ProcessedPriority = priority);

                                Events.ProcessingMessages(this, messages, priority);
                                await this.ProcessMessages(messages, fsm);

                                Events.SendMessagesSuccess(this, messages, priority);
                                MetricsV0.DrainedCountIncrement(this.Endpoint.Id, messages.Length, priority);

                                // Only move on to the next priority if the queue for the current
                                // priority is empty. If we processed any messages, break out of
                                // the inner loop to restart at the beginning of the priorities list
                                // again. This is so we can catch and process any higher priority
                                // messages that came in while we were sending the current batch
                                haveMessagesRemaining = true;
                                break;
                            }
                        }

                        if (!haveMessagesRemaining)
                        {
                            // All the message queues have been drained, reset the hasMessagesInQueue flag.
                            this.hasMessagesInQueue.Reset();
                        }
                    }
                    catch (Exception ex)
                    {
                        Events.SendMessagesError(this, ex);
                        // Swallow exception and keep trying.
                    }
                }
            }
            catch (Exception ex)
            {
                Events.SendMessagesPumpFailure(this, ex);
            }
        }
Esempio n. 2
0
        static async Task <int> MainAsync(IConfigurationRoot configuration)
        {
            string logLevel = configuration.GetValue($"{Logger.RuntimeLogLevelEnvKey}", "info");

            Logger.SetLogLevel(logLevel);

            // Set the LoggerFactory used by the Routing code.
            if (configuration.GetValue("EnableRoutingLogging", false))
            {
                Routing.LoggerFactory = Logger.Factory;
            }

            ILogger logger = Logger.Factory.CreateLogger("EdgeHub");

            EdgeHubCertificates certificates = await EdgeHubCertificates.LoadAsync(configuration, logger);

            bool clientCertAuthEnabled = configuration.GetValue(Constants.ConfigKey.EdgeHubClientCertAuthEnabled, false);

            string       sslProtocolsConfig = configuration.GetValue(Constants.ConfigKey.SslProtocols, string.Empty);
            SslProtocols sslProtocols       = SslProtocolsHelper.Parse(sslProtocolsConfig, DefaultSslProtocols, logger);

            logger.LogInformation($"Enabling SSL protocols: {sslProtocols.Print()}");

            IDependencyManager dependencyManager = new DependencyManager(configuration, certificates.ServerCertificate, certificates.TrustBundle, sslProtocols);
            Hosting            hosting           = Hosting.Initialize(configuration, certificates.ServerCertificate, dependencyManager, clientCertAuthEnabled, sslProtocols);
            IContainer         container         = hosting.Container;

            logger.LogInformation("Initializing Edge Hub");
            LogLogo(logger);
            LogVersionInfo(logger);
            logger.LogInformation($"OptimizeForPerformance={configuration.GetValue("OptimizeForPerformance", true)}");
            logger.LogInformation($"MessageAckTimeoutSecs={configuration.GetValue("MessageAckTimeoutSecs", 30)}");
            logger.LogInformation("Loaded server certificate with expiration date of {0}", certificates.ServerCertificate.NotAfter.ToString("o"));

            var metricsProvider = container.Resolve <IMetricsProvider>();

            Metrics.InitWithAspNet(metricsProvider, logger); // Note this requires App.UseMetricServer() to be called in Startup.cs

            // Init V0 Metrics
            MetricsV0.BuildMetricsCollector(configuration);

            // EdgeHub and CloudConnectionProvider have a circular dependency. So need to Bind the EdgeHub to the CloudConnectionProvider.
            IEdgeHub edgeHub = await container.Resolve <Task <IEdgeHub> >();

            ICloudConnectionProvider cloudConnectionProvider = await container.Resolve <Task <ICloudConnectionProvider> >();

            cloudConnectionProvider.BindEdgeHub(edgeHub);

            // EdgeHub cloud proxy and DeviceConnectivityManager have a circular dependency,
            // so the cloud proxy has to be set on the DeviceConnectivityManager after both have been initialized.
            var deviceConnectivityManager        = container.Resolve <IDeviceConnectivityManager>();
            IConnectionManager connectionManager = await container.Resolve <Task <IConnectionManager> >();

            (deviceConnectivityManager as DeviceConnectivityManager)?.SetConnectionManager(connectionManager);

            // Register EdgeHub credentials
            var edgeHubCredentials             = container.ResolveNamed <IClientCredentials>("EdgeHubCredentials");
            ICredentialsCache credentialsCache = await container.Resolve <Task <ICredentialsCache> >();

            await credentialsCache.Add(edgeHubCredentials);

            // Initializing configuration
            logger.LogInformation("Initializing configuration");
            IConfigSource configSource = await container.Resolve <Task <IConfigSource> >();

            ConfigUpdater configUpdater = await container.Resolve <Task <ConfigUpdater> >();

            await configUpdater.Init(configSource);

            if (!Enum.TryParse(configuration.GetValue("AuthenticationMode", string.Empty), true, out AuthenticationMode authenticationMode) ||
                authenticationMode != AuthenticationMode.Cloud)
            {
                ConnectionReauthenticator connectionReauthenticator = await container.Resolve <Task <ConnectionReauthenticator> >();

                connectionReauthenticator.Init();
            }

            TimeSpan shutdownWaitPeriod = TimeSpan.FromSeconds(configuration.GetValue("ShutdownWaitPeriod", DefaultShutdownWaitPeriod));

            (CancellationTokenSource cts, ManualResetEventSlim completed, Option <object> handler) = ShutdownHandler.Init(shutdownWaitPeriod, logger);

            using (IProtocolHead protocolHead = await GetEdgeHubProtocolHeadAsync(logger, configuration, container, hosting))
                using (var renewal = new CertificateRenewal(certificates, logger))
                {
                    try
                    {
                        await protocolHead.StartAsync();

                        await Task.WhenAny(cts.Token.WhenCanceled(), renewal.Token.WhenCanceled());
                    }
                    catch (Exception ex)
                    {
                        logger.LogError($"Error starting protocol heads: {ex.Message}");
                    }

                    logger.LogInformation("Stopping the protocol heads...");
                    await protocolHead.CloseAsync(CancellationToken.None);

                    logger.LogInformation("Protocol heads stopped.");

                    await CloseDbStoreProviderAsync(container);
                }

            completed.Set();
            handler.ForEach(h => GC.KeepAlive(h));
            logger.LogInformation("Shutdown complete.");
            return(0);
        }