.net下 本地锁、redis分布式锁、zk分布式锁的实现

为何要用锁?

  大型站点在高并发的状况下,为了保持数据最终一致性就须要用到技术方案来支持。好比:分布式锁、分布式事务。有时候咱们在为了保证某一个方法每次只能被一个调用者使用的时候,这时候咱们也能够锁来实现。git

基于本地缓存实现锁

  为何还要写基于本地缓存实现的锁呢,由于有些项目项目可能仍是单机部署的,当随着业务量增加的时候就会变成多机部署,从单机到多机的切换过程当中,咱们也须要把原先业务相关的锁改为分布式锁,来保持数据的最终一致性。固然项目是使用ioc的那就更好了,切换注册时的实现类就完成了切换,很是方便。github

实现思路:

  用户须要用一个key和一个惟一的值(知道当前这个key的使用者是谁)来获取一个锁,获取到锁以后,执行完对应的操做而后释放掉。在释放锁的时候 4咱们须要判断下当前这个锁的使用者对应的值与想要释放传递过来的值是否是相等,若是相等则能够释放,不相等则不释放。redis

实现代码:

public sealed class LocalLock : ILock { private static ConcurrentDictionary<string, object> _LockCache = new ConcurrentDictionary<string, object>(); private static ConcurrentDictionary<string, string> _LockUserCache = new ConcurrentDictionary<string, string>(); /// <summary>
        /// 获取一个锁(须要本身释放) /// </summary>
        /// <param name="key">锁的键</param>
        /// <param name="value">当前占用值</param>
        /// <param name="span">耗时时间</param>
        /// <returns>成功返回true</returns>
        public bool LockTake(string key, string value, TimeSpan span) { EnsureUtil.NotNullAndNotEmpty(key, "Lockkey"); EnsureUtil.NotNullAndNotEmpty(value, "Lockvalue"); var obj = _LockCache.GetValue(key, () => { return new object(); }); if (Monitor.TryEnter(obj, span)) { _LockUserCache[key] = value; return true; } return false; } /// <summary>
        /// 异步获取一个锁(须要本身释放) /// </summary>
        /// <param name="key">锁的键</param>
        /// <param name="value">当前占用值</param>
        /// <param name="span">耗时时间</param>
        /// <returns>成功返回true</returns>
        public Task<bool> LockTakeAsync(string key, string value, TimeSpan span) { return Task.FromResult(LockTake(key, value, span)); } /// <summary>
        /// 释放一个锁 /// </summary>
        /// <param name="key">锁的键</param>
        /// <param name="value">当前占用值</param>
        /// <returns>成功返回true</returns>
        public bool LockRelease(string key, string value) { EnsureUtil.NotNullAndNotEmpty(key, "Lockkey"); EnsureUtil.NotNullAndNotEmpty(value, "Lockvalue"); _LockCache.TryGetValue(key, out object obj); if (obj != null) { if (_LockUserCache[key] == value) { Monitor.Exit(obj); return true; } return false; } return true; } /// <summary>
        /// 异步释放一个锁 /// </summary>
        /// <param name="key">锁的键</param>
        /// <param name="value">当前占用值</param>
        /// <returns>成功返回true</returns>
        public Task<bool> LockReleaseAsync(string key, string value) { return Task.FromResult(LockRelease(key, value)); } /// <summary>
        /// 使用锁执行一个方法 /// </summary>
        /// <param name="key">锁的键</param>
        /// <param name="value">当前占用值</param>
        /// <param name="span">耗时时间</param>
        /// <param name="executeAction">要执行的方法</param>
        public void ExecuteWithLock(string key, string value, TimeSpan span, Action executeAction) { if (executeAction == null) return; if (LockTake(key, value, span)) { try { executeAction(); } finally { LockRelease(key, value); } } } /// <summary>
        /// 使用锁执行一个方法 /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="key">锁的键</param>
        /// <param name="value">当前占用值</param>
        /// <param name="span">耗时时间</param>
        /// <param name="executeAction">要执行的方法</param>
        /// <param name="defaultValue">默认返回</param>
        /// <returns></returns>
        public T ExecuteWithLock<T>(string key, string value, TimeSpan span, Func<T> executeAction, T defaultValue = default(T)) { if (executeAction == null) return defaultValue; if (LockTake(key, value, span)) { try { return executeAction(); } finally { LockRelease(key, value); } } return defaultValue; } /// <summary>
        /// 使用锁执行一个异步方法 /// </summary>
        /// <param name="key">锁的键</param>
        /// <param name="value">当前占用值</param>
        /// <param name="span">耗时时间</param>
        /// <param name="executeAction">要执行的方法</param>
        public async Task ExecuteWithLockAsync(string key, string value, TimeSpan span, Func<Task> executeAction) { if (executeAction == null) return; if (await LockTakeAsync(key, value, span)) { try { await executeAction(); } catch { throw; } finally { LockRelease(key, value); } } } /// <summary>
        /// 使用锁执行一个异步方法 /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="key">锁的键</param>
        /// <param name="value">当前占用值</param>
        /// <param name="span">耗时时间</param>
        /// <param name="executeAction">要执行的方法</param>
        /// <param name="defaultValue">默认返回</param>
        /// <returns></returns>
        public async Task<T> ExecuteWithLockAsync<T>(string key, string value, TimeSpan span, Func<Task<T>> executeAction, T defaultValue = default(T)) { if (executeAction == null) return defaultValue; if (await LockTakeAsync(key, value, span)) { try { return await executeAction(); } catch { throw; } finally { LockRelease(key, value); } } return defaultValue; } }
View Code
class Program { static void Main(string[] args) { ILock localLock = new LocalLock(); int excuteCount = 0; Parallel.For(0, 10000, i => { localLock.ExecuteWithLock("test", Guid.NewGuid().ToString(), TimeSpan.FromSeconds(5), () => { Console.WriteLine("获取锁成功"); Interlocked.Increment(ref excuteCount); }); }); Console.WriteLine("成功次数:" + excuteCount.ToString()); Console.WriteLine("执行完成"); Console.ReadLine(); } }
View Code

基于zk实现的分布式锁

实现思路:

在获取锁的时候在固定节点下建立一个自增的临时节点,而后获取节点列表,按照增量排序,假如当前建立的节点是排在第一个的,那就代表这个节点是获得了执行的权限,假如在它前面还有其它节点,那么就对它的上一个节点进行监听,等到上一个节点被删除了,那么该节点就获得了执行的权限了。缓存

因为代码片断太多,待会再github自行查看实现过程。zk获取锁的速度比较慢,致使有几个多是失败的。并发

基于redis的实现

实现思路:

利用redis的setnx(key, value):“set if not exits”,若该key-value不存在,则成功加入缓存而且返回1,不然返回0。在有效时间内若是设置成功则获取执行限权,没有那就获取权限失败。异步

对比下会发现redis的执行效率会比zk的快一点。async

项目下载地址:https://github.com/ProjectSharing/Lock分布式

相关文章
相关标签/搜索