[英]How to lock a object when using load balancing
背景 :我正在編寫一個函數,使用C#將長時間的操作放入隊列中,每個操作分為3個步驟:
1.數據庫操作(更新/刪除/添加數據)
2.使用Web服務進行長時間計算
3.數據庫操作(保存步驟2的計算結果)在步驟1中的同一個db表上,並檢查db表的一致性,例如,步驟1中的項目是相同的(請參閱下面的更詳細的示例)
為了避免臟數據或損壞,我使用鎖對象(靜態單例對象)來確保作為整個事務完成3個步驟。 因為當多個用戶正在調用該函數來執行操作時,他們可以在他們自己的操作期間的不同步驟修改相同的db表而沒有這個鎖定,例如,user2正在他的step1中刪除項目A,而user1正在檢查A是否仍然存在於他的第3步。(附加信息:同時我正在使用Entity框架中的TransactionScope來確保每個數據庫操作都是一個事務,但重復可讀。)
但是,我需要把它放到一個使用負載均衡機制的雲計算平台上,所以實際上我的鎖對象不會生效,因為該功能將部署在不同的服務器上。
問題:如何使我的鎖定對象在上述情況下工作?
這是一個棘手的問題 - 您需要分布式鎖或某種共享狀態。
由於您已經擁有數據庫,因此可以從“靜態C#鎖”更改您的實現,而是在整個“事務”中為您管理鎖定。
您沒有說明您使用的數據庫,但如果它是SQL Server,那么您可以使用應用程序鎖來實現此目的。 這使您可以顯式“鎖定”對象,所有其他客戶端將等待該對象解鎖。 查看:
http://technet.microsoft.com/en-us/library/ms189823.aspx
我在下面編寫了一個示例實現。 啟動兩個實例來測試它。
using System;
using System.Data;
using System.Data.SqlClient;
using System.Transactions;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var locker = new SqlApplicationLock("MyAceApplication",
"Server=xxx;Database=scratch;User Id=xx;Password=xxx;");
Console.WriteLine("Aquiring the lock");
using (locker.TakeLock(TimeSpan.FromMinutes(2)))
{
Console.WriteLine("Lock Aquired, doing work which no one else can do. Press any key to release the lock.");
Console.ReadKey();
}
Console.WriteLine("Lock Released");
}
class SqlApplicationLock : IDisposable
{
private readonly String _uniqueId;
private readonly SqlConnection _sqlConnection;
private Boolean _isLockTaken = false;
public SqlApplicationLock(
String uniqueId,
String connectionString)
{
_uniqueId = uniqueId;
_sqlConnection = new SqlConnection(connectionString);
_sqlConnection.Open();
}
public IDisposable TakeLock(TimeSpan takeLockTimeout)
{
using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.Suppress))
{
SqlCommand sqlCommand = new SqlCommand("sp_getapplock", _sqlConnection);
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.CommandTimeout = (int)takeLockTimeout.TotalSeconds;
sqlCommand.Parameters.AddWithValue("Resource", _uniqueId);
sqlCommand.Parameters.AddWithValue("LockOwner", "Session");
sqlCommand.Parameters.AddWithValue("LockMode", "Exclusive");
sqlCommand.Parameters.AddWithValue("LockTimeout", (Int32)takeLockTimeout.TotalMilliseconds);
SqlParameter returnValue = sqlCommand.Parameters.Add("ReturnValue", SqlDbType.Int);
returnValue.Direction = ParameterDirection.ReturnValue;
sqlCommand.ExecuteNonQuery();
if ((int)returnValue.Value < 0)
{
throw new Exception(String.Format("sp_getapplock failed with errorCode '{0}'",
returnValue.Value));
}
_isLockTaken = true;
transactionScope.Complete();
}
return this;
}
public void ReleaseLock()
{
using (TransactionScope transactionScope = new TransactionScope(TransactionScopeOption.Suppress))
{
SqlCommand sqlCommand = new SqlCommand("sp_releaseapplock", _sqlConnection);
sqlCommand.CommandType = CommandType.StoredProcedure;
sqlCommand.Parameters.AddWithValue("Resource", _uniqueId);
sqlCommand.Parameters.AddWithValue("LockOwner", "Session");
sqlCommand.ExecuteNonQuery();
_isLockTaken = false;
transactionScope.Complete();
}
}
public void Dispose()
{
if (_isLockTaken)
{
ReleaseLock();
}
_sqlConnection.Close();
}
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.