static void Main(string[] args)
        {
            // optimizing IOCP performance
            int minWorkerThreads;
            int minCompletionPortThreads;
            ThreadPool.GetMinThreads(out minWorkerThreads, out minCompletionPortThreads);
            ThreadPool.SetMinThreads(minWorkerThreads, Math.Max(16, minCompletionPortThreads));

            int threadCount = Environment.ProcessorCount;
            if (args.Length > 0)
            {
                threadCount = int.Parse(args[0]);
            }

            var eventListener = new ObservableEventListener();
            eventListener.LogToConsole();
            eventListener.EnableEvents(BootstrapperEventSource.Log, EventLevel.Verbose);
            eventListener.EnableEvents(MqttIotHubAdapterEventSource.Log, EventLevel.Verbose);
            eventListener.EnableEvents(DefaultEventSource.Log, EventLevel.Verbose);

            try
            {
                var cts = new CancellationTokenSource();

                var certificate = new X509Certificate2("protocol-gateway.contoso.com.pfx", "password");
                var settingsProvider = new AppConfigSettingsProvider();
                BlobSessionStatePersistenceProvider blobSessionStateProvider = BlobSessionStatePersistenceProvider.CreateAsync(
                    settingsProvider.GetSetting("BlobSessionStatePersistenceProvider.StorageConnectionString"),
                    settingsProvider.GetSetting("BlobSessionStatePersistenceProvider.StorageContainerName")).Result;

                TableQos2StatePersistenceProvider tableQos2StateProvider = TableQos2StatePersistenceProvider.CreateAsync(
                    settingsProvider.GetSetting("TableQos2StatePersistenceProvider.StorageConnectionString"),
                    settingsProvider.GetSetting("TableQos2StatePersistenceProvider.StorageTableName")).Result;

                var bootstrapper = new Bootstrapper(settingsProvider, blobSessionStateProvider, tableQos2StateProvider);
                Task.Run(() => bootstrapper.RunAsync(certificate, threadCount, cts.Token), cts.Token);

                while (true)
                {
                    string input = Console.ReadLine();
                    if (input != null && input.ToLowerInvariant() == "exit")
                    {
                        break;
                    }
                }

                cts.Cancel();
                bootstrapper.CloseCompletion.Wait(TimeSpan.FromSeconds(20));
            }
            finally
            {
                eventListener.Dispose();
            }
        }
        async Task RunAsync(CancellationToken cancellationToken)
        {
            var settingsProvider = new RoleEnvironmentSettingsProvider();

            var eventListener = new ObservableEventListener();
            eventListener.LogToWindowsAzureTable(RoleEnvironment.CurrentRoleInstance.Id, settingsProvider.GetSetting("BlobSessionStatePersistenceProvider.StorageConnectionString"), bufferingInterval: TimeSpan.FromMinutes(2));
            eventListener.EnableEvents(BootstrapperEventSource.Log, EventLevel.Informational);
            eventListener.EnableEvents(MqttIotHubAdapterEventSource.Log, EventLevel.Informational);
            eventListener.EnableEvents(DefaultEventSource.Log, EventLevel.Informational);
            
            int minWorkerThreads;
            int minCompletionPortThreads;
            ThreadPool.GetMinThreads(out minWorkerThreads, out minCompletionPortThreads);
            ThreadPool.SetMinThreads(minWorkerThreads, Math.Max(16, minCompletionPortThreads));

            int threadCount = Environment.ProcessorCount;
            
            BlobSessionStatePersistenceProvider blobSessionStateProvider =
                await BlobSessionStatePersistenceProvider.CreateAsync(
                    settingsProvider.GetSetting("BlobSessionStatePersistenceProvider.StorageConnectionString"),
                    settingsProvider.GetSetting("BlobSessionStatePersistenceProvider.StorageContainerName"));

            TableQos2StatePersistenceProvider tableQos2StateProvider =
                await TableQos2StatePersistenceProvider.CreateAsync(
                    settingsProvider.GetSetting("TableQos2StatePersistenceProvider.StorageConnectionString"),
                    settingsProvider.GetSetting("TableQos2StatePersistenceProvider.StorageTableName"));

            var bootstrapper = new Bootstrapper(
                settingsProvider,
                blobSessionStateProvider,
                tableQos2StateProvider);

            X509Certificate2 tlsCertificate = GetTlsCertificate(settingsProvider.GetSetting("TlsCertificateThumbprint"),
                StoreName.My, StoreLocation.LocalMachine);

            try
            {
                await bootstrapper.RunAsync(tlsCertificate, threadCount, cancellationToken);
            }
            catch (Exception ex)
            {
                Trace.TraceError(ex.ToString());
                throw;
            }
            await bootstrapper.CloseCompletion;
        }
        /// <summary>
        /// Called by the service fabric when the listener is to be started
        /// </summary>
        /// <param name="cancellationToken">A token to monitor for abort requests</param>
        /// <returns>A task that completes when the service is openned</returns>
        public async Task<string> OpenAsync(CancellationToken cancellationToken)
        {
            string publishAddress = null;
            Guid traceId = Guid.NewGuid();
            int threadCount = Environment.ProcessorCount;
            this.serverControl = new CancellationTokenSource();
            GatewayConfiguration configuration = this.configurationProvider.Config;

            this.logger.Informational(traceId, this.componentName, "OpenAsync() invoked.");

            var iotHubConfigurationProvider = new ConfigurationProvider<IoTHubConfiguration>(traceId, this.serviceContext.ServiceName, this.serviceContext.CodePackageActivationContext, this.logger, "IoTHubClient");
            iotHubConfigurationProvider.ConfigurationChangedEvent += (sender, args) => this.unsupportedConfigurationChangeCallback?.Invoke();
            iotHubConfigurationProvider.ConfigurationPropertyChangedEvent += (sender, args) => this.unsupportedConfigurationChangeCallback?.Invoke();
            IoTHubConfiguration iotHubConfiguration = iotHubConfigurationProvider.Config;

            X509Certificate2 certificate = this.GetServerCertificate(configuration);
            this.logger.Informational(traceId, this.componentName, "Certificate retrieved.");

            var mqttConfigurationProvider = new ConfigurationProvider<MqttServiceConfiguration>(traceId, this.serviceContext.ServiceName, this.serviceContext.CodePackageActivationContext, this.logger, "Mqtt");
            mqttConfigurationProvider.ConfigurationChangedEvent += (sender, args) => this.unsupportedConfigurationChangeCallback?.Invoke();
            mqttConfigurationProvider.ConfigurationPropertyChangedEvent += (sender, args) => this.unsupportedConfigurationChangeCallback?.Invoke();
            MqttServiceConfiguration mqttConfiguration = mqttConfigurationProvider.Config;
            
            if (mqttConfiguration != null)
            {
                var mqttInboundTemplates = new List<string>(mqttConfiguration.MqttInboundTemplates);
                var mqttOutboundTemplates = new List<string>(mqttConfiguration.MqttOutboundTemplates);

                ISessionStatePersistenceProvider qosSessionProvider = await this.GetSessionStateProviderAsync(traceId, mqttConfiguration).ConfigureAwait(false);
                IQos2StatePersistenceProvider qos2SessionProvider = await this.GetQos2StateProvider(traceId, mqttConfiguration).ConfigureAwait(false);

                this.logger.Informational(traceId, this.componentName, "QOS Providers instantiated.");

                var settingsProvider = new ServiceFabricConfigurationProvider(traceId, this.componentName, this.logger, configuration, iotHubConfiguration, mqttConfiguration);
                this.bootStrapper = new Bootstrapper(settingsProvider, qosSessionProvider, qos2SessionProvider, mqttInboundTemplates, mqttOutboundTemplates);
                this.runTask = this.bootStrapper.RunAsync(certificate, threadCount, this.serverControl.Token);

                publishAddress = this.BuildPublishAddress(configuration);

                this.logger.Informational(traceId, this.componentName, "Bootstrapper instantiated.");
            }
            else
            {
                this.logger.Critical(traceId, this.componentName, "Failed to start endpoint because Mqtt service configuration is missing.");
            }

            return publishAddress;
        }