简体   繁体   English

Azure AppService 无法连接到 SQL 数据库

[英]Azure AppService unable to connect to SQL database

I have an Azure AppService written in C# that connects to a SQL Server database hosted outside of Azure using NHibernate . I have an Azure AppService written in C# that connects to a SQL Server database hosted outside of Azure using NHibernate . The connection string looks like this:连接字符串如下所示:

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

Most of the time everything works fine, but occasionally my AppService loses the connection, and I am getting the following exception:大多数情况下一切正常,但有时我的 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

This starts happening out of nowhere: I am not updating any connection string, not restarting my AppService, etc. The application only fails to connect to the database from Azure.这开始突然发生:我没有更新任何连接字符串,没有重新启动我的 AppService 等。应用程序只是无法从 Azure 连接到数据库。 If I launch the application locally, everything works as expected using the same connection string.如果我在本地启动应用程序,一切都会使用相同的连接字符串按预期工作。 Additionally, I can connect to the DB fine from SSMS.此外,我可以从 SSMS 连接到数据库。

Sometimes restarting my AppService helps, and the connectivity is restored after a restart.有时重新启动我的 AppService 会有所帮助,并且连接会在重新启动后恢复。 But sometimes it doesn't help.但有时也无济于事。

I am suspecting the connection may be blocked by Azure's firewall, but I don't know how to check this.我怀疑连接可能被 Azure 的防火墙阻止,但我不知道如何检查。 My application is using a B1 App Service plan, and I haven't created any custom firewalls, or load balancers in my Azure Portal.我的应用程序正在使用 B1 应用服务计划,并且我没有在我的 Azure 门户中创建任何自定义防火墙或负载平衡器。 In fact, this AppService is the only resource that I currently have.事实上,这个 AppService 是我目前拥有的唯一资源。

Any ideas what might be causing this, and hw to fix it?有什么想法可能导致这种情况,以及如何解决它?

You're most likely hitting SNAT exhaustion.您很可能会遇到 SNAT 耗尽。 Under Diagnose and Solve Problems Blade search for "TCP Connections" which will show you how many TCP connections your application is making.在诊断和解决问题刀片下搜索“TCP 连接”,这将显示您的应用程序正在建立多少 TCP 连接。 If there is a high number of connections to SQL (~128+) you're application is in a state that will most likely run into timeout exceptions.如果与 SQL (~128+) 的连接数量很大,则您的应用程序位于 state 中,很可能会遇到超时异常。

在此处输入图像描述

App Services run in the 201-400 range for the multi-tenant app services so once you application makes 128 individual TCP connections to a specific destination IP/port you'll likely see these issues.应用服务在多租户应用服务的 201-400 范围内运行,因此一旦您的应用程序与特定目标 IP/端口建立 128 个单独的 TCP 连接,您可能会看到这些问题。 https://docs.microsoft.com/en-us/azure/load-balancer/load-balancer-outbound-connections https://docs.microsoft.com/en-us/azure/load-balancer/load-balancer-outbound-connections

My recommendations would be in the following order:我的建议将按以下顺序排列:

  1. Make sure to use connection pooling to limit the individual number of tcp connections.确保使用连接池来限制 tcp 连接的单个数量。 I've worked with customers who had 1000s of tcp connections and after using connection pooling for all their connections it dropped down to sub 100. The plan size does not make a difference for this particular issue.我曾与拥有 1000 个 tcp 连接的客户合作,在对所有连接使用连接池后,它下降到低于 100 个。计划大小对这个特定问题没有影响。 https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-connection-pooling https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-connection-pooling

  2. Use Regional VNET integration - SNAT ports do not come into play with VNET integration.使用区域 VNET 集成 - SNAT 端口不会与 VNET 集成一起使用。 You can then utilize service endpoints to route traffic to Azure SQL.然后,您可以利用服务端点将流量路由到 Azure SQL。 https://docs.microsoft.com/en-us/azure/app-service/web-sites-integrate-with-vnet#regional-vnet-integration https://docs.microsoft.com/en-us/azure/app-service/web-sites-integrate-with-vnet#regional-vnet-integration

  3. Scale out the application to multiple instances - This helps spread the requests and outbound SQL connection across multiple VMs将应用程序扩展到多个实例 - 这有助于将请求和出站 SQL 连接分散到多个 VM

  4. Use an ASE - This is a much more expensive option but just wanted to add it for answer completeness sake.使用 ASE - 这是一个更昂贵的选项,但只是为了回答完整性而想添加它。 The SNAT ports depend on the number of instances you have as seen in the doc above SNAT 端口取决于您在上面的文档中看到的实例数量

This is a very common and frequent problem and it happens due network instability.这是一个非常常见和频繁的问题,它是由于网络不稳定而发生的。 The way to solve is just wrap the code block using a retry pattern.解决的方法就是使用重试模式包装代码块。

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

In your SQL Server firewall you can configure/allow the outbound IP addresses for your App Service.在您的 SQL 服务器防火墙中,您可以为您的应用服务配置/允许出站 IP 地址。 You can get these IPs from property section of your App Service or by using CLI.您可以从应用服务的属性部分或使用 CLI 获取这些 IP。
Inbound and outbound IP addresses in Azure App Service Azure 应用服务中的入站和出站 IP 地址

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM