[Yugong Series] September 2023 .NET CORE Tool Case-Distributed Lock of DistributedLock


Preface

Locks are a synchronization mechanism used to provide mutually exclusive access to shared resources in a multi-threaded or multi-process environment to avoid race conditions and data inconsistencies.

A distributed lock is a lock that implements mutually exclusive access in a distributed system, allowing multiple processes or nodes to use the same lock to coordinate their operations. Distributed locks usually use distributed protocols to ensure the consistency and reliability of locks, such as ZooKeeper, Redis, etc. The application scenarios of distributed locks include data updates under high concurrency, distributed task scheduling, distributed transactions, etc.

1. Distributed lock of DistributedLock

1. Concept

DistributedLock is a mechanism to achieve synchronization or collaboration in a distributed system. In a distributed system, multiple processes, threads, or services may access shared resources or perform the same task at the same time. Uncontrolled access to shared resources can lead to data inconsistency or unpredictable execution results. Distributed locks ensure the security and correctness of shared resources by coordinating operations between different nodes.

There are many ways to implement distributed locks, the more common of which are based on mutual exclusion algorithms (such as Zookeeper-based implementation), cache-based (such as Redis implementation), etc. Through these mechanisms, distributed locks can implement locking and unlocking operations and ensure that only one process, thread or service can access shared resources at the same time.

DistributedLock source code URL: https://github.com/madelson/DistributedLock

Insert image description here

2.Basic use

DistributedLock is an open source library for distributed locks. It provides a variety of distributed lock implementations and can use different data storage technologies to support distributed locks. It supports the coexistence of multiple distributed lock instances and can be applied to a variety of scenarios.

use:

2.1 DistributedLock.SqlServer

To use SqlServer as the data storage technology, you need to install the SqlServer database first and create a database named "Locks". Then use the following code to operate distributed locks:

var connectionString = "your connection string";
var lockName = "your lock name";
using(var sqlLock = new SqlDistributedLock(lockName, connectionString))
{
    
    
    try
    {
    
    
        sqlLock.TryAcquire(TimeSpan.FromSeconds(30));
        // do something that needs synchronization
    }
    finally
    {
    
    
        sqlLock.Release();
    }
}

2.2 DistributedLock.Postgres

To use Postgres as a data storage technology, you need to install the Postgres database first and create a database named "distributed_locks". Then use the following code to operate distributed locks:

var connectionString = "your connection string";
var lockName = "your lock name";
using(var postgresLock = new PostgresDistributedLock(lockName, connectionString))
{
    
    
    try
    {
    
    
        postgresLock.TryAcquire(TimeSpan.FromSeconds(30));
        // do something that needs synchronization
    }
    finally
    {
    
    
        postgresLock.Release();
    }
}

2.3 DistributedLock.MySql

To use MySql as a data storage technology, you need to install the MySql database first and create a database named "Locks". Then use the following code to operate distributed locks:

var connectionString = "your connection string";
var lockName = "your lock name";
using(var mysqlLock = new MySqlDistributedLock(lockName, connectionString))
{
    
    
    try
    {
    
    
        mysqlLock.TryAcquire(TimeSpan.FromSeconds(30));
        // do something that needs synchronization
    }
    finally
    {
    
    
        mysqlLock.Release();
    }
}

2.4 DistributedLock.Oracle

To use Oracle as the data storage technology, you need to install the Oracle database first and create a database named "Locks". Then use the following code to operate distributed locks:

var connectionString = "your connection string";
var lockName = "your lock name";
using(var oracleLock = new OracleDistributedLock(lockName, connectionString))
{
    
    
    try
    {
    
    
        oracleLock.TryAcquire(TimeSpan.FromSeconds(30));
        // do something that needs synchronization
    }
    finally
    {
    
    
        oracleLock.Release();
    }
}

2.5 DistributedLock.Redis

To use Redis as a data storage technology, you need to install the Redis database first. Then use the following code to operate distributed locks:

var connectionString = await ConnectionMultiplexer.ConnectAsync(connectionString); 
//  StackExchange.Redis

var lockName = "your lock name";
using(var redisLock = new RedisDistributedLock(lockName, connectionString))
{
    
    
    try
    {
    
    
        redisLock.TryAcquire(TimeSpan.FromSeconds(30));
        // do something that needs synchronization
    }
    finally
    {
    
    
        redisLock.Release();
    }
}

2.6 DistributedLock.Azure

To use Azure as a data storage technology, you need to create Azure Storage first. Then use the following code to operate distributed locks:

var connectionString = "your connection string";
var lockName = "your lock name";
using(var azureLock = new AzureDistributedLock(lockName, connectionString))
{
    
    
    try
    {
    
    
        azureLock.TryAcquire(TimeSpan.FromSeconds(30));
        // do something that needs synchronization
    }
    finally
    {
    
    
        azureLock.Release();
    }
}

2.7 DistributedLock.ZooKeeper

To use ZooKeeper as a data storage technology, you need to install ZooKeeper first. Then use the following code to operate distributed locks:

var connectionString = "your connection string";
var lockName = "your lock name";
using(var zooKeeperLock = new ZooKeeperDistributedLock(lockName, connectionString))
{
    
    
    try
    {
    
    
        zooKeeperLock.TryAcquire(TimeSpan.FromSeconds(30));
        // do something that needs synchronization
    }
    finally
    {
    
    
        zooKeeperLock.Release();
    }
}

2.8 DistributedLock.FileSystem

When using a file system as a data storage technology, you need to specify a folder as the lock storage directory. Then use the following code to operate distributed locks:

var directoryPath = "your directory path";
var lockName = "your lock name";
using(var fileLock = new FileSystemDistributedLock(lockName, directoryPath))
{
    
    
    try
    {
    
    
        fileLock.TryAcquire(TimeSpan.FromSeconds(30));
        // do something that needs synchronization
    }
    finally
    {
    
    
        fileLock.Release();
    }
}

2.9 DistributedLock.WaitHandles

To use WaitHandles as the data storage technology, you need to specify a WaitHandle named "Global\" + lockName. Then use the following code to operate distributed locks:

var lockName = "your lock name";
using(var waitHandleLock = new WaitHandleDistributedLock(lockName))
{
    
    
    try
    {
    
    
        waitHandleLock.TryAcquire(TimeSpan.FromSeconds(30));
        // do something that needs synchronization
    }
    finally
    {
    
    
        waitHandleLock.Release();
    }
}

3. Read-write lock

Read-write lock is a multi-thread concurrency control mechanism that can improve concurrent access efficiency when there are more reads and less writes. Read operations and write operations are mutually exclusive, that is, while one thread is writing, other threads cannot read or write, but multiple threads can read data at the same time. Read-write locks are divided into read locks and write locks. Read locks and write locks can mutually occupy access rights to shared resources. If a thread occupies a write lock, other threads cannot access it, whether reading or writing, until the write lock is released. If one thread occupies the read lock, other threads can continue to occupy the read lock, but the write lock will be blocked until all read locks are released. The reason why read-write locks can improve efficiency is that when multiple threads need to read data at the same time, the read lock can be occupied by multiple threads at the same time, while the write lock can only be occupied by one thread, thus avoiding read and write conflicts. performance bottleneck.

class DistributedCache
{
    
    
    // 使用 Sql Server 实现
    private readonly SqlDistributedReaderWriterLock _cacheLock = 
        new SqlDistributedReaderWriterLock("DistributedCache", connectionString);
        
    // 如果缓存中存在 key, 则返回,否则,生成新的 key,写入后并返回
    public async Task<object> GetOrCreateAsync(string key, Func<string, object> valueFactory)
    {
    
    
        // 首先,用 Read 锁尝试获取缓存数据
        await using (await this._cacheLock.AcquireReadLockAsync())
        {
    
    
            var cached = await this.GetValueOrDefaultNoLockAsync(key);
            if (cached != null) {
    
     return cached; }  
        }
        
        // 缓存不存在,用 Write 锁, 写入数据并返回
        await using (await this._cacheLock.AcquireWriteLockAsync())
        {
    
    
            // double-check: 检查是否已经被其他的进程写入数据
            var cached = await this.GetValueOrDefaultNoLockAsync(key);
            if (cached != null) {
    
     return cached; }  
            
            // 写入数据并返回
            var generated = valueFactory(key);
            await this.SetValueAsync(key, generated);
            return generated;
        }
    }
    
    private async Task<object?> GetValueOrDefaultNoLockAsync(string key) {
    
     /* 读取数据 */ }
    
    private async Task SetValueAsync(string key, object value) {
    
     /* 写入数据 */ }
}

Guess you like

Origin blog.csdn.net/aa2528877987/article/details/132877023