[C#] 透過Azure Blob 租用解決Azure App Service Scale Out 多 Instances 使用同一個 Lock

2024-03-05

上一篇文章,我們提到了Azure App Service scale out 之後如何取得 instance id ,主要就是我要測試

如果現在他分裂成一台以上,我又要對檔案進行 IO 會產生什麼事情,你會問我為何要這樣操作,因為有可能你會用 

SQLite 或是 LiteDB 等..單機資料庫但是對於檔案寫入會有問題,這時候有什麼方法可解決 ?


我知道你第一個想到的就是百搭使用的 Redis ,但是 Redis 不便宜你知道的,這時候網路上看到有一個做法就是使用  Azure Blob 的租用 來做到 

1. 申請 Azure Blob 服務拿到 Connection String

2. 之後建立 Container 還有 一個檔案 這邊範例 Container: lockuse , 檔案名稱: lockfile



3. 之後就是程式碼的部分,主要就是取用租用後才能夠寫檔案,其中我也記錄了寫檔 instance 的 Id 

[BindProperty] public string Result { get; set; } public async Task<IActionResult> OnGet() { var data = Request.Query["data"]; var index = Request.Query["index"]; if (!string.IsNullOrEmpty(data) && !string.IsNullOrEmpty(index)) { //get app service instance id for test. var instanceId = Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID"); string connectionString = "{connection_string}"; //blob data string containerName = "lockuse"; string blobName = "lockfile"; var blobServiceClient = new BlobServiceClient(connectionString); var blobContainerClient = blobServiceClient.GetBlobContainerClient(containerName); var blobClient = blobContainerClient.GetBlobClient(blobName); // 取得租用 bool acquiredLease = false; var leaseClient = blobClient.GetBlobLeaseClient(); Azure.Storage.Blobs.Models.BlobLease leaseId = null; //暴力取得&#65292;直到取道為止 while (!acquiredLease) { try { //至少要15秒 //這邊我被搞到 //reference: //https://learn.microsoft.com/en-us/dotnet/api/azure.storage.blobs.specialized.blobleaseclient.acquireasync?view=azure-dotnet leaseId = await leaseClient.AcquireAsync(TimeSpan.FromMilliseconds(15000)); acquiredLease = true; } catch (RequestFailedException ex) { // 等待一段時間後重試 await Task.Delay(TimeSpan.FromSeconds(2)); } } try { try { //如果不存在就建立 var str = instanceId + ":" + index + "-" + data + "," + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\r\n"; if (!System.IO.File.Exists(AppDomain.CurrentDomain.BaseDirectory + "text.txt")) { System.IO.File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + "text.txt", str); } else { var oldData = System.IO.File.ReadAllText(AppDomain.CurrentDomain.BaseDirectory + "text.txt"); oldData += str; System.IO.File.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + "text.txt", oldData); } Result = instanceId + "-" + index; return Page(); } catch (Exception ex) { Result = ex.Message; return Page(); } } finally { //最後都一定要釋放 await leaseClient.ReleaseAsync(); } } return Page(); }


4. 測試程式 

Parallel.For(1, 101, i => { using (HttpClient client = new HttpClient()) { string url = "https://samplesite.azurewebsites.net/adddata?data=" + "TESTDATA_" + i + "&index=" + i; HttpResponseMessage response = client.GetAsync(url).Result; // 確保請求成功 response.EnsureSuccessStatusCode(); // 取得回應內容 var responseBody = response.Content.ReadAsStringAsync().Result; Console.WriteLine(i+":"+responseBody); } });


結果,比對一下,不會因為多 instance 的原因導致檔案被覆蓋,或是檔案被鎖定,資料數量也是正確


筆記一下,如果你有需要用到  instance lock 的平價解決方案。

 



當麻許的碎念筆記 2014 | Donma Hsu Design.