在 Azure 上執行的 ASP.NET 應用程序未刪除 Redis 客戶端
我們有一個在 Azure 應用服務上執行的 .NET 4.6.1 ASP.NET Web Forms 應用。我說的是 Web 表單,但該應用程序還內置了 Web Api 2,以及兩個 WebJob 項目,每個項目執行多個任務。
我們使用 StackExchange.Redis 進行記憶體。我們還將 Redis 用於 SessionState。我提到這一點,因為問題始於 Redis 連接的建立,除非我們重新啟動 ASP.NET 應用程序,否則這些連接不會消失。我們使用 Lazy 模式重新共享 ConnectionMultiplexer。本地測試證實該連接確實在各個請求中共享。
對我們的 Redis 配置相當有信心,我們開始研究與 ASP.NET 應用程序相關的指標。執行緒數反映了我們的 Redis 客戶端圖。問題是,線上程數方面,我不知道什麼是正常的。但我預計他們會在幾天內處理/消失/死亡。不積累。
我們廣泛使用 Async/Await,但我們通常不直接處理執行緒。最後一行可能聽起來有點幼稚,抱歉不知道該怎麼說。我們往往會在工作日看到我們最忙碌的時間,晚上的活動很少。
我們不知所措,我們做錯了什麼?我是對的,我們是否應該看到執行緒數隨著網站上的活動減少而下降?也許我已經說過一些話,你可以指著說“你到底在做什麼?”,那就太好了。不過,我真正追求的是對我們可能對這種執行緒的建立負責的建議以及我們如何管理它的建議。
更新 03/08
第二張圖中的線圖,正在由 SUM 聚合。如果您將聚合更改為 MIN、MAX 或 AVG,您會看到一個更合理的執行緒計數,表明執行緒被正確處理。這顯然是 Web 應用程序的一大解脫。
我假設,雖然我的Google搜尋沒有設法確認,但 SUM 表示自上次應用程序重啟以來創建的執行緒總數。如果我的假設是正確的,那麼 Web 應用程序執行緒計數的 SUM 與 AVG Redis 客戶端計數相匹配的事實證實了我們最初的懷疑,即我們的連接多路復用器不僅不共享連接,而且當操作完成。
這是我們的 Redis 類,它看起來與網際網路上的其他 1000 個範例相同,至少據我所知:
Imports System.Configuration Imports StackExchange.Redis Public Class RedisCache Private Shared ReadOnly Property LazyConnection As New Lazy(Of ConnectionMultiplexer)(Function() Dim cacheConnection = ConfigurationManager.AppSettings("CacheConnection") Dim multiplexer = ConnectionMultiplexer.Connect(cacheConnection.ToString()) multiplexer.PreserveAsyncOrder = False Return multiplexer End Function) Public Shared ReadOnly Property Connection As ConnectionMultiplexer Get Return LazyConnection.Value End Get End Property Public Shared ReadOnly Property UseCache As Boolean Get Return ConfigurationManager.AppSettings("CacheConnection") IsNot Nothing End Get End Property End Class正如我已經提到的,我們還將 Redis 用於會話狀態,為此我們有一個非常基本的配置:
<sessionState mode="Custom" customProvider="RedisSessionProvider" <providers> <add name="RedisSessionProvider" type="Microsoft.Web.Redis.RedisSessionStateProvider" port="6380" host="***" accessKey="***" applicationName="***" retryTimeoutInMilliseconds="5000" ssl="true"/> </providers> </sessionState>有誰知道是什麼延長了這些客戶的壽命?
更新 03/08 第二部分
我一直在考慮我們如何可能縮小問題的範圍。最簡單的起點是“是整個記憶體、會話還是 Redis?” 為此,我們將啟動一個額外的 Redis 伺服器,在下一個版本中將有 Cache 或 Session State 指向它。希望其中一台伺服器表現出與我們試圖修復的相同的行為,而另一台的行為,嗯,更好。兩者中的前者將是我們集中精力的地方。
在我們的測試環境中,我做的另一件小事是創建一個測試屬性,它生成一個新的 GUID,與該
Connection屬性的工作方式相同:Private Shared ReadOnly Property LazyGuid As New Lazy(Of Guid)(Function() Guid.NewGuid()) Public Shared ReadOnly Property Guid As Guid Get Return LazyGuid.Value End Get End Property然後,我創建了一些從 Web 應用程序的各個部分到新 GUID 屬性以及現有 Redis 程式碼的呼叫:
<Route("guid"), HttpGet> Public Async Function GetGuid() As Task(Of Tuple(Of String, String)) Return New Tuple(Of String, String)(RedisCache.Guid.ToString(), If(RedisCache.UseCache, RedisCache.Connection.ClientName, Nothing)) End Function <Route("guid"), HttpPost> Public Async Function PostForGuid() As Task(Of Tuple(Of String, String)) Return New Tuple(Of String, String)(RedisCache.Guid.ToString(), If(RedisCache.UseCache, RedisCache.Connection.ClientName, Nothing)) End Function <Route("guid/sync"), HttpGet> Public Function GetSyncGuid() As Tuple(Of String, String) Return New Tuple(Of String, String)(RedisCache.Guid.ToString(), If(RedisCache.UseCache, RedisCache.Connection.ClientName, Nothing)) End Function上面的範例,以及嵌入在 ASPX 頁面中的一些呼叫,跨越多個會話(和時區),產生了相同的結果。所以目前我強烈懷疑問題出在
RedisSessionStateProvider, 或者至少是我們對它的使用上。2008 年 13 月更新
首先,我已經在測試伺服器上執行了兩個 Redis 實例大約一個星期。一個接受 Session 請求,另一個接受來自上述 RedisCache 類的請求。兩者都保持穩定的 15-20 連接。
不過,這次更新的重點。我有在生產 Redis 上執行*CLIENT LIST的想法。*在客戶端數量約為 1.3k 的情況下,我設法獲取了 850 個客戶端行的樣本。
在整個範例中,有一個客戶端的最後一個命令是GET,其餘的是UNSUBSCRIBE或INFO。年齡範圍從 5000 秒到 65,000 秒。空閒時間範圍為 0 到 60 秒。我了解UNSUBSCRIBE命令與 StackExchange.Redis 對 pub/sub 功能的處理有關,據我所知,我沒有使用該功能。
為什麼這些客戶保持活躍並成倍增加?
id=1367825 addr=*** fd=45 name=*** age=465516 idle=56 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=info numops=7680 id=1319911 addr=*** fd=611 name=*** age=489772 idle=48 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=info numops=8082 id=1409149 addr=*** fd=477 name=*** age=444591 idle=34 flags=N db=0 sub=1 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=unsubscribe numops=7328 id=1319912 addr=*** fd=508 name=*** age=489772 idle=38 flags=N db=0 sub=1 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=unsubscribe numops=8072 id=2169495 addr=*** fd=954 name=*** age=59035 idle=56 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=info numops=984 id=2169496 addr=*** fd=955 name=*** age=59035 idle=56 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=info numops=984 id=1219863 addr=*** fd=557 name=*** age=540498 idle=38 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=info numops=8917 id=1032642 addr=*** fd=594 name=*** age=635373 idle=56 flags=N db=0 sub=1 psub=0 multi=-1 qbuf=0 qbuf-free=0 obl=0 oll=0 omem=0 ow=0 owmem=0 events=r cmd=unsubscribe numops=104742008 年 14 月更新
一個潛在的積極發展。我在 Github 上註冊了一張票。聽起來我們的症狀問題已在 2.0 中得到修復。永不消亡的客戶
我可以告訴你,這幾個月太瘋狂了。StackExchange 團隊在28 天前發布了 2.0.495 。這太棒了,但我們對 RedisStateProvider 的依賴又依賴於 StackExchange.Redis 的 StrongName 變體。StrongName 變體在 2.0 中已停止使用,因此我向 Azure 團隊記錄了一張票。你相信他們在 15 天前發布了自己的版本嗎?驚人的驚人。
好消息/壞消息情況。更新成功了,耶!它工作得有點太好了,噓。我們之前在 1.1k 多個連接中大量使用的通信突然被壓縮到 40 個左右的連接中。超時大量。不過,錯誤消息帶有非常有用的連結。建議是 ConnectionMultiplexers 池可能是答案。使用基於此SO的程式碼,我們實現了我們自己的池,可以使用應用程序設置放大或縮小。該池仍處於測試階段,因此現在說它是否是我們問題的答案還為時過早,但結果看起來很有希望。


