在分布式缓存的应用中,会遇到多个客户端同时争用的问题。这个时候,需要用到分布式锁,得到锁的客户端才有操作权限
下面通过一个简单例子介绍:
这里引用的是Memcached.ClientLibrary.dll
//引用 using Memcached.ClientLibrary; namespace Memcache.AddLock { public class MemcacheHelper { //实例化Client public MemcachedClient MClient; public MemcacheHelper() { //参数设置 string SockIOPoolName = "demo"; string[] MemcacheServiceList = { "127.0.0.1:11211" }; //设置连接池 SockIOPool SPool = SockIOPool.GetInstance(SockIOPoolName); SPool.SetServers(MemcacheServiceList); SPool.Initialize(); MClient = new MemcachedClient(); MClient.PoolName = SockIOPoolName; //是否启用压缩数据:如果启用了压缩,数据压缩长于门槛的数据将被储存在压缩的形式 MClient.EnableCompression = false; ////压缩设置,超过指定大小的都压缩 //MClient.CompressionThreshold = 1024 * 1024; } /// <summary> /// 根据key存储对象 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public bool Set(string key, object value) { var result = MClient.Set(key, value); return result; } /// <summary> /// 根据key存储对象,并且设置过期时间 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="timeOut"></param> /// <returns></returns> public bool Set(string key, object value, DateTime timeOut) { var result = MClient.Set(key, value, timeOut); return result; } /// <summary> /// 根据key获取对应的对象 /// </summary> /// <param name="key"></param> /// <returns></returns> public object Get(string key) { var result = MClient.Get(key); return result; } /// <summary> /// 替换对应key的value /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <returns></returns> public bool Replace(string key, object value) { var result = MClient.Replace(key, value); return result; } /// <summary> /// 删除对应key /// </summary> /// <param name="key"></param> /// <returns></returns> public bool Delete(string key) { return MClient.Delete(key); } /// <summary> /// 删除对应key,并设置从内存中移除的时间点 /// </summary> /// <param name="key"></param> /// <param name="timeOut"></param> /// <returns></returns> public bool Delete(string key, DateTime timeOut) { return MClient.Delete(key, timeOut); } /// <summary> /// 判断key是否存在,存在返回true,不存在返回false /// </summary> /// <param name="key"></param> /// <returns></returns> public bool KeyExists(string key) { return MClient.KeyExists(key); } /// <summary> /// Memcache分布式锁 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <returns>当key存在返回false,当key不存在返回true</returns> public bool Add(string key, object value) { return MClient.Add(key, value); } /// <summary> /// Memcache分布式锁,并且设置过期时间 /// Memcached分布式锁可以使用 Add 命令,该命令只有KEY不存在时,才进行添加,否则不会处理。Memcached 所有命令都是原子性的,并发下add 同一个KEY,只会一个会成功。 /// 利用这个原理,可以先定义一个锁 LockKEY,Add 成功的认为是得到锁。并且设置[过期超时] 时间,保证宕机后,也不会死锁。 /// 在完成具体操作后,判断锁 LockKEY 是否已超时。如果超时则不删除锁,如果不超时则 Delete 删除锁。 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="timeOut"></param> /// <returns>当key存在返回false,当key不存在返回true</returns> public bool Add(string key, object value, DateTime timeOut) { return MClient.Add(key, value, timeOut); } } }
控制台程序:
//引用 using Memcached.ClientLibrary; namespace Memcache.AddLock { /// <summary> /// Memcache分布式锁简单实例 /// </summary> public class Program { //创建一个公共类 public static MemcacheHelper memcache; public static void Main(string[] args) { memcache = new MemcacheHelper(); Console.WriteLine("线程开始前,输出" + memcache.Get("demoKey")); var result = memcache.Delete("demoKey"); Console.WriteLine("线程开始前,输出" + memcache.Get("demoKey") + ",删除对应key返回:" + result); Console.WriteLine("线程开始前,输出" + memcache.Delete("LockKey")); memcache.Set("demoKey", "0"); //定义三个线程 Thread myThread1 = new Thread(new ParameterizedThreadStart(AddVal)); Thread myThread2 = new Thread(new ParameterizedThreadStart(AddVal)); Thread myThread3 = new Thread(AddVal); myThread1.Start("1"); myThread2.Start("2"); myThread3.Start(); Console.WriteLine("等待两个线程结束"); Console.ReadKey(); } public static void AddVal(object num) { for (int i = 0; i < 500; i++) { //int result = int.Parse(memcache.Get("demoKey").ToString()); //memcache.Set("demoKey", (result + 1).ToString()); //如果0.5秒不释放锁 自动释放,避免死锁 if (memcache.Add("LockKey", "Hello World", DateTime.Now.AddSeconds(0.5))) { //得到锁 try { int result = int.Parse(memcache.Get("demoKey").ToString()); memcache.Set("demoKey", (result + 1).ToString()); //注意:这里最好加上主动去删除锁 //检查锁是否超时(直接去删除就可以) memcache.Delete("LockKey"); } catch (Exception ex) { //发生异常时也直接删除锁 memcache.Delete("LockKey"); } } else { i = i - 1; //没有得到锁时等待 } } Console.WriteLine("线程" + num + "结束,输出" + memcache.Get("demoKey")); } } }
运行结果效果图说明:
图一是没有加分布式锁的情况下执行结果
图二是加分布式锁的情况下执行结果,三个线程各循环500次,最终缓存值应该为1500才正确