在分布式缓存的应用中,会遇到多个客户端同时争用的问题。这个时候,需要用到分布式锁,得到锁的客户端才有操作权限
下面通过一个简单例子介绍:
这里引用的是ServiceStack.Redis
using ServiceStack.Redis; namespace Redis.AcquireLock { public class RedisHelper { //实例化Client public RedisClient client = new RedisClient("192.168.100.152", 6901); /// <summary> /// 根据key存储T对象 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public bool Set<T>(string key, T value) { var result = client.Set<T>(key, value); return result; } /// <summary> /// 根据key存储T对象,并且设置过期时间 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <param name="timeOut"></param> /// <returns></returns> public bool Set<T>(string key, T value, TimeSpan timeOut) { var result = client.Set<T>(key, value, timeOut); return result; } /// <summary> /// 根据key存储T对象,并且设置过期时间 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <param name="value"></param> /// <param name="timeOut"></param> /// <returns></returns> public bool Set<T>(string key, T value, DateTime timeOut) { var result = client.Set<T>(key, value, timeOut); return result; } /// <summary> /// 根据key设置过期时间 /// </summary> /// <param name="key"></param> /// <param name="timeOut"></param> /// <returns></returns> public bool ExpireTime(string key, TimeSpan timeOut) { var result = client.ExpireEntryIn(key, timeOut); return result; } /// <summary> /// 根据key设置过期时间 /// </summary> /// <param name="key"></param> /// <param name="timeOut"></param> /// <returns></returns> public bool ExpireTime(string key, DateTime timeOut) { var result = client.ExpireEntryAt(key, timeOut); return result; } /// <summary> /// 根据key获取对应的对象T /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> public T Get<T>(string key) { var result = client.Get<T>(key); return result; } /// <summary> /// 移除/删除对应key /// </summary> /// <param name="key"></param> /// <returns></returns> public bool Remove(string key) { return client.Remove(key); } /// <summary> /// 删除对应key(返回被删除 key 的数量) /// </summary> /// <param name="key"></param> /// <returns></returns> public long Delete(string key) { return client.Del(key); } /// <summary> /// 判断key是否存在,存在返回1,不存在返回0 /// </summary> /// <param name="key"></param> /// <returns></returns> public long Exists(string key) { return client.Exists(key); } /// <summary> /// 判断key是否存在,存在返回true,不存在返回false /// </summary> /// <param name="key"></param> /// <returns></returns> public bool ContainsKey(string key) { return client.ContainsKey(key); } /// <summary> /// Redis分布式锁 /// </summary> /// <param name="key"></param> /// <returns></returns> public IDisposable AcquireLock(string key) { return client.AcquireLock(key); } /// <summary> /// Redis分布式锁,并且设置过期时间 /// </summary> /// <param name="key"></param> /// <param name="timeOut"></param> /// <returns></returns> public IDisposable AcquireLock(string key, TimeSpan timeOut) { return client.AcquireLock(key, timeOut); } } }
控制台程序:
namespace Redis.AcquireLock { /// <summary> /// Redis分布式锁简单实例 /// </summary> public class Program { public static void Main(string[] args) { RedisHelper redis = new RedisHelper(); Console.WriteLine("线程开始前,输出" + redis.Get<int>("redisKey")); Console.WriteLine("线程开始前,输出" + redis.Remove("redisKey")); Console.WriteLine("线程开始前,输出" + redis.Delete("redisKey")); redis.Set<int>("redisKey", 0); //定义两个线程 Thread myThread1 = new Thread(new ParameterizedThreadStart(AddVal)); Thread myThread2 = new Thread(AddVal); myThread1.Start("1"); myThread2.Start(); Console.WriteLine("等待两个线程结束"); Console.ReadKey(); } public static void AddVal(object num) { RedisHelper redis = new RedisHelper(); for (int i = 0; i < 500; i++) { //int result = redis.Get<int>("redisKey"); //redis.Set<int>("redisKey", result + 1); //返回的是IDisposable,证明我们需要手动释放资源,所以这里使用了using完事后自动释放资源 //如果1秒不释放锁 自动释放,避免死锁 using (redis.AcquireLock("redis_lock", TimeSpan.FromSeconds(1))) { int result = redis.Get<int>("redisKey"); redis.Set<int>("redisKey", result + 1); } } Console.WriteLine("线程" + num + "结束,输出" + redis.Get<int>("redisKey")); //设置缓存过期时间 redis.ExpireTime("redisKey", TimeSpan.FromSeconds(10)); } } }
运行结果效果图说明:
图一是没有加分布式锁的情况下执行结果
图二是加分布式锁的情况下执行结果,两个线程各循环500次,最终缓存值应该为1000才正确