簡體   English   中英

如何在使用負載平衡時鎖定對象

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM