Created
July 7, 2020 15:39
-
-
Save oguzhaneren/179925a9c114ae4482f7c60cdd2e63c7 to your computer and use it in GitHub Desktop.
redis distributed lock .net
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class ServiceCollectionExtensions | |
{ | |
public static IServiceCollection AddRedisDistributedLocker( this IServiceCollection serviceCollection,IDatabase database) | |
{ | |
serviceCollection. AddSingleton<IDistributedLocker>(sp=>new RedisDistributedLocker(database)); | |
return serviceCollection; | |
} | |
public static IServiceCollection AddRedisClientDistributedLocker( this IServiceCollection serviceCollection) | |
{ | |
serviceCollection. AddSingleton<IDistributedLocker>(sp=>new RedisDistributedLocker(sp.GetRequiredService<IRedisCacheClient>().GetDbFromConfiguration().Database)); | |
return serviceCollection; | |
} | |
} | |
public interface IDistributedLocker | |
{ | |
Task<DistributedLock> AcquireLock(string key, TimeSpan? expire = null, CancellationToken token = default); | |
} | |
public class RedisDistributedLocker | |
: IDistributedLocker | |
{ | |
private const string AtomicReleaseScript = @" | |
if redis.call(""get"",KEYS[1]) == ARGV[1] then | |
return redis.call(""del"",KEYS[1]) | |
else | |
return 0 | |
end | |
"; | |
private const CommandFlags Flags = CommandFlags.DemandMaster; | |
private readonly IDatabase _redis; | |
public RedisDistributedLocker(IDatabase redis) | |
{ | |
_redis = redis; | |
} | |
public async Task<DistributedLock> AcquireLock(string key, TimeSpan? expire = null, CancellationToken token = default) | |
{ | |
var uniqueLockKey = Guid.NewGuid().ToString("N"); | |
var isLocked = await _redis.StringSetAsync(key, uniqueLockKey, expire, When.NotExists, Flags); | |
return new DistributedLock(() => Unlock(key, uniqueLockKey), isLocked); | |
} | |
public async Task Unlock(string key, string uniqueLockKey) | |
{ | |
RedisKey[] keys = {key}; | |
RedisValue[] values = {uniqueLockKey}; | |
await _redis.ScriptEvaluateAsync(AtomicReleaseScript, keys, values); | |
} | |
} | |
public class DistributedLock : IAsyncDisposable | |
{ | |
private readonly Func<Task> _unlockFunc; | |
private readonly bool _isLocked; | |
public DistributedLock(Func<Task> unlockFunc, bool isLocked) | |
{ | |
_unlockFunc = unlockFunc; | |
_isLocked = isLocked; | |
} | |
public async ValueTask Unlock() | |
{ | |
if (_isLocked) | |
{ | |
return; | |
} | |
await _unlockFunc(); | |
} | |
public ValueTask DisposeAsync() | |
{ | |
return Unlock(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment