Azure AppService无法连接到SQL数据库

时间:2020-07-19 12:09:44

标签: sql-server azure azure-web-app-service

我有一个用C#编写的Azure AppService,它使用NHibernate连接到Azure外部托管的SQL Server数据库。连接字符串如下所示:

Data Source=tcp:SQL1234.3rdpartyserver.net;MultipleActiveResultSets=true;Initial Catalog=DB_SQL1234;User Id=****;Password=****;

大多数时候,一切正常,但偶尔我的AppService失去连接,并且出现以下异常:

 System.Data.SqlClient.SqlException: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 0 - A connection
 attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.) ---> System.ComponentModel.Win32Exception: A connection
 attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond --- End of inner exception stack trace
 ---
 at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling, String accessToken)
 at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)
 at System.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)
 at System.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
 at System.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)
 at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
 at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
 at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
 at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
 at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
 at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
 at System.Data.SqlClient.SqlConnection.Open()
 at NHibernate.Connection.DriverConnectionProvider.GetConnection()
 at NHibernate.Tool.hbm2ddl.SuppliedConnectionProviderConnectionHelper.Prepare()
 at NHibernate.Tool.hbm2ddl.SchemaMetadataUpdater.GetReservedWords(Dialect dialect, IConnectionHelper connectionHelper)
 at NHibernate.Tool.hbm2ddl.SchemaMetadataUpdater.Update(ISessionFactoryImplementor sessionFactory)
 at NHibernate.Impl.SessionFactoryImpl..ctor(Configuration cfg, IMapping mapping, Settings settings, EventListeners listeners)
 at NHibernate.Cfg.Configuration.BuildSessionFactory()
 at FluentNHibernate.Cfg.FluentConfiguration.BuildSessionFactory() --- End of inner exception stack trace

这无处不在:我没有更新任何连接字符串,没有重启我的AppService,等等。该应用程序仅无法从Azure连接到数据库。如果我在本地启动该应用程序,则使用相同的连接字符串,一切都会按预期工作。另外,我可以从SSMS连接到数据库。

有时重新启动AppService会有所帮助,并且重新启动后会恢复连接。但这有时无济于事。

我怀疑连接可能被Azure的防火墙阻止,但是我不知道如何检查。我的应用程序正在使用B1应用程序服务计划,并且尚未在Azure门户中创建任何自定义防火墙或负载平衡器。实际上,此AppService是我当前拥有的唯一资源。

任何想法可能是什么原因导致的,并解决该问题?

3 个答案:

答案 0 :(得分:1)

您最有可能遇到SNAT耗尽。在“诊断和解决问题”下,刀片服务器搜索“ TCP连接”,它将显示您的应用程序正在建立多少个TCP连接。如果与SQL的连接数量很多(〜128个以上),则说明您的应用程序处于可能会发生超时异常的状态。

enter image description here

应用服务针对多租户应用服务运行在201-400范围内,因此一旦您的应用与特定目标IP /端口建立128个单独的TCP连接,您很可能会看到这些问题。 https://docs.microsoft.com/en-us/azure/load-balancer/load-balancer-outbound-connections

我的建议将按以下顺序进行:

  1. 确保使用连接池限制单个tcp连接数。我曾与拥有1000个tcp连接的客户一起工作,并且在对所有连接使用连接池之后,该连接下降到了sub100。该计划的规模对于此特定问题没有影响。 https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-connection-pooling

  2. 使用区域VNET集成-SNAT端口在VNET集成中不起作用。然后,您可以利用服务终结点将流量路由到Azure SQL。 https://docs.microsoft.com/en-us/azure/app-service/web-sites-integrate-with-vnet#regional-vnet-integration

  3. 将应用程序扩展到多个实例-这有助于在多个VM之间分散请求和出站SQL连接

  4. 使用ASE-这是一个昂贵得多的选择,但只想 添加它是为了回答完整性。 SNAT端口取决于 在上面的文档中看到的实例数

答案 1 :(得分:0)

这是一个非常常见且常见的问题,由于网络不稳定而发生。解决的方法是使用重试模式包装代码块。

https://docs.microsoft.com/en-us/azure/architecture/patterns/retry

答案 2 :(得分:0)

在SQL Server防火墙中,您可以配置/允许App Service的出站IP地址。您可以从App Service的“属性”部分或使用CLI获取这些IP。
Inbound and outbound IP addresses in Azure App Service