परिचय
<पी> इस लेख में, हम चर्चा करेंगे कि .NET Core में Redis के साथ एक वितरित लॉक कैसे बनाया जाए। <पी> जब हम वितरित सिस्टम बनाते हैं, तो हमें कई प्रक्रियाओं का सामना करना पड़ेगा जो एक साझा संसाधन को एक साथ संभालती हैं। यह इस तथ्य के कारण कुछ अप्रत्याशित समस्याएं पैदा करेगा कि उनमें से केवल एक ही समय में साझा संसाधन का उपयोग कर सकता है! <पी> इस समस्या को हल करने के लिए हम एक वितरित लॉक का उपयोग कर सकते हैं। लॉक क्यों वितरित किया गया?
<पी> हमेशा की तरह, हम इस समस्या से निपटने के लिए लॉक का उपयोग करेंगे। <पी> निम्नलिखित कुछ नमूना कोड दिखाता है जो लॉक के उपयोग को प्रदर्शित करता है।
public void SomeMethod()
{
// Do something...
lock (obj)
{
// Do ....
}
// Do something...
}
<पी> हालाँकि, इस प्रकार का लॉक हमें समस्या को अच्छी तरह से हल करने में मदद नहीं कर सकता है! यह एक इन-प्रोसेस लॉक है जो साझा संसाधनों के साथ केवल एक प्रक्रिया को हल कर सकता है। <पी> यही मुख्य कारण है कि हमें वितरित लॉक की आवश्यकता है! <पी> मैं यहां एक सरल वितरित लॉक बनाने के लिए रेडिस का उपयोग करूंगा। <पी> और मैं इस कार्य को करने के लिए रेडिस का उपयोग क्यों करूं? रेडिस की एकल-थ्रेडेड प्रकृति और परमाणु संचालन करने की इसकी क्षमता के कारण। लॉक कैसे बनाएं?
<पी> मैं आपको दिखाने के लिए एक .NET कोर कंसोल एप्लिकेशन बनाऊंगा। <पी> अगले चरण से पहले, हमें रेडिस सर्वर चलाना चाहिए! <पी> पी> <पी> StackExchange.Redis .NET पर सबसे लोकप्रिय रीड्स क्लाइंट है, और इसमें कोई संदेह नहीं है कि हम इसका उपयोग निम्नलिखित कार्यों के लिए करेंगे। <पी> सबसे पहले रेडिस के साथ संबंध बनाना।
/// <summary>
/// The lazy connection.
/// </summary>
private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
{
ConfigurationOptions configuration = new ConfigurationOptions
{
AbortOnConnectFail = false,
ConnectTimeout = 5000,
};
configuration.EndPoints.Add("localhost", 6379);
return ConnectionMultiplexer.Connect(configuration.ToString());
});
/// <summary>
/// Gets the connection.
/// </summary>
/// <value>The connection.</value>
public static ConnectionMultiplexer Connection => lazyConnection.Value;
<पी> किसी साझा संसाधन पर लॉक का अनुरोध करने के लिए, हम निम्नलिखित कार्य करते हैं।
SET resource_name unique_value NX PX duration
<पी> resource_name एक मान है जिसे आपके एप्लिकेशन के सभी इंस्टेंस साझा करेंगे। <पी> यूनिक_वैल्यू कुछ ऐसा है जो आपके एप्लिकेशन के प्रत्येक उदाहरण के लिए अद्वितीय होना चाहिए। और इस अद्वितीय मान का उद्देश्य लॉक को हटाना (अनलॉक करना) है। <पी> अंत में, हम एक अवधि (मिलीसेकंड में) भी प्रदान करते हैं, जिसके बाद रेडिस द्वारा लॉक स्वचालित रूप से हटा दिया जाएगा। <पी> यहां C# कोड में कार्यान्वयन है।
/// <summary>
/// Acquires the lock.
/// </summary>
/// <returns><c>true</c>, if lock was acquired, <c>false</c> otherwise.</returns>
/// <param name="key">Key.</param>
/// <param name="value">Value.</param>
/// <param name="expiration">Expiration.</param>
static bool AcquireLock(string key, string value, TimeSpan expiration)
{
bool flag = false;
try
{
flag = Connection.GetDatabase().StringSet(key, value, expiration, When.NotExists);
}
catch (Exception ex)
{
Console.WriteLine($"Acquire lock fail...{ex.Message}");
flag = true;
}
return flag;
}
<पी> यहां एक्वायर लॉक का परीक्षण करने के लिए कोड दिया गया है।
static void Main(string[] args)
{
string lockKey = "lock:eat";
TimeSpan expiration = TimeSpan.FromSeconds(5);
// 5 person eat something...
Parallel.For(0, 5, x =>
{
string person = $"person:{x}";
bool isLocked = AcquireLock(lockKey, person, expiration);
if (isLocked)
{
Console.WriteLine($"{person} begin eat food (with lock) at {DateTimeOffset.Now.ToUnixTimeMilliseconds()}.");
}
else
{
Console.WriteLine($"{person} cannot eat food due to not getting the lock.");
}
});
Console.WriteLine("end");
Console.Read();
}
<पी> कोड चलाने के बाद, हमें निम्नलिखित परिणाम मिल सकते हैं। <पी>
<पी> केवल एक व्यक्ति ही ताला प्राप्त कर सकता है! अन्य लोग इंतज़ार कर रहे हैं. <पी> हालाँकि Redis द्वारा लॉक स्वचालित रूप से हटा दिया जाएगा, यह साझा संसाधन का अच्छा उपयोग भी नहीं करता है! <पी> क्योंकि जब कोई प्रक्रिया अपना काम पूरा कर लेती है, तो उसे दूसरों को संसाधन का उपयोग करने देना चाहिए, न कि अंतहीन प्रतीक्षा करनी चाहिए! <पी> इसलिए हमें लॉक को भी रिलीज करने की जरूरत है। लॉक कैसे खोलें?
<पी> लॉक जारी करने के लिए, हमने रेडिस में आइटम को हटा दिया! <पी> जहां तक यह बात है कि ताला बनाने में हम क्या लेते हैं, हमें संसाधन के अद्वितीय मूल्य से मेल खाने की जरूरत है। इससे सही लॉक को रिलीज़ करना सुरक्षित हो जाएगा। <पी> मिलान करते समय, हम लॉक हटा देंगे, जिसका अर्थ है कि अनलॉक सफल रहा। अन्यथा, अनलॉक असफल रहा। <पी> हमें एक समय में गेट और डेल कमांड निष्पादित करने की आवश्यकता है, इसलिए हम ऐसा करने के लिए लुआ स्क्रिप्ट का उपयोग करेंगे!
/// <summary>
/// Releases the lock.
/// </summary>
/// <returns><c>true</c>, if lock was released, <c>false</c> otherwise.</returns>
/// <param name="key">Key.</param>
/// <param name="value">Value.</param>
static bool ReleaseLock(string key, string value)
{
string lua_script = @"
if (redis.call('GET', KEYS[1]) == ARGV[1]) then
redis.call('DEL', KEYS[1])
return true
else
return false
end
";
try
{
var res = Connection.GetDatabase().ScriptEvaluate(lua_script,
new RedisKey[] { key },
new RedisValue[] { value });
return (bool)res;
}
catch (Exception ex)
{
Console.WriteLine($"ReleaseLock lock fail...{ex.Message}");
return false;
}
}
<पी> जब कोई प्रक्रिया समाप्त हो जाए तो हमें इस विधि को कॉल करना चाहिए। <पी> जब किसी प्रक्रिया को लॉक मिल जाता है और किसी कारण से लॉक जारी नहीं होता है, तो अन्य प्रक्रियाएं उसके जारी होने तक इंतजार नहीं कर सकती हैं। इस समय अन्य प्रक्रियाएं भी आगे बढ़नी चाहिए. <पी> इस दृश्य से निपटने के लिए यहां एक नमूना दिया गया है।
Parallel.For(0, 5, x =>
{
string person = $"person:{x}";
var val = 0;
bool isLocked = AcquireLock(lockKey, person, expiration);
while (!isLocked && val <= 5000)
{
val += 250;
System.Threading.Thread.Sleep(250);
isLocked = AcquireLock(lockKey, person, expiration);
}
if (isLocked)
{
Console.WriteLine($"{person} begin eat food (with lock) at {DateTimeOffset.Now.ToUnixTimeMilliseconds()}.");
if (new Random().NextDouble() < 0.6)
{
Console.WriteLine($"{person} release lock {ReleaseLock(lockKey, person)} {DateTimeOffset.Now.ToUnixTimeMilliseconds()}");
}
else
{
Console.WriteLine($"{person} do not release lock ....");
}
}
else
{
Console.WriteLine($"{person} begin eat food (without lock) at {DateTimeOffset.Now.ToUnixTimeMilliseconds()}.");
}
});
<पी> नमूना चलाने के बाद, आपको निम्नलिखित परिणाम मिल सकते हैं। <पी> पी> <पी> जैसा कि आप देख सकते हैं, व्यक्ति 3 और 4 बिना ताले के आगे बढ़ेंगे। <पी> यहां स्रोत कोड है जिसे आप मेरे GitHub पृष्ठ पर पा सकते हैं।
सारांश
<पी> इस आलेख में बताया गया है कि .NET कोर में रेडिस के साथ वितरित ताले कैसे बनाएं। और यह एक बुनियादी संस्करण है, आप अपने व्यवसाय के आधार पर इसमें सुधार कर सकते हैं। <पी> मुझे आशा है कि इससे आपको मदद मिलेगी।