您的位置:首頁>科技>正文

雲計算之路-阿裡雲上:資料庫連接數過萬的真相,從阿裡雲RDS到微軟.NET Core

在昨天的博文中, 我們堅持認為資料庫連接數過萬是阿裡雲RDS的問題, 但後來阿裡雲提供了當時的資料庫連接情況, 讓我們動搖了自己的想法。

帳戶連接數A4077B3995C741D698E519

上面這5個帳戶產生了10030個資料庫連接, 當看前4個帳戶(產生了9511個連接)的名稱時, 我們打了一個寒顫 —— 這些都是運行 Linux 上的 ASP.NET Core 網站。 。 。 這不是巧合, 其中必有蹊蹺。

隨後, 我們觀察了主備庫切換後的 RDS 中資料庫連接情況。 有一個運行在 Linux 上的 ASP.NET Core 網站, 用了3台伺服器, 卻產生了1528個資料庫連接。

SELECT * FROM sys.sysprocesses WHERE loginame='xxx'

重啟其中1台伺服器上的網站, 連接數立馬從1528降到了391。 什麼情況?資料庫連接池發飆了?

繼續觀察, 當前資料庫中大量的連接都是由運行在 Linux 上的 ASP.NET Core 網站產生的, 而且會隨著時間的推移保持增長。

資料庫連接洩漏了, 這還是第1次遇到!可我們在 APS.NET Core 應用中所有的資料庫操作都用的是Entity Framework Core, 不存在沒有及時關閉資料庫連接的情況, 唯一可以懷疑的物件是在 System.Data.SqlClient 中實現的 ADO.NET 資料庫連接池。

資料庫連接池究竟出什麼狀況了?我們在資料庫連接字串中沒有另外設置連接池, 用的是默認設置(Min_Pool_Size = 0; 與 Max_Pool_Size = 100;)。 而且更奇怪的是 Max_Pool_Size 的限制沒起作用, 不然只會報下面的錯誤, 不會連接數一直增長。

Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.

我們想來想去, 唯一能想得通的解釋是 .NET Core 的資料庫連接池發生了這樣的狀況 —— 連接池中已經創建的連接無法被重用, 不僅如此, 而且它們直接被 SqlClient 給無視了,

都沒有被計算在 Pool Size 中, 所以根本觸發不了 Max_Pool_Size 的限制, 造成連接無限制, 任由 SqlClient 建。 更要命的是, 這些被無視的連接卻一直在保持著與資料庫的連接。 於是, 連接洩露成了命中註定。

在有了這個唯一想得通的猜測後, 我們今天開始在測試環境中進行驗證。

部署一個 ASP.NET Core 網站, 創建一個專用資料庫連接帳戶, 然後用下面的 SQL 語句查看資料庫連接是否被重用, 同時在測試伺服器用 tcpdump 進行抓包, 並且分別用阿裡雲 RDS 與我們自己搭建的 SQL Server 伺服器進行測試。

SELECT * from sys.sysprocesses where loginame='測試專用帳戶'

如果連接池正常工作, 第1次訪問, 新建所需的資料庫連接;第2次訪問同樣的頁面, 應該重用已有的資料庫連接, 不會創建新的資料庫連接。

開始測試時, 不管連接阿裡雲 RDS 還是我們自己的 SQL Server,

連接池都工作正常, 連接能被重用。

後來分析了一下, 雖然生產環境中連接數一直在增長, 但增長速度不是很快, 可能問題的發生需要一定的時間間隔, 或許連接閒置超過一定時間之後才不會被重用。

於是, 我們間隔了10分鐘左右進行訪問測試, 問題重現了!比如其中的一次測試, 同一個頁面第1次訪問, 產生了5個連接;過10分鐘左右再訪問, 會新建3個連接變成8個連接;再過10分鐘左右訪問, 連接增長到11個。 這種連接不能被重用的情況通過 tcp 抓包也可以看出來。 如果在很短的時間內訪問, 連接數保持不變(連接被重用)。

這個問題不僅在阿裡雲 RDS (SQL Server 2008 R2)可以重現, 而且在我們自己搭建的 SQL Server 2014 也能重現, 問題的真相隨之水落石出。

資料庫連接數過萬問題不是阿裡雲 RDS 的問題, 而是 .NET Core 中 System.Data.SqlClient的連接池在 Linux 上的實現問題, 我們錯怪了阿裡雲, 輕信了微軟。 這是我們使用阿裡雲以來對阿裡雲最大的一次誤會, 這是我們 .NET Core 遷移過程中遇到的最大的一個坑。

為什麼最近才出現這個問題?是因為我們最近將更多網站遷移到了 ASP.NET Core , 而且將之前一些跑在 Windows 上的 ASP.NET Core 網站切換到了 Linux 。

如何解決這個問題?我們會察看一下 System.Data.SqlClient 的實現代碼, 看能否找到實現層面的線索。 阿裡雲會進一步驗證這個問題, 如果確認是微軟實現上的問題, 會與微軟溝通解決。

【16:55 更新】

我們在 Windows 上進行對比測試發現, 在 Windows 上連接池中閒置的資料庫連接過段時間會被自動關閉, 與上面 Linux 同樣的測試場景, 間隔10分鐘後查看,

資料庫連接全消失了。

【18:18 更新】

感謝 @feiyun0112在評論中提供的線索, 2016年11月7日就有人發現了這個問題, 並且在 github 上提交了issue。

【18:41 更新】

我們在應用中使用的 System.Data.SqlClient.dll 版本是 4.3.0, 是在2016年11月5日生成的, 正好在這個 issue之前。

【20:56 更新-成功解決】

通過手動替換 System.Data.SqlClient.dll 文件解決了這個問題。 操作步驟如下:

2)在 corefx-1.1.0 檔中運行 init-tools.cmd 命令安裝 build 工具

3)用 VS2017 打開 corefx-1.1.0\src\System.Data.SqlClient 中的 System.Data.SqlClient.sln 解決方案

4)打開 SNITcpHandle.cs , 去掉 private readonly NetworkStream _tcpStream; 中的 readonly , 在 Dispose 方法中添加如下代碼:

if (_tcpStream != null) { _tcpStream.Dispose; _tcpStream = null; }

5)用 VS2017 以 Release 方式 build System.Data.SqlClient 項目。

6)將 corefx-1.1.0\bin\Unix.AnyCPU.Release\System.Data.SqlClient 資料夾中生成的 System.Data.SqlClient.dll 檔, 在 git bash 中通過 scp 命令上傳到 Linux 伺服器上的 nuget 資料夾。

MINGW64 /c/Dev/GitHub/corefx-1.1.0/bin/Unix.AnyCPU.Release/System.Data.SqlClient $ scp System.Data.SqlClient.dll root@ubuntu-server:~/.nuget/packages/system.data.sqlclient/4.3.0/runtimes/unix/lib/netstandard1.3 System.Data.SqlClient.dll 100% 708KB 176.9KB/s 00:04

7)登錄 Linux 伺服器重啟 ASP.NET Core 網站

8)第一次訪問, 在資料庫中看到了這些新建的連接, 然後停止訪問。 。 。 等了5-6分鐘, 這些連接全部消失, 和在 Windows 上的表現一致, 連接洩露的問題搞定!

連接洩露引起的資料庫連接數過萬的問題, 僅僅是因為少寫了1行 Dispose 代碼。

附:我們 build 出來的修復這個問題的 System.Data.SqlClient.dll

【23:15 更新】

更新 System.Data.SqlClient.dll 之後,效果是立竿見影!

僅僅是因為少寫了1行 Dispose 代碼。

附:我們 build 出來的修復這個問題的 System.Data.SqlClient.dll

【23:15 更新】

更新 System.Data.SqlClient.dll 之後,效果是立竿見影!

Next Article
喜欢就按个赞吧!!!
点击关闭提示